Files
cobblemon-survival-pack/tools/check_server_update.py
T
claudeandClaude Opus 5.5 4083791944 Phase 9: reproducible builds, balance from the server, spark check, play feedback
Build: every tool pinned (JDK, packwiz, Gradle plugins, Python packages), exports
normalised, tools/check_reproducible.py proves two clean builds are byte-identical.

Server-side balance: a server can load config/cobblesurvival/datapack.zip in place of
the jar's data, /reload applies it, tools/server_update.py packages it and refuses
when a client change is needed. JEI drop pages are sent by the server. Code-held
balance numbers moved to tuning/server.json.

Performance: spark in the pack (server only), tools/perf_test.py with fake players
whose chunks really are active, docs/performance.md.

Adrian's feedback: default keys R, U, J, §; drop pages link to Cobblemon Info and show
real chances; bosses only hurt in battles; battle XP halved; channels between the
islands low level; temperature ×3 with insulation doubled; hazards start at once; no
item dots on the map; a wild attack starts a battle; the starter only faints; quest
book on join with a basics page and an HMs page; regional forms obtainable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 16:26:30 +01:00

77 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Check on the test server that a balance change goes live with tools/server_update.py alone (Adrian, 2026-11-09):
the server loads the update's data instead of the copy in its jar, `/reload` picks up a changed file while it runs,
and without the update the jar's copy is used again.
Usage: tools/check_server_update.py (needs eula.txt from tools/selftest.py --accept-eula)
"""
import json
import subprocess
import sys
import time
import zipfile
from io import BytesIO
from pathlib import Path
from mcserver import ROOT, SERVER, Server, eula_accepted, prepare
ZIP = SERVER / "config/cobblesurvival/datapack.zip"
TUNING = "data/cobblesurvival/tuning/server.json"
LEVEL = "world-update-check"
FLAT = {"level-name": LEVEL, "level-type": "minecraft:flat",
"generator-settings": '{"layers":[{"block":"minecraft:stone","height":4}],"biome":"minecraft:plains"}'}
def expect(line, *parts):
missing = [p for p in parts if p not in line]
if missing:
sys.exit(f"expected {missing} in: {line}")
print(f"ok: {line.strip()}")
def set_boss_evs(value):
with zipfile.ZipFile(ZIP) as z:
items = [(i, z.read(i.filename)) for i in z.infolist()]
out = BytesIO()
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for info, content in items:
if info.filename == TUNING:
content = json.dumps(json.loads(content) | {"boss_evs": value}).encode()
z.writestr(info, content)
part = ZIP.with_name(ZIP.name + ".part")
part.write_bytes(out.getvalue())
part.replace(ZIP) # as an update does, with the server running
def main():
if not eula_accepted():
sys.exit("build/server/eula.txt does not accept the Minecraft EULA yet (see tools/selftest.py --accept-eula).")
args_file = prepare()
subprocess.run([sys.executable, str(ROOT / "tools/server_update.py"), "--install", str(SERVER), "--level", LEVEL, "--force"], check=True)
try:
with Server(args_file, FLAT, stdout_name="update-check-stdout.log") as server:
server.wait_ready()
first = server.command("cobblesurvival data")
expect(first, "data server;", "boss_evs 170;")
set_boss_evs(150)
server.command("reload")
for _ in range(60):
time.sleep(2)
line = server.command("cobblesurvival data")
if "boss_evs 150" in line:
break
expect(line, "data server;", "boss_evs 150;")
server.stop()
ZIP.unlink()
with Server(args_file, FLAT, stdout_name="update-check-stdout.log") as server:
server.wait_ready()
expect(server.command("cobblesurvival data"), "data jar;", "boss_evs 170;")
server.stop()
finally:
ZIP.unlink(missing_ok=True)
print("SERVER UPDATE CHECK PASSED")
if __name__ == "__main__":
main()