Gessa Docs
Product · Explanation

Concept

Explanation: Netcode Model

How Gessa's server-authoritative runtime keeps a room's clock, authority, prediction admission, replication, load ladders, durability, and rejoin coherent across browser clients.
engine v1.0.234since v1Copy for LLM
engine v1.0.232

Last verified 2026-09-02 against engine v1.0.232.

Gessa runs multiplayer worlds under a server-authoritative runtime. The server simulates each room and replicates its state to browser clients; the client renders a projection of that state and, in a few tightly bounded cases, predicts ahead of it. This page explains the model: how the room clock advances, who owns authority, when a client is allowed to predict, how state reaches clients, what happens under load, and how a session survives a checkpoint and rejoins. It frames the why. Every concrete field, policy key, threshold, and capacity number lives in the Netcode Contract reference, which this page links down to and never restates.

Two framings from Backend Authority carry through everything here: the server owns the world, and the client is a non-authoritative view of it. Netcode is the discipline that keeps that view honest.

The contract is the interface

A room does not hand clients loose configuration flags. When a session is accepted, the server resolves a single versioned netcode contract from the room's policy and the authored project graph, validates it against the protocol schema, and hashes its canonical JSON. That hash travels on every authority frame, and a client that computes a different hash refuses the frame rather than guessing. The resolver is resolveRuntimeNetcodeContract; the contract tells each client exactly how to predict, interpolate, reconcile, and budget, so there is one source of truth per concern instead of client and server drifting apart.

The contract is immutable per content. Its cache key, runtimeNetcodeContractRevisionKey, fingerprints the room's identity and content coordinates (room, game, deployment, content version, environment, tick and snapshot rates, blueprint, room template, runtime budget) and deliberately excludes volatile bookkeeping such as player counts and lease progress, so the same content always resolves the same contract.

The room clock

The clock is a pure function of the tick rate, a start phase, and a monotonic clock. Input arrival never reschedules a tick. The period is held in full floating-point precision so a representable rate stays exact:

periodMs=1000max(1,tickRateHz)

Slot due-times are computed by multiplication from the origin rather than by accumulating a delta, so rounding error cannot compound over a long session. The count of ideal slots elapsed at a given instant is a floor with a small positive epsilon that absorbs floating-point error at slot boundaries for rates whose period is not exactly representable:

slots(now)=max(0,⌊now-originperiodMs+ε⌋)

Each planned step integrates exactly one tick of time; there is no free dt argument, which is what makes two rooms fed the same schedule advance identically.

When the process falls behind, the clock recovers a bounded number of missed deadlines and then declares the rest as a discontinuity rather than sprinting through a backlog. The count it recovers is ROOM_CLOCK_DEFAULT_MAX_CATCH_UP_TICKS; once the backlog reaches ROOM_CLOCK_DEFAULT_OVERLOAD_DISCONTINUITY_THRESHOLD_TICKS it runs only the newest step and reports the dropped slots. The accounting identity always holds:

Pseudocode
executed + declared = ideal

so time is never silently lost; a stall becomes an explicit, observable discontinuity.

Time itself is split into two domains that cannot be confused. Monotonic (process-relative) time drives cadence; wall (epoch) time is the only domain written into a durable or wire record. The two are nominal branded types, so passing one where the other is expected is a compile error, and the single sanctioned conversion is against an explicit anchor:

Pseudocode
wall = anchor.wall + (monotonic - anchor.monotonic)

This is why a cadence-domain slot time can never be stamped into a record as if it were a timestamp.

Authority and prediction admission

Each room has one writer. A lease supervisor renews the owner lease on a fraction of its term and surrenders authority on renewal failure, so exactly one process advances the room.

Client prediction is an exception the server grants narrowly, never a default. For the possessed character the resolver derives a transform owner: a dynamic-rigidbody character resolves to physics and is not predicted, a kinematic controller resolves to transform and may be predicted, and with no controllable character it fails closed to physics. Dynamic-rigidbody prediction is refused outright because it would require a rollback physics contract the engine does not ship; a client_predicted movement action on a physics body is demoted to server-authoritative with a diagnostic.

The contract exposes a small fixed set of input modes with different storage and mutation semantics, from fully server-authoritative through predicted-and-replayed to visual-only. Scripted behavior may be predicted only when it is declared deterministic, its host functions are on an allowlist, and its ops are a narrow allowlisted subset; anything else stays server-authoritative and reconciles. The exact modes, their effect systems, and one internal divergence worth knowing about are enumerated in the reference. Environment state (time of day, weather, wind) is never predicted at all: weather blending is float-divergent under rollback, so clients converge on replicated state plus a heartbeat.

Five static checks keep these boundaries from eroding: the client prediction path may not import a dynamic-physics engine, may not reach a script executor, must gate script prediction through the deterministic admission helper, must source the immutable authored graph at both resolution sites, and must read both movement dialects when deriving collision participants. They run as scripts/check-runtime-netcode-prediction-hardgates.mjs.

Replication

State reaches clients on two lanes, both unreliable. A pose lane carries frequent transform updates at the tick rate to a public audience; a world lane carries fuller snapshots and deltas at the snapshot rate to the interested audience only. Interest is spatial: an entity is relevant to a client inside a radius, bucketed into a cell grid.

A world delta is fitted to a byte budget by selectRuntimeWorldDeltaForBudget. Two rules make the fit safe rather than lossy: a delta that carries a full scene frame is refused rather than partially sent (a scene frame cannot be half-applied), and when entity updates are dropped to fit, every required parent upsert is pulled in before its children so a client never receives a child whose parent it has not seen. Which entities a client sees is composed by composeRuntimeClientSnapshotEntities, which unions the interested set with the always-relevant set and subtracts owner-private entities last.

Remote entities are interpolated in a delay buffer, and the contract emits more than one interpolation profile so an authored entity can choose to hug the newest sample, ride the default buffer, or run a doubled buffer for perfectly fluid slow movers; a stalled lane extrapolates only up to a fixed cap before halting rather than inventing unbounded motion. The lanes, profiles, and caps are in the reference.

The producer side is a closed corridor. The three functions that build authority, notice, and private frames may be referenced only from an authorized set of files, enforced by scripts/check-netcode-replication-fanout-boundary.mjs, so fan-out cannot sprawl into arbitrary call sites.

Load ladders

Overload is handled by two ordered ladders, not a single cliff. A per-room ladder sheds work in a fixed order as a room comes under pressure. A per-shard ladder composes above it: when rooms run on worker-thread shards, each shard sheds load in its own fixed order before it can ever approach the event-loop-lag kill threshold, and it never relaxes that threshold to make room. evaluateShardDegradation walks the shard ladder deterministically from calm upward and returns the single highest rung the current signals meet. Both ladders and their rung names are in the reference.

Admission is elastic rather than a hard refusal. When a deployment is genuinely full, a waiting player is enqueued in a capacity-overflow queue and promoted when a seat frees, and a refresh by the same actor returns the same place in line rather than cutting to the front.

Durability and rejoin

A room checkpoints periodically so it can be recovered, and a returning player presents a MAC-signed resume token with a bounded lifetime to re-admit into the running room. The protocol under which a room commits its durable tick stream is a one-way epoch ratchet, legacy.v1 to canonical.v1 to kernel.v1, that the database enforces; the newest epoch journals the tick and projects it behind the cadence (a durability inversion) rather than committing in-lane, and the runtime's measured capacity numbers were taken on that epoch. The epoch ladder and the durability contracts are in the reference.

How this connects to playability

A world is playable only when runtime authority, not a rendered scene, demonstrates the claimed behavior under a live session. The netcode model is the machinery behind that claim: admission, the first authoritative snapshot, and possession all run through the runtime described here. For what formally counts as playable and how it is proven, see Runtime Playability and Playability Proofs.

Was this helpful?Report an issueContact support

On this page