07 DOCS · DETERMINISM
Same seed,
same game.
A Fizlet sim that steps on a fixed dt, draws every random number from engine.random and keeps its state in typed arrays replays exactly from a seed. That is what makes a replay test possible, and what lets two players run the same game in lockstep with only their inputs crossing the wire.
The rules for a deterministic step
- Seeded random only.
engine.random()is seeded bycreateEngine({ seed })and allocation-free;engine.random.statesnapshots and restores it. NoMath.randomin the step. - No clock in the step. Use
dtandengine.steps(an integer count of fixed steps), neverperformance.now()orDate. - No layout reads. A sim that reads the page depends on the screen. In net play, fix the arena size and scale the drawing.
- Inputs as bits. Pack what a player does into one 32-bit word per frame; the step reads only the words.
- Nothing else moves on engine steps. Anything that advances with the step (a drop timer, a random draw in an effect) must advance on the same frames on every peer.
// The sim: typed arrays, e.random, inputs as bits. No Math.random, no clock, no DOM.
function sim(e: Engine, players: number) {
const pos = new Float64Array(players * 2);
for (let i = 0; i < pos.length; i++) pos[i] = e.random() * 100;
return {
state: new Uint8Array(pos.buffer), // a byte view, made once
step(e: Engine, inputs: Uint32Array) {
for (let p = 0; p < players; p++) {
const bits = inputs[p] ?? 0;
pos[p * 2] = (pos[p * 2] ?? 0) + ((bits & 2 ? 1 : 0) - (bits & 1 ? 1 : 0)) * 2;
if (bits & 4) pos[p * 2 + 1] = (pos[p * 2 + 1] ?? 0) + e.random() * 8;
}
},
};
}
// In any test (Node or a browser): two peers, 600 frames, a late and jittery link.
const r = syncCheck((e, slot, players) => {
const s = sim(e, players);
return { ...s, input: () => (e.steps * 7 + slot) & 7 }; // scripted presses
}, { players: 2, frames: 600, latency: 3, jitter: 2 });
if (!r.ok) throw new Error(`peers diverged at frame ${r.diverged}`); // -1 when none didsyncCheck: find the first frame that differs
syncCheck(setup, { players, frames, latency, jitter }) from @fzlt/core/presence/check runs your sim as lockstep clients over an in-memory link with the latency and jitter you ask for, hashes each client's state every frame and returns { ok, frames, diverged, stalls }. diverged is the first frame whose state differed, or -1. It is test-only code (4,870 B gzip) and never ships to players.
The presence brick's own tests run two clients byte-identical for 3,000 frames with no latency, stay identical with delayed, jittered and reordered inputs at unequal frame rates, run four players at latency 3, and catch a forced desync at the next hash check.