Files
cobblemon-survival-pack/tools/check_keybinds.py
T
claudeandClaude Opus 5.5 398301f0be Cobblemon Survival Pack: phases 0-2 and Phase 3 zones and hazards
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>
2026-10-29 19:01:21 +01:00

73 lines
3.0 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"
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.
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()