6.4 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.
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;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.
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 baseline 01, and Testing and benchmarks.