Files
gamedev-the-steward/docs/FEATURE_REGIONAL_SIMULATION_AND_PERSISTENCE.md
T
2026-08-23 17:20:41 +02:00

133 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Feature slice: regional simulation and persistence
The regional package is the scale foundation for one authority spanning
settlements, routes, caravans, named people, aggregate cohorts, and polities.
The production `SimulationManager` now owns a bounded regional facade for one
inbound Jajce route, while the broader regional contracts continue to prove
deterministic unloaded work and conservation before wider integration.
## Regional record model
`RegionalWorldState` is a primitive-only, versioned aggregate with deterministic
indexes for:
- `LocationStateRecord` — world/location identity, type, address, and history;
- `SettlementStateRecord` — location/polity, tier, founding group, stockpile;
- `RouteStateRecord` — endpoints, travel ticks, direction, history;
- `MobileGroupStateRecord` — caravan/army identity, membership, route state,
cargo ledger, capacity, arrival/departure ticks, and cargo event cursor;
- `PersonStateRecord` — globally unique named people and memberships;
- `PopulationCohortRecord` — conserved aggregate residents;
- `PolityStateRecord` and `DiplomaticRelationRecord` — sparse regional politics.
Membership is explicit and validated: a person is at one location or in one
mobile group, routes connect existing locations, groups reference existing
routes/members, and IDs are globally unique and append-only. Records contain
stable IDs and primitive values only; no scene, `NodePath`, resource, or
callable can enter the regional payload.
## Deterministic scheduled work
`ScheduledJobRecord` and `RegionalJobScheduler` order due work by:
```text
(due_tick, phase, entity_id, stable_sequence)
```
The scheduler supports O(log n) insertion/cancellation, deterministic due
draining, repeats, dedupe keys, a serialized cursor, and strict restore
validation. A frame or service budget may process one, eight, or unlimited jobs
per call, but all budgets must produce the same order, checksum, and final
backlog. Dynamic jobs inserted while draining are ordered into the same queue,
not handled by an ad-hoc frame loop.
`KeyedRandom` derives values from `(world_seed, system_id, entity_id,
occurrence)`, so update order and save/load do not consume another entity's
random stream. `AnalyticalRegionalUpdates` advances elapsed needs, growth, and
periodic work from last-updated ticks instead of scanning every entity each
tick.
## The caravan proof
`RegionalCaravanService` owns an envelope containing `RegionalWorldState`,
`RegionalJobScheduler`, and `WorldEventStore`. `depart()` validates origin,
destination, route, cargo capacity, group status, and exact state; it withdraws
the cargo and schedules a typed arrival. The arrival handler updates the group,
deposits the exact cargo into the destination settlement, and appends the causal
load/depart/arrive/deposit chain. Presentation mode is an input label for the
test, never an authority branch.
The service is transactional: invalid jobs, forged event pairs, stale event
cursors, duplicate departure pairs, and malformed restore envelopes fail with
no partial mutation. `to_dictionary()`, `from_dictionary()`, and `checksum()`
provide deterministic save/restore. The test runs always-loaded, never-loaded,
and load/unload presentation modes through the same authoritative service.
`RegionalSimulationFacade` is the first production consumer. It owns one
authored Travnik-to-Jajce route, advances before each committed local tick, and
exposes one bounded delivery command. The cargo is a separate catalogued
`regional_trade_goods` item with no local storage tag, so this proof does not
shadow or duplicate the pantry and woodpile economy.
## Chunked persistence
`RegionalChunkedPersistence` captures the service into a manifest plus fixed
primitive chunks:
- one global index;
- one location chunk per location;
- one mobile-group chunk per group;
- one scheduler chunk;
- fixed-size event segments.
Each descriptor contains record count, byte size, and SHA-256 checksum. Restore
rejects missing, duplicated, unknown, reordered, tampered, out-of-scope, or
non-primitive chunks. `load_active_location_metadata()` reads the global index
and one location while retaining authoritative references to unloaded chunks.
`RegionalChunkedFileStore` writes immutable fixed-width generation directories,
validates a temporary generation before install, updates a small current
pointer, recovers the newest valid generation when the pointer is stale, and
keeps at least two valid generations when pruning. It uses Godot's available
flush/close/rename primitives; a platform fsync is not exposed by this layer.
The store has explicit path, file-count, chunk-size, generation-size, and
primitive-tree bounds.
## Current integration boundary
`main.tscn` now advances the regional facade and `SaveSlotStore` writes a
combined `SimulationSaveManifest` containing the unchanged local v16 record
and canonical regional envelope. Both checksums plus world, seed, location,
and tick alignment are validated before either authority is restored. Raw
local v16 slots remain compatible and create an idle regional facade;
explicit non-Jajce adapter scopes keep writing local v16 until they have a
matching regional facade. Those scopes cannot dispatch a Jajce delivery, and
local-only saving fails closed if regional state is no longer reconstructibly
idle.
The remaining boundary is intentionally narrow:
- the facade has one callable inbound route, not the live economy's 20-trade
market scheduler;
- event retention/rollups and regional chunk files are not yet one integrated
long-horizon save transaction;
- loaded/unloaded local NPC travel still has the old active-visual boundary;
- cohort promotion, settlement growth, offices, diplomacy, armies, and
aggregate conflict remain planned.
Do not create one `SimulationManager` per settlement or caravan. The next
production step is to publish retained raw events, daily rollups, and chunked
regional history as one atomic, lazily loaded transaction behind the facade.
## Scale evidence
`RegionalScaleBenchmark` builds five settlements, 20 caravans × 12 named
members, 2,000 named people, 50,000 aggregate residents, and 2,025 jobs. It
proves conservation, canonical roundtrip, budget parity, and repeatable
checksums. It does **not** claim rendered frame time, a complete market, a
30-day economy, or weak-PC GPU performance.
See [Regional simulation contract](REGIONAL_SIMULATION.md),
[regional baseline 01](benchmarks/REGIONAL_SCALE_BASELINE_01.md), and
[Testing and benchmarks](FEATURE_TESTING_AND_BENCHMARKS.md).