Free NeoForge 1.21.1 modpack on Cobblemon 1.8.4 built with packwiz, plus the Kotlin companion mod (cobblesurvival) and its built-in data pack. - Phase 0: packwiz pack with Modrinth/CurseForge cross-links, build and export tooling, Prism instance sync, test server with smoke checks. - Phase 1: Poke Ball shaped island (WorldGen Editor preset, seed isle-26), Adrian's biome list, no natural ore, structures without ore or vanilla mobs, no Nether portals, world template builder and world report. - Phase 2: permadeath (CobbleHardcoreMon plus the companion mod's gap fixes), Revival Charm, expensive Healing Machine, Lootr refreshing loot, in-game self-tests on Minecraft's GameTest framework. - Phase 3 so far: 14 zones with their own spawn pools and level bands (tools/zone_layout.py, tools/zone_spawns.py, "zone" spawn condition), Cold Sweat heat and cold as gates with boss-drop insulation recipes, spawn survey and hazard self-tests in tools/quick_check.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Install or update a packwiz pack into a folder with packwiz-installer.
|
|
|
|
packwiz-installer wants a URL, so the pack folder is served on 127.0.0.1 for
|
|
the duration of the sync. It downloads Modrinth and CurseForge files from
|
|
their own CDNs and removes files that left the pack since the last sync.
|
|
|
|
Usage: tools/packwiz_sync.py <client|server> <target_dir> [pack_dir]
|
|
(pack_dir defaults to build/stage/full)
|
|
"""
|
|
import functools
|
|
import http.server
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import threading
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
CACHE = ROOT / "build" / ".cache"
|
|
# Pinned; the bootstrap runs with --bootstrap-no-update so neither self-updates.
|
|
TOOLS = {
|
|
"packwiz-installer-0.5.14.jar":
|
|
"https://github.com/packwiz/packwiz-installer/releases/download/v0.5.14/packwiz-installer.jar",
|
|
"packwiz-installer-bootstrap-0.0.3.jar":
|
|
"https://github.com/packwiz/packwiz-installer-bootstrap/releases/download/v0.0.3/packwiz-installer-bootstrap.jar",
|
|
}
|
|
|
|
|
|
def cached(name):
|
|
jar = CACHE / name
|
|
if not jar.exists():
|
|
CACHE.mkdir(parents=True, exist_ok=True)
|
|
with urllib.request.urlopen(TOOLS[name], timeout=120) as r, open(jar, "wb") as f:
|
|
shutil.copyfileobj(r, f)
|
|
return jar
|
|
|
|
|
|
def java():
|
|
for candidate in (Path.home() / ".var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/java/java-runtime-delta/bin/java",):
|
|
if candidate.exists():
|
|
return str(candidate)
|
|
return shutil.which("java")
|
|
|
|
|
|
class QuietHandler(http.server.SimpleHTTPRequestHandler):
|
|
def log_message(self, *args):
|
|
pass
|
|
|
|
|
|
def sync(side, target, pack_dir):
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
handler = functools.partial(QuietHandler, directory=str(pack_dir))
|
|
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
|
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
try:
|
|
url = f"http://127.0.0.1:{server.server_port}/pack.toml"
|
|
subprocess.run([java(), "-jar", str(cached("packwiz-installer-bootstrap-0.0.3.jar")),
|
|
"--bootstrap-no-update",
|
|
"--bootstrap-main-jar", str(cached("packwiz-installer-0.5.14.jar")),
|
|
"-g", "-s", side, url],
|
|
cwd=target, check=True)
|
|
finally:
|
|
server.shutdown()
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 3 or sys.argv[1] not in ("client", "server"):
|
|
sys.exit(__doc__)
|
|
pack_dir = Path(sys.argv[3]) if len(sys.argv) > 3 else ROOT / "build" / "stage" / "full"
|
|
sync(sys.argv[1], Path(sys.argv[2]).resolve(), pack_dir.resolve())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|