5.6 KiB
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.
It is intentionally additive: the current Jajce SimulationManager remains
the playable local facade while the regional contracts prove deterministic
unloaded work and conservation in isolation.
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;PolityStateRecordandDiplomaticRelationRecord— 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:
(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.
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
The regional package is not yet the production authority for main.tscn:
- Jajce local saves still use
SaveSlotStoreandSimulationStateRecordv16; RegionalCaravanServiceis a complete isolated transfer proof, not the live economy's 20-trade 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 an explicit regional facade that delegates to these records
and services, while the active adapter presents only a bounded relevant context.
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 baseline 01, and Testing and benchmarks.