From c81275856a359453b5290db8ae3e04a197e79417 Mon Sep 17 00:00:00 2001 From: Fierelier Date: Mon, 29 Mar 2021 04:16:23 +0200 Subject: [PATCH] Support for inherited JSONs and their libs, specifically fabric --- offline-minecraft-launcher.py | 199 +++++++++++++++++++++++++++------- 1 file changed, 160 insertions(+), 39 deletions(-) diff --git a/offline-minecraft-launcher.py b/offline-minecraft-launcher.py index fa315e8..b831834 100644 --- a/offline-minecraft-launcher.py +++ b/offline-minecraft-launcher.py @@ -22,6 +22,39 @@ import subprocess import json import hashlib import platform +import urllib.request + +def download(url,decode = "utf-8"): + request = urllib.request.Request(url,headers={"User-Agent":"Mozilla/5.0"}) + response = urllib.request.urlopen(request) + rt = response.read() + if decode == False: return rt + return response.read().decode(decode) + +def readFile(file,decode = "utf-8"): + fileh = False + fileh = open(file,"rb") + data = fileh.read() + fileh.close() + if decode == False: return data + return data.decode(decode) + +def fileDl(url,file,read = True,decode = "utf-8"): + if os.path.isfile(file): + if read == True: + return readFile(file,decode) + return + + print(url) + data = download(url,False) + fileh = open(tmpFile,"wb") + fileh.write(data) + fileh.close() + if pUp(file) != "" and not os.path.isdir(pUp(file)): os.makedirs(pUp(file)) + os.rename(tmpFile,file) + if read == False: return + if decode == False: return data + return data.decode(decode) def readJsonFile(file): fileh = open(file,"r") @@ -29,10 +62,112 @@ def readJsonFile(file): fileh.close() return json.loads(data) +def findInChain(versionsPath,version,path): + versionPath = p(versionsPath,version) + clientJson = readJsonFile(p(versionPath,version + ".json")) + curPath = clientJson.copy() + success = True + for dir in path: + if dir in curPath: + curPath = curPath[dir] + else: + success = False + break + + if success == False: + return findInChain(versionsPath,clientJson["inheritsFrom"],path) + return curPath + +def findInChainDeepest(versionsPath,version,path): + found = False + while True: + versionPath = p(versionsPath,version) + clientJson = readJsonFile(p(versionPath,version + ".json")) + curPath = clientJson.copy() + for dir in path: + if dir in curPath: + curPath = curPath[dir] + else: + if "inheritsFrom" in clientJson: + version = clientJson["inheritsFrom"] + continue + else: + curPath = False + break + + found = curPath + if "inheritsFrom" in clientJson: + version = clientJson["inheritsFrom"] + continue + else: + break + + if found == False: raise + return found + +def processVersion(versionsPath,libraryPath,nativePath,version): + versionPath = p(versionsPath,version) + clientJson = readJsonFile(p(versionPath,version + ".json")) + libraries = [] + natives = [] + arguments = [] + downloadLibraries = {} + + if "inheritsFrom" in clientJson: + _,libraries,downloadLibraries,natives,arguments = processVersion(versionsPath,libraryPath,nativePath,clientJson["inheritsFrom"]) + + for library in clientJson["libraries"]: + if "downloads" in library: + if "artifact" in library["downloads"]: + library["downloads"]["artifact"]["path"] = library["downloads"]["artifact"]["path"].replace("/",os.path.sep) + libraries.append(p(libraryPath,library["downloads"]["artifact"]["path"])) + if "classifiers" in library["downloads"]: + native = False + if "natives-" +lv["osName"] in library["downloads"]["classifiers"]: + native = library["downloads"]["classifiers"]["natives-" +lv["osName"]] + + if lv["osName"] == "macos" and native == False and "natives-osx" in library["downloads"]["classifiers"]: + native = library["downloads"]["classifiers"]["natives-osx"] + + if native != False: + native["path"] = native["path"].replace("/",os.path.sep) + natives.append(p(nativePath,native["path"])) + elif "name" in library: + #net.fabricmc:sponge-mixin:0.9.2+mixin.0.8.2 + libSplit = library["name"].rsplit(":",1) + libVersion = libSplit[1] + libName = libSplit[0].rsplit(":",1)[1] + libPath = libSplit[0].replace(".","/").replace(":","/") + "/" + libVersion + "/" +libName+ "-" +libVersion+ ".jar" + library["path"] = libPath.replace("/",os.path.sep) + libraries.append(p(libraryPath,library["path"])) + + if "url" in library: + fullUrl = library["url"] + while len(fullUrl) > 0 and fullUrl[-1] == "/": fullUrl = fullUrl[:-1] + fullUrl = fullUrl + "/" + libPath + downloadLibraries[p(libraryPath,library["path"])] = fullUrl + + libraries.append(p(versionPath,version + ".jar")) + + if "arguments" in clientJson: + for arg in clientJson["arguments"]["game"]: + if type(arg) != str: continue + arguments.append(arg) + elif "minecraftArguments" in clientJson: + margs = clientJson["minecraftArguments"].replace('"',"").split(" ") + for arg in margs: + if type(arg) != str: continue + arguments.append(arg) + + return clientJson,libraries,downloadLibraries,natives,arguments + def main(): + print("Reading config...") config = configparser.ConfigParser() config.read(os.path.splitext(s)[0] + ".ini") + global lv + lv = config["default"] for var in lv: glbs = globals() @@ -58,48 +193,43 @@ def main(): if not lv["osName"] in ["windows","linux","macos"]: print("Warning, unsupported OS detected: " +lv["osName"]) print("Needs to be either windows, linux or macos. Define it with osName=name in the config.") - print("") + + print("") if not "version" in lv: lv["version"] = input("Version ID: ") if not "name" in lv: lv["name"] = input("Player name: ") + global tmpFile + tmpFile = p(lv["gamePath"],"file.tmp") + if os.path.isfile(tmpFile): os.remove(tmpFile) + + print("Scanning .json(s)...") + versionsPath = p(lv["gamePath"],"versions") + libraryPath = p(lv["gamePath"],"libraries") + nativePath = p(lv["gamePath"],"natives") + clientJson,libraries,downloadLibraries,natives,arguments = processVersion(versionsPath,libraryPath,nativePath,lv["version"]) versionPath = p(lv["gamePath"],"versions",lv["version"]) - libraryPath = p(lv["gamePath"],"libraries") - nativesPath = p(lv["gamePath"],"natives") nativesOutPath = p(versionPath,"natives-" +lv["osName"]) assetsPath = p(lv["gamePath"],"assets") - print("Extracting natives...") - clientJson = readJsonFile(p(versionPath,lv["version"] + ".json")) - libraryList = "" separator = ";" if lv["osName"] != "windows": separator = ":" + libraryList = separator.join(libraries) - for library in clientJson["libraries"]: - if "artifact" in library["downloads"]: - library["downloads"]["artifact"]["path"] = library["downloads"]["artifact"]["path"].replace("/",os.path.sep) - libraryList += p(libraryPath,library["downloads"]["artifact"]["path"]) + separator - if "classifiers" in library["downloads"]: - native = False - if "natives-" +lv["osName"] in library["downloads"]["classifiers"]: - native = library["downloads"]["classifiers"]["natives-" +lv["osName"]] - - if lv["osName"] == "macos" and native == False and "natives-osx" in library["downloads"]["classifiers"]: - native = library["downloads"]["classifiers"]["natives-osx"] - - if native != False: - native["path"] = native["path"].replace("/",os.path.sep) - if not os.path.isdir(nativesOutPath): os.makedirs(nativesOutPath) - subprocess.run(["7z","x",p(nativesPath,native["path"]),"-o" +nativesOutPath,"-aos"],check=True,stdout=subprocess.DEVNULL) + print("Downloading libraries...") + for library in downloadLibraries: + fileDl(downloadLibraries[library],library,read = False) - libraryList += p(versionPath,lv["version"] + ".jar") - #print(libraryList) + print("Extracting natives...") + if not os.path.isdir(nativesOutPath): os.makedirs(nativesOutPath) + for native in natives: + subprocess.run(["7z","x",native,"-o" +nativesOutPath,"-aos"],check=True,stdout=subprocess.DEVNULL) launcherVariables = {} launcherVariables["auth_player_name"] = lv["name"] - launcherVariables["version_name"] = lv["version"] + launcherVariables["version_name"] = findInChainDeepest(versionsPath,lv["version"],["id"]) launcherVariables["game_directory"] = lv["gamePath"] launcherVariables["assets_root"] = assetsPath - launcherVariables["assets_index_name"] = clientJson["assets"] + launcherVariables["assets_index_name"] = findInChain(versionsPath,lv["version"],["assets"]) launcherVariables["auth_access_token"] = "-" launcherVariables["auth_uuid"] = hashlib.md5(lv["name"].encode('utf-8')).hexdigest() launcherVariables["user_type"] = "offline" @@ -115,19 +245,10 @@ def main(): args.append(libraryList) args.append(clientJson["mainClass"]) - if "arguments" in clientJson: - for arg in clientJson["arguments"]["game"]: - if type(arg) != str: continue - for var in launcherVariables: - arg = arg.replace("${" +var+ "}",launcherVariables[var]) - args.append(arg) - elif "minecraftArguments" in clientJson: - margs = clientJson["minecraftArguments"].replace('"',"").split(" ") - for arg in margs: - if type(arg) != str: continue - for var in launcherVariables: - arg = arg.replace("${" +var+ "}",launcherVariables[var]) - args.append(arg) + for arg in arguments: + for var in launcherVariables: + arg = arg.replace("${" +var+ "}",launcherVariables[var]) + args.append(arg) print("Launching Minecraft...") if lv["console"] == "1":