#!/usr/bin/env python3 """rebuild_manifest.py — make public/props/manifest.json match what is on disk. python3 tools/gen/rebuild_manifest.py The manifest is the list the game loads at boot; a prop missing from it renders as a placeholder even though its PNG is sitting right there. Two ways that happens: a concurrent batch losing an entry to a read-modify-write race (now locked in props_import.py, but earlier runs were not), or a sprite copied into the directory by hand. Directory listing is the source of truth here, deliberately — the PNGs are what the game can actually load. """ from __future__ import annotations import json from pathlib import Path PROPS = Path(__file__).resolve().parent.parent.parent / "public" / "props" def main() -> None: names = sorted(p.stem for p in PROPS.glob("*.png")) man_path = PROPS / "manifest.json" before = set(json.loads(man_path.read_text()).get("props", [])) if man_path.exists() else set() man_path.write_text(json.dumps({"props": names}, indent=2) + "\n") added = [n for n in names if n not in before] dropped = sorted(before - set(names)) print(f"manifest now lists {len(names)} props") if added: print(f" recovered (on disk, were missing from the manifest): {', '.join(added)}") if dropped: print(f" removed (listed but no PNG on disk): {', '.join(dropped)}") if __name__ == "__main__": main()