#!/usr/bin/env python3 """Generate config_player.xml from SERVER.xml mod list preserving load order.""" import os import sys import xml.etree.ElementTree as ET MODLIST_PATH = "/mnt/nvme/B/SteamLibrary/steamapps/common/Barotrauma/ModLists/SERVER.xml" LOCALMODS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "LocalMods") OUTPUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "config_player.xml") WORKSHOP_DIR = "/mnt/nvme/B/SteamLibrary/steamapps/workshop/content/602960" def log_ok(msg: str, indent: int = 0): prefix = " " * indent + "\033[92m✓\033[0m" print(f"{prefix} {msg}") def log_info(msg: str, indent: int = 0): prefix = " " * indent + "•" print(f"{prefix} {msg}") def log_warn(msg: str, indent: int = 0): prefix = " " * indent + "\033[93m⚠\033[0m" print(f"{prefix} {msg}") def log_err(msg: str, indent: int = 0): prefix = " " * indent + "\033[91m✗\033[0m" print(f"{prefix} {msg}", file=sys.stderr) def get_workshop_name(mod_id: str) -> str | None: fl_path = os.path.join(WORKSHOP_DIR, mod_id, "filelist.xml") if os.path.isfile(fl_path): try: return ET.parse(fl_path).getroot().get("name") except Exception: return None return None def find_mod_folder(name: str) -> str | None: name_lower = name.lower() if not os.path.isdir(LOCALMODS_DIR): return None for d in os.listdir(LOCALMODS_DIR): if d.lower() == name_lower: return d return None def resolve_mod_path(name: str, mod_id: str | None = None) -> str: folder = find_mod_folder(name) if folder and os.path.isfile(os.path.join(LOCALMODS_DIR, folder, "filelist.xml")): return f"LocalMods/{folder}/filelist.xml" if mod_id: workshop_name = get_workshop_name(mod_id) if workshop_name: folder = find_mod_folder(workshop_name) if folder and os.path.isfile(os.path.join(LOCALMODS_DIR, folder, "filelist.xml")): return f"LocalMods/{folder}/filelist.xml" return f"LocalMods/{name}/filelist.xml" def main(): print() print("╔══════════════════════════════════════════╗") print("║ Autogen Config from SERVER.xml order ║") print("╚══════════════════════════════════════════╝") print() if not os.path.isfile(MODLIST_PATH): log_err(f"Mod list not found: {MODLIST_PATH}") sys.exit(1) log_info(f"Reading: {MODLIST_PATH}") try: tree = ET.parse(MODLIST_PATH) root = tree.getroot() except Exception as e: log_err(f"Parse error: {e}") sys.exit(1) entries = list(root) log_info(f"Total entries in mod list: {len(entries)}") print() resolved = [] missing = [] for el in entries: tag = el.tag if tag == "Vanilla": continue name = el.get("name", "").strip() mod_id = el.get("id", "").strip() if tag == "Workshop" else None if not name: continue path = resolve_mod_path(name, mod_id) resolved.append(path) full_path = os.path.join(os.path.dirname(OUTPUT), path) if os.path.isfile(full_path): log_ok(f"{name}", indent=1) else: missing.append(name) log_warn(f"{name} — filelist.xml not found, adding anyway", indent=1) print() log_info(f"Mods resolved: {len([r for r in resolved if r])}") if missing: log_warn(f"Missing from LocalMods: {len(missing)}") for m in missing: log_info(f" {m}", indent=1) print() log_info("Generating config_player.xml...") barotrauma = ET.Element("Barotrauma") cps = ET.SubElement(barotrauma, "contentpackages") ET.SubElement(cps, "corepackage", path="Content/ContentPackages/Vanilla.xml") reg = ET.SubElement(cps, "regularpackages") for r in resolved: ET.SubElement(reg, "package", path=r) ET.indent(barotrauma) xml_str = ET.tostring(barotrauma, encoding="unicode", xml_declaration=True) with open(OUTPUT, "w") as f: f.write(xml_str) log_ok(f"Written {OUTPUT}") print() if __name__ == "__main__": main()