133 lines
6.4 KiB
Markdown
133 lines
6.4 KiB
Markdown
# 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).
|