acron.persist¶
Files a game keeps between sessions: JSON read defensively, written atomically, and brought forward through a schema-version chain.
Three mechanisms every save system re-invents, grown out of REGOLITH’s run
file and ECHO’s prints (GAMEDEMO_MINING.md §3 is the mined rule set):
Defensive reads.
read_json()answers the fallback for a file that is missing, unreadable or not JSON — a game must start whatever is on disk — andread_object()narrows that to a JSON object, since a file holding[1, 2]is valid JSON and once crashed a boot on.items(). A file that is on disk but will not parse is evidence, not garbage: it is moved aside as<name>.corrupt(quarantine()) so the loss can be looked at, and the read falls back.Atomic writes.
write_json()goes through a temporary file, anfsyncand a rename, so a power cut between the write and the rename leaves the previous file rather than a zero-length one. It answers whether the write landed — a save that silently fails is a player’s evening lost, and the caller should say so once.Versioned migration.
migrate()walks a dictionary forward through{from_version: fn}steps to the version the build writes (vmissing or malformed reads as version 1, the first shipped shape); a newer file than the build knows is handed back as it is, for the defensive reader to take what it recognises. The steps themselves are the game’s.
Paths are pathlib.Path; where the files live and what they are
called is policy and stays with the game.
Global Functions
- migrate(d: dict, version: int, upgrades: dict[int, Callable[[dict], dict]]) dict¶
dbrought forward toversionthroughupgrades({from_version: fn(dict) -> dict}), one step at a time. A step the table lacks ends the walk; a version pastversionis handed back untouched.
- read_json(path, fallback=None)¶
The JSON in
path, orfallbackif the file is missing, unreadable, or not JSON (in which case it is quarantined).
- read_object(path) dict | None¶
read_json(), narrowed to a JSON object: anything else — a list, a string, a number — reads asNone.
- remove(path) bool¶
Remove a file. True if it is gone (or never was); False only when it is there and could not be removed — which the caller should say.