diff options
Diffstat (limited to 'modpackman.py')
-rwxr-xr-x | modpackman.py | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/modpackman.py b/modpackman.py new file mode 100755 index 0000000..e5e5fdc --- /dev/null +++ b/modpackman.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +import os +import sys + +if hasattr(sys, '_MEIPASS'): # we're running in a bundle, go to where we have our bundled assets + os.chdir(sys._MEIPASS) + +import argparse +import shutil +import requests +import pathlib +import hashlib +from configparser import RawConfigParser + +import util +from util import config + +# Apply updates to the actual mod pack +def install(): + mods_location = os.path.join(config["pack"]["location"], "mods") + whitelist = config["pack"]["whitelist"] + blacklist = config["pack"]["blacklist"] + + # Actual download links are stored in pack-lock.ini, so we need to read it in here + pack_lock = RawConfigParser() + pack_lock.read('pack-lock.ini') + + print(f"Updating pack with version {pack_lock['global']['pack_version']}...") + + # create the mods folder if it doesn't already exist + pathlib.Path(mods_location).mkdir(parents=True, exist_ok=True) + + names = set(f'{mod}.jar' for mod in pack_lock['mod_versions'].keys()) + # whitelist client mods (e.g. optifine) + names.update(whitelist) + + i = 0 + for entry in pack_lock['mod_versions'].items(): + name = f'{entry[0]}.jar' + checksum, url = entry[1].split(',') + mod_path = os.path.join(mods_location, name) + i += 1 + if (os.path.exists(mod_path) and os.path.isfile(mod_path) and \ + hashlib.sha1(open(mod_path, 'rb').read()).hexdigest() == checksum) or \ + name in blacklist: + print(f"Skipping {name}, already up to date") + else: + print(f'Installing {name} from {url}...') + print(f' ({i} of {len(pack_lock["mod_versions"])})', end='\r') + util.download_file(url, mod_path) + print("Done!" + " " * 8) + + # Remove any old mods that might be stuck in the mods folder + print() + print("Removing old mods...") + for jar in os.listdir(mods_location): + if jar not in names and os.path.splitext(jar)[1] == ".jar": + os.remove(os.path.join(mods_location, jar)) + print(f"Removing '{jar}'") + + + print('\nInstalling configs...') + # For config files, we don't need to remove any extras, as they + # will simply be ignored by Forge + if not os.path.exists('config'): + raise RuntimeError("Error: config folder must exist!") + + mc_config_folder = os.path.join(config['pack']['location'], 'config') + if not os.path.exists(mc_config_folder): + os.mkdir(mc_config_folder) + for cfg in os.listdir('config'): + shutil.copyfile(os.path.join('config', cfg), os.path.join(mc_config_folder, cfg)) + + print() + print("Finished installing mods!") + + + +def apply_updates(): + """ + Using the URLs defined in pack.ini, update all mods to the latest + compatible version and write them out to pack-lock.ini + """ + print("Populating version file...") + print("Getting new versions of all mods...") + mod_urls = util.find_updated_urls([x for x in config['mods'].values()], config['pack']['game_version'], threads=16) + print("Downloading and checksumming all mods...") + if None in mod_urls: + print("[!] Checksum generation aborted due to invalid URLs. Please fix them and try again.") + exit(1) + checksums = util.find_checksums(mod_urls) + + # Write the updated mods list out to pack-lock.ini + pack_lock = RawConfigParser() + pack_lock.read('pack-lock.ini') + pack_lock['global']['pack_version'] = str(int(pack_lock['global']['pack_version']) + 1) + # This is only needed for the self-update feature + pack_lock['global']['config_files'] = ','.join(os.listdir('config')) + pack_lock['mod_versions'] = {name: f'{checksum},{url}' for name, checksum, url in zip(config['mods'].keys(), checksums, mod_urls)} + with open('pack-lock.ini', 'w') as f: + pack_lock.write(f) + + print() + print("Done!") + print("Updates applied to pack-lock.ini") + print(f"New pack version is {pack_lock['global']['pack_version']}") + print("[!] No mods were installed. To update your mods folder, run 'update.py install'") + + +# Find if any updates are available +def check_updates(mods, version_file, version=(2, 0, 0)): + pack_version = util.get_version_from_file(version_file) + print("Checking for updates to version " + str(pack_version) + "...") + latest = [(k, mods[k]) for k in mods.keys()] + old = util.read_file(version_file) + old_urls = [mod[2] for mod in old] + num_updates = 0 + + print("Checking updates...") + ffx = util.firefox() + + for mod in latest: + print("Checking for updates to {mod[0]}...".format(mod=mod), end="") + sys.stdout.flush() # takes care of line-buffered terminals + if 'curseforge' in mod[1]: + url = util.find_cdn(ffx, mod[1], version) + else: + url = requests.get(mod[1]).url + if url in old_urls: + print(" No updates") + else: + print(" Found update: " + url.split('/')[-1]) + num_updates += 1 + ffx.close() + + print("Finished checking for updates. {num} mods can be updated".format(num=num_updates)) + if num_updates >= 0: + print("Run 'python update.py apply_updates' to create a new version with these updates applied.") + + +parser = argparse.ArgumentParser( + description="A Simple Git-Based Modpack Manager", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog='''\ +Available commands: + install : Downloads mods listed in version.txt and populates the mods folder specified in pack-location.txt + apply_updates : Using the urls in mods.txt, repopulates version.txt to reflect the most recent mod versions + check_updates : Compares version.txt and mods.txt to see if any mods can be updated +''') + +parser.add_argument('command', + nargs='?', + default='install', + help="The action to perform (default: install)") + +if __name__ == "__main__": + args = parser.parse_args() + mods = config['mods'] + pack = config['pack'] + + # run the command + if args.command == "install": + install() + elif args.command == "apply_updates": + apply_updates() + elif args.command == "check_updates": + check_updates(config['mods'], "version.txt", config['pack']["game_version"]) + else: + print("Error: command \"" + args.command + "\" does not exist") + parser.print_help() + sys.exit(1) |