from typing import List import os import httpx import shutil import asyncio import subprocess import json server_dir = "server" mods_dir = f"{server_dir}/mods" os.makedirs(server_dir, exist_ok=True) os.makedirs(mods_dir, exist_ok=True) delete_list = [ "audioplayer_uploads/", "config/", "cache/", "world/", "logs/", "banned-players.json", "server.properties", "banned-ips.json", "usercache.json", "whitelist.json", "ops.json", "eula.txt", ] with open("config/config.json", "r") as f: config = json.load(f) mc_version = config.get("minecraft_version", "1.21.10") fabric_loader_version = config.get("fabric_loader_version", "0.17.3") loader_meta_version = config.get("loader_meta_version", "1.1.0") modrinth_mods = config.get("modrinth_mods", []) async def download_modrinth(mod_list: List[str], mc_ver: str): downloaded_files = [] failed_mods = [] async with httpx.AsyncClient(timeout=60) as client: for slug in mod_list: try: versions_url = f"https://api.modrinth.com/v2/project/{slug}/version" resp = await client.get(versions_url) if resp.status_code != 200: print(f"[Modrinth] Could not get versions for {slug}") failed_mods.append(slug) continue versions = resp.json() compatible_versions = [ v for v in versions if "fabric" in v.get("loaders", []) and mc_ver in v.get("game_versions", []) ] if not compatible_versions: print(f"[Modrinth] No Fabric versions found for {slug} on MC {mc_ver}") failed_mods.append(slug) continue latest = max(compatible_versions, key=lambda v: v["date_published"]) file_info = latest.get("files", [])[0] jar_url = file_info["url"] filename = file_info["filename"] print(f"[Modrinth] Downloading {slug} -> {filename} ...") jar_resp = await client.get(jar_url) if jar_resp.status_code != 200: print(f"[Modrinth] Couldn't download jar for {slug}") failed_mods.append(slug) continue path = os.path.join(mods_dir, filename) with open(path, "wb") as f: f.write(jar_resp.content) downloaded_files.append(path) print(f"[Modrinth] Successfully downloaded: {filename}") except Exception as e: print(f"[Modrinth] Error downloading {slug}: {e}") failed_mods.append(slug) return downloaded_files, failed_mods async def download_fabric_server(mc_ver: str, loader_ver: str, meta_ver: str): url = f"https://meta.fabricmc.net/v2/versions/loader/{mc_ver}/{loader_ver}/{meta_ver}/server/jar" filename = "server.jar" path = os.path.join(server_dir, filename) async with httpx.AsyncClient(timeout=60) as client: try: print(f"[Fabric] Downloading Fabric server for MC {mc_ver} ...") resp = await client.get(url) if resp.status_code != 200: print(f"[Fabric] Failed to download Fabric server JAR: {resp.status_code}") return None with open(path, "wb") as f: f.write(resp.content) print(f"[Fabric] Successfully downloaded Fabric server: {filename}") return path except Exception as e: print(f"[Fabric] Error downloading Fabric server: {e}") return None async def run_fabric_server(): jar_path = os.path.join(server_dir, "server.jar") if not os.path.exists(jar_path): print("[Server] Server JAR not found, cannot start server.") return eula_path = os.path.join(server_dir, "eula.txt") if not os.path.exists(eula_path): print("[Server] Accepting EULA automatically...") with open(eula_path, "w") as f: f.write("eula=true\n") print(f"[Server] Starting Fabric server from {jar_path} ...") process = subprocess.Popen( ["java", "-jar", "server.jar", "nogui"], cwd=server_dir ) process.wait() print("[Server] Fabric server stopped.") def cleanup_server(delete_list: List[str]): print("[Cleanup] Cleaning up server files...") for item in delete_list: path = os.path.join(server_dir, item) if os.path.exists(path): try: if os.path.isdir(path): shutil.rmtree(path) print(f"[Cleanup] Removed folder: {item}") else: os.remove(path) print(f"[Cleanup] Removed file: {item}") except Exception as e: print(f"[Cleanup] Failed to remove {item}: {e}") else: print(f"[Cleanup] {item} does not exist, skipping.") print("[Cleanup] Cleanup complete.") async def main(): print(f"Starting Benson Mod Updater for Fabric MC {mc_version}...\n") files, failed_mods = await download_modrinth(modrinth_mods, mc_version) server_file = await download_fabric_server(mc_version, fabric_loader_version, loader_meta_version) print("\nAll downloads complete!") print(f"Downloaded {len(files)} mods:") for f in files: print(f" - {f}") if failed_mods: print("\nThe following mods failed to download:") for mod in failed_mods: print(f" - {mod}") else: print("\nAll mods downloaded successfully!") if server_file: await run_fabric_server() cleanup_server(delete_list) if __name__ == "__main__": asyncio.run(main())