import platform from pathlib import PurePath, Path from os import getenv import subprocess import json import uuid from zipfile import ZipFile import datetime import logging import argparse def get_sysroot(): if platform.system() == 'Windows': if getenv('APPDATA'): return PurePath(getenv('APPDATA')) if platform.system() == 'Darwin': return PurePath(Path.home(), 'Library', 'Application Support') if platform.system() == 'Linux': return PurePath(Path.home()) return None def get_minecraft_folder(sys_root): return sys_root / f"{'' if platform.system() == 'Darwin' else '.'}minecraft" def get_installs(minecraft_folder): with open(minecraft_folder / 'launcher_profiles.json') as f: json_data = json.load(f) return json_data.get('profiles') def get_modpack_id(modpack_name, install_list): for install in install_list: if modpack_name in install_list[install]['name']: return install return None def main(modpack_path, manifest_path, forge_installer=None): # Get Minecraft Install Location sys_root = get_sysroot() if not sys_root: sys_root = input("Unable to locate your minecraft install - please enter the path to it here: ") logging.debug(f"System path set to: {sys_root}") # Get Manifest info: with open(manifest_path, 'r') as f: manifest_info = json.load(f) # Get Modpack Name from zipfile: modpack_name = manifest_info['name'] logging.debug(f"Modpack name extracted as: {modpack_name}") # Get Minecraft Installs install_list = get_installs(get_minecraft_folder(sys_root)) modpack_id = get_modpack_id(modpack_name, install_list) or uuid.uuid4().hex logging.debug(f"Modpack ID extracted as: {modpack_id}") install_path = sys_root / '.moddedminecraft' / modpack_name.replace(' ', '_') logging.debug(f"Modpack install path set to: {install_path}") if forge_installer: install_forge(forge_installer) else: logging.info("Forge installer not found/specified, assuming forge is already installed...") # Check if modpack is already installed if modpack_id not in install_list: logging.info(f"Modpack {modpack_name} ({modpack_id}) not found in install list - creating...") install_list[modpack_id] = { 'created': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z', 'gameDir': str(install_path), 'icon': manifest_info['icon'], 'javaArgs': manifest_info['javaArgs'], 'lastUsed': '', 'lastVersionId': manifest_info['lastVersionId'], 'name': modpack_name, 'type': 'custom' } with open(get_minecraft_folder(sys_root) / 'launcher_profiles.json', 'r') as f: full_profile_setup = json.load(f) with open(get_minecraft_folder(sys_root) / 'launcher_profiles.json', 'w') as f: full_profile_setup['profiles'] = install_list json.dump(full_profile_setup, f, indent=2) logging.info(f"Successfully added mod to installed profile list.") # Validate file structure compared to mod zip with ZipFile(modpack_path, 'r') as zip: logging.info("Validating mod files...") Path(install_path).mkdir(parents=True, exist_ok=True) zip.extractall(install_path) logging.info("Validation Completed.") def find_modpack_files(path_to_search=PurePath()): path = Path(path_to_search) modpack_zip = None modpack_manifest = None forge_installer = None for p in path.glob("*"): if p.is_dir(): continue elif '.jar' in p.name.lower(): forge_installer = p elif '.zip' in p.name.lower(): modpack_zip = p elif '.json' in p.name.lower(): modpack_manifest = p return modpack_zip, modpack_manifest, forge_installer def install_forge(forge_installer_path): logging.info(f"Forge installer found, launching...") subprocess.call(['java', '-jar', Path(forge_installer_path).resolve()]) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("--log_level", type=str, default='INFO', help='set logging level of this program', nargs='?') parser.add_argument("--modpackpath", type=str, default=PurePath(), nargs='?') parser.add_argument("--replace", type=bool, default=True, nargs='?', help='Overwrite contents of the mod folder') args = parser.parse_args() logging.basicConfig(filename='piston.log', filemode='w', level=getattr(logging, args.log_level.upper()), format='[%(asctime)s] [%(levelname)s] %(message)s', datefmt='%Y-%m-%dT%H:%M:%S') target_dir = args.modpackpath or PurePath() modpack_zip, modpack_manifest, forge_installer = find_modpack_files(target_dir) main(modpack_zip, modpack_manifest, forge_installer) logging.info("Done baking your cake - you can open your minecraft launcher now!")