Quick answer: Replay and spectator bugs are determinism bugs: a recorded match that plays back differently than it happened, or a spectator whose view drifts from the live game. To reproduce, capture the replay seed, the recorded input stream, the engine and content version, and the tick where playback diverged from the original outcome. With the seed, inputs, and divergence tick you can rerun the simulation and find the nondeterministic step.

Replays and spectating both rest on the same fragile promise: that re running the same inputs through the same simulation produces the same result. When a replay shows a player winning a fight they actually lost, or a spectator sees a different position than the players do, that promise has been broken by a sliver of nondeterminism, a floating point difference, an unseeded random, an iteration order that varies. The player reports that the replay is wrong, but the bug is buried at some tick where two runs of the same simulation diverged. This post covers the determinism state to capture so replay drift becomes a reproducible divergence you can bisect.

Replays depend on determinism

A replay is usually not a video, it is a recording of inputs that the engine re simulates to reconstruct the match. This is compact and powerful, but it demands perfect determinism: every system, physics, AI, RNG, must produce identical output from identical input every time. If anything reads uninitialized memory, depends on frame timing, or iterates a hash map in nondeterministic order, the replay will slowly drift until it shows events that never happened. The recorded inputs are correct, but the simulation that consumes them is not reproducible, and that gap is the bug.

Spectating is the live cousin of the same problem. A spectator client often runs the same simulation from the same inputs as the players, just delayed, so a spectator whose view disagrees with the players has desynced exactly as a wrong replay has. The root cause is identical: a nondeterministic step somewhere in the simulation. Treating replays and spectating as two faces of one determinism requirement means the same captured state, the seed, the inputs, and the divergence point, serves to debug both, which is a meaningful simplification.

Capture the seed, inputs, and versions

The core payload is everything needed to re run the simulation identically. Capture the replay seed or initial RNG state, a reference to or hash of the recorded input stream, and the exact engine build and content version, since a replay recorded on one version will desync if played on another. Version mismatch is one of the most common replay bugs and one of the easiest to detect once you capture both versions. With the seed, the inputs, and matching versions pinned, the simulation should be reproducible, and any drift is a genuine determinism defect rather than an environment mismatch.

Record the simulation tick rate and any fixed timestep configuration alongside the inputs, because replays that re simulate at a different step than they recorded will drift even when the logic is otherwise deterministic. A replay captured at sixty hertz and played back at a variable frame rate is a classic source of subtle divergence. Pinning the timestep makes the playback environment match the recording environment exactly, removing one more way for a correct simulation to produce an incorrect replay.

Find the diverging tick

The single most useful thing you can capture for a desync is the tick at which the two runs first disagree. If your simulation periodically hashes its state, the original match and the replay each produce a stream of checksums, and the first tick where they differ localizes the bug in time. Capture that divergence tick and the state hash on both sides, and you turn a vague the replay is wrong into the replay diverges at tick 4127, which is a bisectable, concrete starting point. Without it you are hunting a needle across an entire match's worth of frames.

Make the state hash granular enough to point at a subsystem, not just a tick, by hashing physics, AI, and RNG state separately. When the divergence tick arrives with a note that only the physics hash differs while AI and RNG match, you have narrowed the search from the entire simulation to one system in one frame. That extra resolution costs little to compute and saves enormous time, because the difference between knowing a replay desynced and knowing physics desynced at tick 4127 is often the difference between a day of hunting and an afternoon.

Common sources of nondeterminism

Once you have a divergence tick, the usual suspects are a short list worth knowing. Floating point operations whose order differs between recording and playback, RNG that was not seeded from the replay, container iteration that depends on memory addresses, time based logic that reads wall clock instead of tick count, and uninitialized values that happen to differ between runs. Capturing the divergence tick plus the subsystem active at that tick narrows which suspect to examine first. Determinism bugs feel mystical until you can point at the exact frame and system, after which they are usually a small, specific fix.

Platform differences add a layer in cross platform games, where the same build can produce slightly different floating point results on different hardware, desyncing a replay recorded on one device and watched on another. Capturing the platform and hardware alongside the versions lets you spot when divergence correlates with a device boundary rather than a code path. That distinction matters because the fix, forcing deterministic math modes or fixed point, is different from fixing an unseeded RNG. The captured environment tells you which class of nondeterminism you are dealing with.

Setting it up with Bugnet

Bugnet's in-game report button lets a player flag a wrong replay or a desynced spectator view, and you attach the determinism state as custom fields: the replay seed, the input stream reference, the engine and content version, the platform, and the divergence tick with the state hashes on both sides. The player writes the replay shows the wrong winner, and everything needed to re run the simulation arrives in one dashboard. Occurrence grouping clusters reports that diverge at the same tick or in the same subsystem, which often means one nondeterministic step is behind many seemingly unrelated replay complaints.

Because the divergence subsystem and the platform are fields, you can filter to see whether a cluster of desyncs all share a physics divergence, or all occur across a particular device boundary, which points immediately at the class of nondeterminism to chase. A wall of vague wrong replay reports collapses into a few concrete patterns, and the one with the highest occurrence count is the one nondeterministic call worth fixing first because it is corrupting the most replays.

Test determinism continuously

Determinism is an invariant that decays silently, so guard it with automation. A nightly job that simulates a recorded match twice and compares state hashes tick by tick will catch a newly introduced nondeterministic call before it ships, long before a player notices a wrong replay. When the hashes diverge, the failing tick points straight at the offending change. This kind of continuous determinism testing is the only practical way to keep replays trustworthy as your simulation grows, because manual replay checking cannot cover the combinatorial space of matches.

The captured field divergences feed the same loop. Each reported desync, pinned to a tick and subsystem, becomes a regression test that re simulates the inputs and asserts the hashes match after your fix. Over time you accumulate a battery of real matches that exercise the determinism corners players actually hit. Replays and spectating then become reliable features rather than sources of embarrassing public desyncs, and players can trust that what they rewatch is what really happened, which is the entire value proposition of having replays at all.

A replay is determinism made visible. Capture the seed, the inputs, and the diverging tick and a wrong replay becomes a bisectable bug.