aboutsummaryrefslogtreecommitdiff
path: root/modpackman.py
diff options
context:
space:
mode:
Diffstat (limited to 'modpackman.py')
-rwxr-xr-xmodpackman.py171
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)