Files
Reaper-s-Sky/server/build_mods.py
T
ReaperOfVeriod 2a5354fd63 Add Reaper's Sky dedicated server deploy files
- server/mods.json: 59 server-safe mods pinned by sha1 (19 client-only excluded)
- server/deploy.sh: idempotent Debian 13 installer (Java 21, NeoForge 21.1.248,
  mods, eula/server.properties, 8G run.sh)
- server/build_mods.py: regenerate mods.json from the Prism instance
- server/README.md: setup + ops guide
2026-08-20 20:08:14 +02:00

92 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Regenerate server/mods.json from the Prism instance's mods directory.
For every .jar in the pack instance it:
- computes sha1
- looks up the Modrinth download URL via the version_file API
(aero-shadow-fix is CurseForge-only, pinned manually)
- drops the 19 client-only mods
and writes the server manifest to mods.json.
Usage: python3 build_mods.py
"""
import hashlib, json, os, subprocess, sys, time
INSTANCE = os.path.expanduser(
"~/.local/share/PrismLauncher/instances/Reaper's Sky - dev/.minecraft/mods")
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mods.json")
MC_VERSION = "1.21.1"
NEOFORGE_VERSION = "21.1.248"
CLIENT_ONLY = {
"iris-neoforge-1.8.14-beta.1+mc1.21.1.jar",
"EuphoriaPatcher-1.9.3-r5.8.1-neoforge.jar",
"iris-flywheel-compat-NeoForge-2.3.1.jar",
"sodium-neoforge-0.8.12+mc1.21.1.jar",
"ImmediatelyFast-NeoForge-1.6.12+1.21.1.jar",
"entityculling-neoforge-1.10.5-mc1.21.1.jar",
"dynamic-fps-3.11.4+mc1.21.0-neoforge.jar",
"MouseTweaks-neoforge-mc1.21-2.26.1.jar",
"appleskin-neoforge-mc1.21-3.0.9.jar",
"Controlling-neoforge-1.21.1-19.0.5.jar",
"Searchables-neoforge-1.21.1-1.0.2.jar",
"skinlayers3d-neoforge-1.11.2-mc1.21.1.jar",
"xaerominimap-neoforge-1.21.1-26.4.2.jar",
"xaeroworldmap-neoforge-1.21.1-1.44.2.jar",
"Jade-1.21.1-NeoForge-15.10.6.jar",
"jade-sable-compat-1.3.0.jar",
"aero-shadow-fix.jar",
"aeronautics-propeller-blur-1.0a.jar",
"jei-1.21.1-neoforge-19.44.0.403.jar",
}
CURSEFORGE_ONLY = {
"aero-shadow-fix.jar": "https://www.curseforge.com/api/v1/mods/1641969/files/8589529/download",
}
UA = "opencode-pack-builder/1.0"
def sha1(path):
h = hashlib.sha1()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
h.update(chunk)
return h.hexdigest()
def resolve(jar, digest):
if jar in CURSEFORGE_ONLY:
return CURSEFORGE_ONLY[jar]
r = subprocess.run(
["curl", "-s", "--max-time", "20", "-H", f"User-Agent: {UA}",
f"https://api.modrinth.com/v2/version_file/{digest}?algorithm=sha1"],
capture_output=True, text=True)
try:
return json.loads(r.stdout)["files"][0]["url"]
except Exception:
return ""
def main():
mods = []
for jar in sorted(os.listdir(INSTANCE)):
if not jar.endswith(".jar"):
continue
if jar in CLIENT_ONLY:
print(f" skip (client-only): {jar}")
continue
digest = sha1(os.path.join(INSTANCE, jar))
url = resolve(jar, digest)
if not url:
print(f" !! no URL for {jar} -- fix manually", file=sys.stderr)
mods.append({"file": jar, "url": url, "sha1": digest})
time.sleep(0.1)
manifest = {"minecraft": MC_VERSION, "neoforge": NEOFORGE_VERSION, "mods": mods}
with open(OUT, "w") as f:
json.dump(manifest, f, indent=2)
print(f"Wrote {len(mods)} server mods to {OUT}")
if __name__ == "__main__":
main()