Reference4 min read4 sections

Simulation time and migration · contract 2

docs/SIMULATION_TIME.mdSource updated 2026-10-24
Markdown
On this page 4 sections

Ticks, input sequences, ECS commit ticks, scheduler ticks and event sequence/tick/expiry values have the unsigned 64-bit range 0–18,446,744,073,709,551,615. packages/schema/time.ts defines checked conversion: canonical decimal strings in JSON and worker messages, bigint for arithmetic/comparison. Numbers, whitespace, leading zeroes, signs, exponents and out-of-range values are rejected in the current format. Neither parsing nor comparison passes through JavaScript Number.

The fixed-step accumulator measures elapsed seconds with floating point and bounds catch-up as before. Its diagnostic step counter is now bigint. Simulation ticks advance only for accepted unpaused steps, while the external fixed clock counts callback executions; the two counters have different lifetimes. Wall-clock accumulators are not part of a simulation checkpoint.

#Formats and explicit upgrades

Data Previous Current Upgrade
Local player save format: 1, numeric tick format: 2, decimal-string tick parseSave accepts a nonnegative safe-integer legacy tick and produces an isolated current record
Simulation checkpoint Profiles 1 (numeric time), 2–4 (string time) darkwind.simulation.5, exact ticks plus bounded model animation state Explicit Simulation.migrateCheckpoint(world, legacy)
Input frame Numeric sequence Canonical decimal-string sequence Update live producers; legacy values are converted only inside checkpoint migration
ECS/event/scheduler snapshots Decimal strings already Unchanged No time-field migration
World content / relay room data Existing authored format / protocol 1 Unchanged No content or durable room migration

Simulation.fromCheckpoint accepts the current profile only. Migration first verifies the legacy envelope checksum, engine/content/catalog identity and size limits. For profile 1 it accepts only exact safe-integer legacy values and translates the two time fields and state format. Profile 2 already carries exact string time. Profiles 1 and 2 add authored rows; profile 3 preserves its existing rows while upgrading its verified compiler signature before resealing the current profile; see checkpoint migration. A complete independent runtime then validates ECS relationships, native physics, retained events, scheduler state and player agreement before returning a migrated checkpoint. Temporary runtimes are disposed. Source data and any existing runtime are untouched on success or rejection. A checksum is integrity checking, not authorization; the native checkpoint API remains trusted-input only.

Migration never guesses the lost precision of an old unsafe Number. Unknown profiles or formats, inconsistent tick/cursor state and malformed native data reject. The profile-1 fixture, captured from commit d21f675145173e6d8936764399e94519e88794b5, preserves native physics, pending input and an event just below the old limit. Its stored next-tick checkpoint provides an independent before/after migration comparison.

#Browser save retention

The player reads darkwind:lantern-house:save:v2 first, falling back to the v1 key only when v2 is absent. Valid legacy saves upgrade in memory; all new writes use v2. The original v1 bytes remain intact so an older build can still load its last pre-upgrade snapshot. New progress is stored only in v2 and does not flow back to an older build. Invalid current data disables writes and does not silently fall back to an older save. The existing explicit “Return to the house” reset clears both keys.

The runtime and worker exchange current-format state, so player diagnostics/downloads also expose exact string ticks. No browser BigInt JSON serializer patch is needed. The UI and native physics coordinates continue using ordinary finite numbers where they represent continuous quantities.

#Ordering and exhaustion

Input order uses bigint comparisons. A smaller sequence is ignored, including values differing by one above 2^53. Equal sequences retain the existing trusted-local behavior of refreshing held input. Pause and place travel clear movement while preserving the accepted sequence high-water mark. Input expiry also preserves that mark. Checkpoints capture the accepted frame, so a restored runtime rejects older input.

A simulation at the maximum tick rejects the next step before scheduler or physics work and remains checkpointable. The fixed clock similarly rejects before invoking its callback; neither counter wraps. Input producers stop rather than emit a value above the wire range. Existing event rules remain strict: a lifetime whose expiry would exceed uint64 rejects, rather than inventing a wrapped expiry. Whole-interaction/tick rollback and durable timeline replacement are separate remaining contracts; callers must treat a simulation fault as a boundary requiring recovery, not continue after arbitrary partial effects.

#Verification

tests/time.test.ts covers canonical conversion, unsafe legacy rejection, save copy isolation, complete native migration, exact continuation past 2^53, event expiry, input ordering/pause, malformed migration isolation and maximum-tick rejection without stepping. Browser acceptance verifies legacy bytes remain intact, the new key wins on reload, high ticks keep advancing and malformed saves remain protected.

The profile-4 replay fixture covers 426 boundaries and 26 complete restores across movement/events, large ticks/input sequences, doors and place travel. Node and the browser worker compare the same full evidence; the old profile-1, profile-2 and profile-3 traces remain as historical evidence. Exact agreement is for these named fixtures and the tested Node/Chromium pair, not universal physics determinism or the entire engine roadmap.

Clean source a25e1e7877608a3dd48c8f1100594f4193e9874b passed all 19 browser scenarios, including save-key preservation, exact large ticks and full Node/browser replay. Its capability report, simulation timing and profile-2 checkpoint timing preserve source identity and environment. These measurements follow the scope limits above.

End of Simulation time