summaryrefslogblamecommitdiff
path: root/game/client.rpy
blob: 42f428c67ae79615f26098b448b12bae745526a8 (plain) (tree)
1
2
3
4
5
6
7
8
9





                                                                                 
              
                                                                                
                          
               
                               
                                           

                    
                                       
                                                   
                                          
                                                    
                                        
                                                  
             

                                                                         

                
                         
                                                                   

                                                                                
                                                                                         

                                               
                                               
                                      
                                                       





                                                                 


                                                                    














                                                                                           



                                                                                            
                                                      


                                                         

                                    
                                                                                
                                         
                                                                      
                                                           




                                                            
                                                                           
                                            
                                    

                                                                   
                                       

                                                                         
                                                    
                               

                                  











                                                                                

                                                                                 
                                                                          
               

                                           
                           

                          
 
                                                                                


                                                
                                                                                                                                                    




                                                                                
               
                                                                                                                               


                              

                                      
                                                                            
                               
 
                                                               

                                                                                                                             

                                                                                                                         
 
                                                                       




                                                                                 
                 
                                 
 

                                  



                                      

                                       
                                                         

                                                                      
                                



























                                                                                 
                     



                                                              
                                 

                                                                              





                                                                                                                                    


                                                                                
                                          
                                                                           


                                                               
                                          

              
                                      

                                            
               
          
                                       
                            
                                                                         
                           
                                                              
                                                               
                                                                  


                                                               
                                                                                                  



                                                                           
                                      




                                              
 

             



                                                  

                           



                                                       
                                                                                                                      

                                                   


                                                                  
                        
             

                                                     
                                               
                       

                             
                                                                                                                  
                      
                                                                
                    
 


                                                














                                                                         
                                       




                                                                






                                                   

                    




                                 

              
 
#################################################################################
#     This file is part of Mana Launcher.
#     Copyright (C) 2021  Jesusalva <jesusalva@tmw2.org>
#
#     Distributed under the MIT license, except for Steam parts.
#################################################################################
init 2 python:
    def handle_client(CLIENT_NAME="", launch=False, download=True, force=False):
        ## Local variables
        f=False
        if (CLIENT_NAME == ""):
            CLIENT_NAME=persistent.evol2cli

        ## Main loop
        if (CLIENT_NAME == "manaplus"):
            f=cli_manaplus(launch, download, force)
        elif (CLIENT_NAME == "manaverse"):
            f=cli_manaverse(launch, download, force)
        elif (CLIENT_NAME == "builtin"):
            f=cli_builtin(launch, download, force)
        else:
            status_update("ERROR, unrecognized client: %s" % CLIENT_NAME)

        return f

    def launch_game(idx):
        global progress, responsive, statusmsg, MLP_DEST, SCR_CUTIN
        ########################################################################
        ## Setup
        RPCUpdate(persistent.serverlist[idx]["Name"], persistent.serverlist[idx]["Back"])
        HOST=persistent.serverlist[idx]["Host"]
        PORT=persistent.serverlist[idx]["Port"]
        TYPE=persistent.serverlist[idx]["Type"]
        CMD=handle_client(launch=True)
        OPT="-s %s -y %s -p %s -S" % (HOST, TYPE, PORT)

        ## Config and Local overrides
        if renpy.linux:
            paten="/".join(CMD.split("/")[:-1])
            paten='%s' % paten.replace("\\ ", " ")
            OPT+=' -C "%s/Config" -L "%s/Local"' % (paten, paten)
        elif renpy.windows:
            paten="\\".join(CMD.replace('/', '\\').split("\\")[:-2])
            OPT+=' -C "%s\\Config" -L "%s\\Local"' % (paten, paten)

        ## Steam Deck must be fullscreen
        if renpy.variant("steam_deck"):
            bf=[]
            with open("%s/Config/config.xml", "r") as f:
                for l in f:
                    l=l.replace('"screen" value="0"', '"screen" value="0"')
                    l=l.replace('"screenwidth" value="1280"', '"screenwidth" value="800"')
                    l=l.replace('"screenheight" value="720"', '"screenheight" value="600"')
                    bf.append(l)
            with open("%s/Config/config.xml", "w") as f:
                for l in bf:
                    f.write(l)
            del bf

        #    OPT+=" --fullscreen"
        #if True: #(CLIENT_NAME in ["manaplus", "manaverse"]):
        #    #OPT+=" -C \"%s\" -ud \"%s/%s\"" % (get_path("config"), get_path("data"), HOST)
        #    #OPT+=" -C \"%s\"" % (get_path("config"))
        #    # TODO: --screenshot-dir. Where, exactly?
        #    #  <option name="screenheight" value="629"/>
        #    # <option name="screenwidth" value="839"/>

        stdout("%s %s" % (CMD, OPT))

        ########################################################################
        ## Obtain access token from vault
        ## Failures are skipped (!!) unless they are a 403 (FORBIDDEN)
        statusmsg=_("Requesting credentials from Vault...")
        auth = {"vaultId": vaultId,
                "token": vaultToken,
                "world": persistent.serverlist[idx]["UUID"]}
        PWD=""
        try:
            r=vault.post(VAULT_HOST+"/world_pass", json=auth, timeout=15.0)
            ## We got the access credentials
            if r.status_code == 200:
                auth2=r.json()
                PWD=" -U %s -P %s" % (auth2["user"], auth2["pass"])
            ## We were refused by Vault
            elif r.status_code == 403:
                stdout("Warning: Connection was refused (Vault logout?)")
                statusmsg=_("You're not logged in.")
                time.sleep(1.0)
                responsive = False
                return
            ## We are rate-limited, try again
            elif r.status_code == 429:
                statusmsg=_("Rate limited, we'll try again...")
                time.sleep(15.0)
                r=vault.post(VAULT_HOST+"/world_pass", json=auth, timeout=15.0)
                ## Only accept OK this time
                if r.status_code != 200:
                    stdout("Get World Auth - Returned error %d" % r.status_code)
                    raise Exception("Vault returned error %d" % r.status_code)
                auth2=r.json()
                PWD=" -U %s -P %s" % (auth2["user"], auth2["pass"])
            ## Internal error, maybe?
            else:
                stdout("Get World Auth - Returned error code %d" % r.status_code)
                raise Exception("Vault returned error %d" % r.status_code)
        except:
            traceback.print_exc()
            statusmsg=_("TMW Vault Error.")
            time.sleep(2.5)
            progress = 100
            return

        ########################################################################
        ## If it is a new world, show a cutscene
        try:
            if auth2["new"]:
                SCR_CUTIN=str("intro_"+persistent.serverlist[idx]["Name"].lower().replace(" ", "").replace('"','').replace("'","").replace(':','_'))
        except:
            traceback.print_exc()
            pass

        ########################################################################
        ## Loop
        statusmsg=_("You can minimize this window now.\n\nWorld selection list will be shown once you quit the current world.")
        # If cutscene, wait
        while SCR_CUTIN != "":
            sdelay(0.2)
        try:
            ## Minimize to tray if set
            if not renpy.mobile and persistent.iconify and type(CMD) == str:
                renpy.iconify()

            ## Sanitize input to avoid arbitrary code execution
            if type(CMD) == str:
                CMD=CMD.replace(";", "").replace("&", "").replace(">", "").replace("<", "").replace("|", "").replace("$", "")
            OPT=OPT.replace(";", "").replace("&", "").replace(">", "").replace("<", "").replace("|", "").replace("$", "")
            PWD=PWD.replace(";", "").replace("&", "").replace(">", "").replace("<", "").replace("|", "").replace("$", "")

            ## Launch your preferred game client, wait for it to finish
            if type(CMD) == str:
                if renpy.windows:
                    app=execute(san("\"%s\" %s%s" % (CMD, OPT, PWD)), shell=True)
                else:
                    app=execute(san("%s %s%s" % (CMD, OPT, PWD)), shell=True)
            else:
                app=CMD(OPT, PWD)

            if not LEGACY:
                app=app.returncode

            if "tmwa" in TYPE.lower():
                app = 7

            ## Determine error messages
            if app == 7:
                stdout("[CLIENT] Mirror Lake triggered.")
                ## Give "plenty" time to ensure data will be available
                statusmsg=_("Synchronizing data...")
                time.sleep(1.65)
                try:
                    r=vault.post(VAULT_HOST+"/getlake", json=auth, timeout=15.0)
                    ## We got the access credentials
                    if r.status_code == 200:
                        goto=r.json()["world"]
                        stdout("MLP Target: %s" % str(goto))
                        if goto == "" or goto.lower() == "vault":
                            stdout("Mirror Lake False Positive")
                        else:
                            MLP_DEST=goto
                            progress=100
                            return
                    ## We were refused by Vault
                    elif r.status_code == 403:
                        stdout("Warning: Connection was refused (Vault logout?)")
                        statusmsg=_("You're not logged in.")
                        time.sleep(1.0)
                        responsive = False
                        return
                    else:
                        stdout("[CLIENT] Sync error code %d." % r.status_code)
                        statusmsg=_("Synchronization error.") # 500 = No Lake
                        time.sleep(1.0)
                except:
                    traceback.print_exc()
                    stdout("[CLIENT] Sync failed.")
                    statusmsg=_("Synchronization error.")
                    time.sleep(1.0)
            elif app:
                traceback.print_exc()
                stdout("[CLIENT] An error happened: %d" % app)
                #responsive = False
        except:
            traceback.print_exc()
            stdout("[FATAL] An error happened trying to launch the client D:")
            responsive = False

        # NOTE: Now we would like to un-minimize ourselves
        # But we cannot =/
        # There's a few tricks but not very nice...
        # https://stackoverflow.com/questions/45426203/minimize-window-with-python (Linux)
        # https://stackoverflow.com/questions/2791489/how-do-i-take-out-the-focus-or-minimize-a-window-with-python/2792059 (Windows)

        ########################################################################
        ## Cleanup
        statusmsg=_("Thanks for playing!")
        # time.sleep(0.1) ## TODO: Ensure the world finishes updating Vault
        progress=100

        # Clean discord RPC and go back to world selection menu
        RPCUpdate("Main Menu", "launcher")
        return

    def md5check_client(silent=False):
      if not silent:
          renpy.notify("Checking md5sum...")
      ## Theirs
      try:
        installdir=get_path("manaplus")
        ## Get the file name
        fname=handle_client(launch=True, download=False).split("/").pop()
        ## Get the MD5 Hash
        if renpy.linux and persistent.evol2cli == "manaverse":
            r=requests.get(manaverseLinux+".md5", timeout=10.0)
        elif renpy.windows and persistent.evol2cli == "manaverse":
            r=requests.get(manaverseWin64+".md5", timeout=10.0)
        elif (persistent.evol2cli != "builtin"):
            ## FIXME: Simplify
            stdout("[INFO] Client '%s' is supplied by the launcher team..." % persistent.evol2cli)
            r=requests.get(persistent.host+"/%s.md5" % fname, timeout=10.0)
        else:
            raise Exception("Built-in has no MD5 Hash (builtin)")
        ## Sanitize the MD5 Hash
        md5up=r.text.replace("\n", "")
      except:
        traceback.print_exc()
        if not silent:
            renpy.notify("An error happened.")
        return

      ## Ours
      try:
        if renpy.windows:
            md5us=md5sum(fname)
        else:
            md5us=md5sum(installdir+"/%s" % fname)

        ## The check itself
        if md5up != md5us:
            stdout("MD5 Mismatch: hashes differ", True)
            stdout("Ours: %s" % md5us, True)
            stdout("Them: %s" % md5up, True)
            #shutil.rmtree(get_path("manaplus")) # FIXME! Download new client and override only specific files instead
            if not silent:
                renpy.notify("Files are outdated!")
            stdout("Client is outdated. Updating...")
            handle_client(launch=False, download=True, force=True)
            stdout("Client updated.")
            return False
        else:
            if not silent:
                renpy.notify("All files up to date!")
            stdout("All files are up to date.")
            return True
      except:
        traceback.print_exc()
        #shutil.rmtree(get_path("manaplus")) # FIXME! Download new client and override only specific files instead
        if not silent:
            renpy.notify("An error happened, nothing was done.")
        return False

    ############################################
    # TODO: Launch thread requesting /soul data
    def load_souldata():
        global progress, responsive, vaultId, vaultToken, mySoul
        try:
            auth = {"vaultId": vaultId,
                    "token": vaultToken}

            ## Make the POST request
            r=vault.post(VAULT_HOST+"/souldata", json=auth, timeout=15.0)
            if r.status_code != 200:
                raise Exception("Request error: %d" % r.status_code)
            dat=r.json()

            ## Update mySoul
            mySoul={}
            mySoul["level"]=dat["soulv"]
            mySoul["exp"]=dat["soulx"]
            mySoul["next"]=dat["varlv"]
            mySoul["up"]=False

            ## Newer versions of API may level you up - catch it
            try:
                mySoul["up"]=dat["lvlup"]
                mySoul["home"]=dat["homew"]
                for s in persistent.serverlist:
                    if mySoul["home"] == s["UUID"]:
                        mySoul["home"] = s["Name"]
                        break
                if mySoul["home"] == "VAULT":
                    mySoul["home"]="Not Set"
            except:
                pass
            progress = 100
        except:
            traceback.print_exc()
            mySoul = None
            responsive = False
        return