Files
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
3.2 KiB
Python

#!/usr/bin/env python3
"""Report keyboard keys that more than one keybind uses, with the pack's defaults applied.
NeoForge's Controls screen marks two keybinds as conflicting whenever they share
a base key, even when one only works in GUIs and the other only in-game (its
KeyMapping.same() falls back to comparing keys). The only exception is two
binds in the same context with different modifiers (Ctrl+F next to F). So the
pack's defaults give every keyboard bind its own base key.
The list of keybinds comes from a client's options.txt (written on the first
launch), so run this after adding a mod and launching the game once. Mouse
buttons are left out: JEI's click actions in its item list share them with
attack/use/pick block by design.
Usage: tools/check_keybinds.py [options.txt] (default: the Prism dev instance)
"""
import sys
from collections import defaultdict
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DEFAULTS = ROOT / "pack/config/defaultoptions/keybindings.txt"
# Share a key on purpose (Adrian, 2026-11-09): JEI's only works in a screen, Cobblemon's sends out a Pokémon in the world.
BY_DESIGN = [{"key.jei.showRecipe", "key.cobblemon.throwpartypokemon"}]
INSTANCE = (Path.home() / ".var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances"
/ "Cobblemon Survival Pack (dev)/minecraft/options.txt")
def parse(lines):
binds = {}
for line in lines:
if not line.startswith("key_"):
continue
name, _, rest = line[4:].partition(":")
key, _, modifiers = rest.partition(":")
binds[name] = (key, modifiers.replace("NONE", ""))
return binds
def main():
options = Path(sys.argv[1]) if len(sys.argv) > 1 else INSTANCE
if not options.exists():
sys.exit(f"{options} not found; launch the game once so it is written")
binds = parse(options.read_text().splitlines())
defaults = parse(DEFAULTS.read_text().splitlines())
unknown = sorted(set(defaults) - set(binds))
binds.update(defaults)
by_key = defaultdict(list)
for name, (key, modifiers) in binds.items():
if key.startswith("key.keyboard.") and key != "key.keyboard.unknown":
by_key[key].append((name, modifiers))
def clashes(entries):
# One mod's binds share a context, so distinct modifiers keep them apart (e.g. JEI's
# Page Down and Shift+Page Down). Anything else on the same key shows as a conflict.
if {name for name, _ in entries} in BY_DESIGN:
return False
mods = {name.rsplit(".", 1)[0] for name, _ in entries}
return len(entries) > 1 and not (len(mods) == 1 and len({m for _, m in entries}) == len(entries))
shared = {k: [f"{n}{' +' + m if m else ''}" for n, m in v] for k, v in sorted(by_key.items()) if clashes(v)}
print(f"{len(binds)} keybinds, {len(defaults)} set by the pack, {len(by_key)} keyboard keys in use")
for name in unknown:
print(f" warning: the pack sets {name}, which this client doesn't have")
if shared:
print("keys used by more than one keybind:")
for key, names in shared.items():
print(f" {key}: {', '.join(names)}")
sys.exit(1)
print("no keyboard key is used twice")
if __name__ == "__main__":
main()