#################################################################################
# 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