113 lines
4.7 KiB
Markdown
113 lines
4.7 KiB
Markdown
# The Steward — Simulation State Schema
|
|
|
|
## Current contract
|
|
|
|
`SimulationStateRecord` is the versioned JSON boundary for the current
|
|
simulation. The current world schema is v3 and captures:
|
|
|
|
- simulation seed, tick interval, tick count, clock remainder, and elapsed
|
|
clock ticks;
|
|
- every NPC's identity, needs, attributes, task lifecycle, target, position,
|
|
starvation state, and decision RNG stream;
|
|
- village resource counters;
|
|
- controlled per-NPC wander RNG streams;
|
|
- scene-independent resource state plus the definition facts needed while its
|
|
ResourceNode is unloaded.
|
|
- pantry contents, carried NPC inventory, the ordered economic event stream,
|
|
and its next stable event ID.
|
|
|
|
The top-level identity is:
|
|
|
|
```json
|
|
{
|
|
"schema": "the_steward.simulation",
|
|
"schema_version": 3
|
|
}
|
|
```
|
|
|
|
Unknown schema versions and structurally incomplete records are rejected. Add
|
|
an explicit migration function before accepting a future version; never
|
|
silently reinterpret old data as the newest layout.
|
|
|
|
NPC action and profession references are serialized as stable strings and
|
|
restored as `StringName`. Records referencing unknown executable actions or
|
|
professions are rejected against the validated definition registry. See
|
|
[the simulation definitions](SIMULATION_DEFINITIONS.md).
|
|
|
|
## Deterministic continuation
|
|
|
|
RNG seeds and internal states are encoded as decimal strings. They are signed
|
|
64-bit values and cannot safely pass through every JSON number implementation
|
|
without precision loss. Converting them to ordinary JSON numbers caused a
|
|
restored simulation to retain the same visible state while silently changing
|
|
its future random sequence.
|
|
|
|
`tests/simulation_state_serialization_test.gd` protects this contract by:
|
|
|
|
1. running a fixed-seed scenario for 48 ticks without interruption;
|
|
2. running the same scenario for 24 ticks;
|
|
3. serializing and restoring into a fresh manager;
|
|
4. running the remaining 24 ticks;
|
|
5. requiring the complete final-state checksums to match.
|
|
|
|
It also verifies clock remainder, resource amount/reservation/enabled
|
|
round-tripping, presentation unload/rebind, and rejection of unsupported
|
|
schemas.
|
|
|
|
NPCStateRecord v2 adds the resolved travel destination and whether it is
|
|
active. Nested v1 NPC records migrate explicitly with no invented active
|
|
destination. This allows a reloaded visual to resume travel without changing
|
|
target selection or deterministic RNG state.
|
|
|
|
NPCStateRecord v3 adds carried inventory. SimulationStateRecord v2 adds
|
|
StorageStateRecord entries; world-schema v1 migrates legacy village food into
|
|
the stable `village_pantry` record. Parsed storage values are canonicalized so
|
|
save/restore continuation retains byte-stable checksums.
|
|
|
|
SimulationStateRecord v3 adds ordered `EconomicEventRecord` entries and
|
|
`next_event_id`. World schemas v1 and v2 migrate explicitly to an empty event
|
|
stream. Event records use stable source/destination/item IDs, reject invalid or
|
|
duplicate IDs, and preserve deterministic ordering across save/restore. See
|
|
[the economic event stream](ECONOMIC_EVENTS.md).
|
|
|
|
## Resource authority
|
|
|
|
`SimulationManager` owns `ResourceStateRecord` instances independently of the
|
|
scene tree. Amount, reservation, enabled state, action/resource identity,
|
|
yield, and usage permissions remain available and serializable while no
|
|
ResourceNode is loaded.
|
|
|
|
ResourceNode binds to a record by stable ID and provides presentation,
|
|
interaction transforms, and active-world discoverability. Unloading a node does
|
|
not delete its record. Loading another node with the same stable ID rebinds to
|
|
the existing record and cannot overwrite its amount or reservation with scene
|
|
defaults.
|
|
|
|
ResourceStateRecord v2 adds the definition facts needed while unloaded. Nested
|
|
v1 resource records are migrated explicitly and hydrate their missing
|
|
definition from a matching presentation node when one becomes available.
|
|
|
|
## Local quicksave boundary
|
|
|
|
`SaveSlotStore` writes the existing versioned JSON record to
|
|
`user://saves/quicksave.json`. It validates the serialized record before
|
|
replacement, limits file size, restricts slot names, preserves the previous
|
|
file during replacement, and can recover that backup if replacement is
|
|
interrupted. Loading parses and validates the complete record before mutating
|
|
the simulation.
|
|
|
|
In `main.tscn`, F5 saves and F9 loads this slot. Restoration rebuilds active NPC
|
|
visuals from authoritative state.
|
|
|
|
## Deliberately out of scope
|
|
|
|
This phase does not yet provide:
|
|
|
|
- a save-slot menu, metadata, thumbnails, autosaves, or multiple profiles;
|
|
- migrations older than the explicitly supported world schemas v1 and v2;
|
|
- player inventory, relationship, schedule, or social-memory records;
|
|
- persistence for the player transform or presentation-only scene state.
|
|
|
|
Those features should build on this boundary rather than inventing parallel
|
|
serialization paths.
|