import io import json import tempfile import subprocess import time import os import datetime import zipfile import tarfile from pathlib import Path import shutil from FTP import * import socket def install_prism(): global data_dir if not os.path.exists(os.path.join(data_dir, "prism")): install_java(21) suff = "win.zip" if os.name == "posix": suff = "lin.tar.gz" prism_file = "prism/prism-"+suff with io.BytesIO() as buff: ftpDownload(prism_file, buff) buff.seek(0) if os.name == "nt": with zipfile.ZipFile(buff) as zip: zip.extractall(os.path.join(data_dir, "prism")) else: with tarfile.open(fileobj=buff) as tar: tar.extractall(os.path.join(data_dir, "prism")) with io.BytesIO() as buff: ftpDownload("prism/prismlauncher.cfg", buff) buff.seek(0) with open(os.path.join(data_dir, "prism", "prismlauncher.cfg"), 'wb') as file: file.write(buff.getbuffer()) suff = "win" cmd = "java.exe" if os.name == "posix": suff = "lin" cmd = "java" data = [] with open(os.path.join(data_dir, "prism", "prismlauncher.cfg"), 'r') as file: data = file.readlines() for ind, line in enumerate(data): if line.startswith("JavaPath="): data[ind] = "JavaPath="+data_dir.replace("\\", "/")+"/java/java-21-"+suff+"/bin/"+cmd+"\n" if line.startswith("LastHostname="): data[ind] = "LastHostname="+socket.gethostname()+"\n" with open(os.path.join(data_dir, "prism", "prismlauncher.cfg"), 'w') as file: file.writelines(data) def get_tar_members_stripped(tar, n_folders_stripped = 1): members = [] for member in tar.getmembers(): p = Path(member.path) member.path = p.relative_to(*p.parts[:n_folders_stripped]) members.append(member) return members def unzip(zip, dest): for file_info in zip.infolist(): if file_info.is_dir(): continue file_path = file_info.filename extracted_path = file_path.split("/", 1)[1] extracted_path = os.path.join(dest, extracted_path) print(extracted_path) os.makedirs(os.path.dirname(extracted_path), exist_ok=True) with open(extracted_path, 'wb') as dst: with zip.open(file_info, 'r') as src: shutil.copyfileobj(src, dst) def get_java_version(mc_version): global data_dir tokens = mc_version.split(".") java = 8 if tokens[1] == "17": java = 17 elif tokens[1] == "18" or tokens[1] == "19": java = 17 elif int(tokens[1]) > 19: if tokens[1] == "20" and int(tokens[2]) < 5: java = 17 else: java = 21 return java def install_java(java): suff = "win" ext = ".zip" if os.name == "posix": suff = "lin" ext = ".tar.gz" java_path = "java/java-"+str(java)+"-"+suff+ext if not os.path.exists(data_dir+"/java/java-"+str(java)+"-"+suff): print("Downloading Java "+str(java)) with io.BytesIO() as buff: ftpDownload(java_path, buff) if os.name == "posix": buff.seek(0) with tarfile.open(fileobj=buff) as tar: print(tar.getnames()) tar.extractall(data_dir+"/java/java-"+str(java)+"-"+suff, members=get_tar_members_stripped(tar, 1)) else: with zipfile.ZipFile(buff) as zip: unzip(zip, data_dir+"/java/java-"+str(java)+"-"+suff) def resource_path(relative_path): try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def perform_installation(instance_name, prism_command, prism_instance_path, pack): global data_dir if os.name == "posix": data_dir = os.getenv("HOME") + "/.local/share/FCLauncher" else: data_dir = os.getenv("APPDATA") + "/FCLauncher" Path(data_dir).mkdir(parents=True, exist_ok=True) Path(data_dir+"/java").mkdir(parents=True, exist_ok=True) install_prism() if os.name == "nt": prism_command = os.path.join(data_dir, "prism", "prismlauncher.exe") else: prism_command = os.path.join(data_dir, "prism", "PrismLauncher") prism_instance_path = os.path.join(data_dir, "prism", "instances") try: # Fetching versions.json from FTP bio = io.BytesIO() ftpDownload(pack+"/versions.json", bio) bio.seek(0) versions = json.load(bio) bio.close() # Checking current version version = "0.0.0" version_file_path = os.path.join(prism_instance_path, instance_name, ".minecraft", "version.txt") if os.path.exists(version_file_path): with open(version_file_path, 'r') as fp: version = fp.readline().rstrip() # Checking if update is needed if version != versions[-1]["Version"]: print(f"Current version: {version}") print(f"Latest version: {versions[-1]['Version']}") # Downloading modpack with tempfile.TemporaryDirectory() as temp_dir: modpack_file_path = os.path.join(temp_dir, instance_name + ".mrpack") with open(modpack_file_path, 'wb') as modpack: ftpDownload(pack+"/"+versions[-1]["File"], modpack) # Running PrismLauncher with modpack subprocess.Popen([prism_command, '-I', modpack_file_path]) # Waiting for installation to complete while True: time.sleep(5) if os.path.exists(version_file_path): with open(version_file_path, 'r') as fp: version = fp.readline().rstrip() if version == versions[-1]["Version"]: info_file_path = os.path.join(prism_instance_path, instance_name, "mmc-pack.json") mc_version = "" with open(info_file_path) as file: info = json.load(file) for component in info["components"]: if component["uid"] == "net.minecraft": mc_version = component["version"] break; java = get_java_version(mc_version) install_java(java) option_path = os.path.join(prism_instance_path, instance_name, "instance.cfg") data = [] suff = "win" cmd = "java.exe" if os.name == "posix": suff = "lin" cmd = "java" with open(option_path, 'r') as file: data = file.readlines() was_set = False for ind, line in enumerate(data): if line.startswith("JavaPath="): data[ind] = "JavaPath="+data_dir.replace("\\", "/")+"/java/java-"+str(java)+"-"+suff+"/bin/"+cmd+"\n" was_set = True if not was_set: data.append("JavaPath="+data_dir.replace("\\", "/")+"/java/java-"+str(java)+"-"+suff+"/bin/"+cmd+"\n") data.append("OverrideJavaLocation=true\n") data.append("OverrideJava=true\n") with open(option_path, 'w') as file: file.writelines(data) print(data) subprocess.run([prism_command, '-l', instance_name]) time.sleep(30) break else: subprocess.run([prism_command, '-l', instance_name]) except Exception as e: print(e) print("Unable to check for updates. Modpack may be out of date") subprocess.run([prism_command, '-l', instance_name]) def upload_pack(username, password, version_tag, fileName, pack): bio = io.BytesIO() ftpDownload(pack+"/versions.json", bio) bio.seek(0) versions = json.load(bio) bio.close() time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") versions.append({"Version":version_tag, "Date":time, "File":"Versions/"+pack+"-"+time+".mrpack"}) with zipfile.ZipFile(fileName, 'r') as zin: zipbytes = io.BytesIO() zout = zipfile.ZipFile(zipbytes, 'w') for item in zin.infolist(): buffer = zin.read(item.filename) if item.filename != "overrides/version.txt": zout.writestr(item, buffer) zout.writestr("overrides/version.txt", version_tag) zout.close() ftpUpload(pack+"/"+versions[-1]["File"], zipbytes, username, password) bio = io.BytesIO() bio.write(json.dumps(versions).encode()) ftpUpload(pack+"/versions.json", bio, username, password) bio.close() modpackUpdate(pack, username, password) def getModpacks(): bio = io.BytesIO() ftpDownload("modpacks.json", bio) bio.seek(0) return json.load(bio) def uploadModpacks(modpacks, username, password): bio = io.BytesIO() bio.write(json.dumps(modpacks).encode()) ftpUpload("modpacks.json", bio, username, password) bio.close() def modpackUpdate(id, username, password): modpacks = getModpacks() time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") for modpack in modpacks: if modpack["id"] == id: modpack["last-updated"] = time uploadModpacks(modpacks, username, password); def createModpack(id, name, username, password): modpacks = getModpacks() time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") modpacks.append({"id":id, "name":name, "last-updated":time}) uploadModpacks(modpacks, username, password) ftpMakeDirectory(id, username, password) versions = [] bio = io.BytesIO() bio.write(json.dumps(versions).encode()) ftpUpload(id+"/versions.json", bio, username, password) ftpMakeDirectory(id+"/Versions", username, password) def deleteModpack(username, password, id): modpacks = getModpacks() for pack in modpacks: if pack["id"] == id: modpacks.remove(pack) break uploadModpacks(modpacks, username, password) ftpDeleteDirectory(id, username, password)