Debug prediction
A predicted mechanic is right when the client and the room compute the same result from the same input. When they do not, the room corrects the client again and again, and the player sees it. This page lists what that looks like, what the client reports in development, and the fix for each report.
Find the cause from what the player sees
Section titled “Find the cause from what the player sees”| What the player sees | The usual cause |
|---|---|
| The character starts late, or glides on after the key comes up | A system that changes the character’s speed or velocity runs on the room alone, or writes her velocity instead of calling addSpeedModifier or pushCharacter. |
| A bar jumps back, then counts again | A system that is not a predicted system writes an anyEntity predicted trait of hers, or a timer lives outside a predicted trait. |
| An action shows, then undoes itself, every time | The rule reads something the client does not hold, so the client always decides differently. |
| A timer or a recharge runs fast after each correction | A predicted system keeps state in a trait that is not predicted, so each re-run counts it again. |
| An enemy’s health drops, then comes back | The client ran an outcome. Damage belongs behind step.authoritative. |
| An entity appears twice for a moment | A system that runs on both ends spawned it on the client too. |
A single correction after a bump into something, or a burst of them during a lag spike, is expected: the room knew something the client had not heard of yet.
The correction warning
Section titled “The correction warning”A development client counts the room’s corrections of the player’s character. Once the room corrects half or more of its results for five seconds in a row, the console says so once, and names the traits it found differing:
The room corrected this page's character 20 times a second for 5 s: a system on the page writes movement.speed (page 0, room 5) differently from the room. A system that changes what her page predicts, such as her speed, is a predicted system, marked predicted: true, which both ends run and her page re-runs after a correction; one that reads or writes what the room alone holds runs on the room alone.The usual cause is where a system runs. A plugin’s system runs on the room alone unless its entry declares runsOn: RunContext.Both, RunContext.Client or predicted: true, as Where a system runs describes. A system that changes what the client predicts, such as the character’s velocity, must run on both ends and re-run in a correction: make it a predicted system. A system on the client that reads a trait only the room holds throws on its first read in development, naming the system and the trait.
A development world also warns once, naming the system, when a predicted system moves an entity outside a re-run’s set, as one that walks its characters with updateEach rather than step.updateEachPredicted can over anyEntity traits, the only predicted traits updateEach takes:
The predicted system dash.dash moved an entity outside the re-run's set: on a re-run, step the entities step.rerunEntities names and no other, as step.updateEachPredicted hands them.A mover call on the client or in a re-run refuses an entity its step’s pass did not hand out, such as a PredictedEntity a system kept from an earlier step: a development client throws, naming the system, and a built one skips the move and logs it once:
moveAndSlide was handed entity 7, which this step's pass did not hand out (outside-pass): move the PredictedEntity step.updateEachPredicted hands out in the step that moves it, and keep none past it; on a re-run, the entities step.rerunEntities names. The system that moved it: karts.drive. [SP0577]The prediction checks
Section titled “The prediction checks”A development client in a room also watches for seven mistakes that make prediction go wrong without an error. It names each one once in the console, in a line that starts Prediction check: and gives the trait, the fix, and the system where the client saw the write; room-write and outcome infer the mistake from what the room sent and cannot name the system. A built game downloads none of the checks.
A development room, spawnite simulate, a headless test and a single-player game in development run predicted-write too, over every character. There it names the system that writes a character’s predicted trait at the write, where the client could only infer room-write from the correction. A trait of yours declared predicted: true alone never reaches it, since koota’s functions refuse that trait by type; it watches the anyEntity traits, such as the engine’s movement and cooldowns, and a write past the type:
Prediction check: slam.knock changed the predicted velocity of a character, and it is not a predicted system, so in a room her page cannot predict the write and the room corrects her for it. Write it in a system marked predicted: true, put an effect the room starts, such as a slow, in a streamed trait that a predicted system reads, and make a one-off change, such as a refill, with grantPredicted(step, character, trait, value).That check holds the engine’s own systems to the rule it holds a game’s: an engine system on the room that changes a character’s predicted traits, such as a respawn, does it through a door the check leaves unnamed, such as teleport. The other checks run on the client alone.
| Kind | What the client found | The fix |
|---|---|---|
character-system-write |
A predicted system changed a trait of the character that is not predicted, such as a timer in a trait made with koota’s trait(). A re-run counts it twice. |
Declare the trait with defineTrait(name, schema, { predicted: true }). |
predicted-write |
A system that is not a predicted system changed a predicted trait of the character. No re-run repeats the write. A knockback or a teleport the room starts through pushCharacter or teleport, and a grant through grantPredicted, are the writes it leaves unnamed. |
Write it in a predicted: true system. Put an effect the room starts in a streamed trait a predicted system reads, for a knockback or a teleport, call pushCharacter or teleport with the room’s step, and for a one-off change, call grantPredicted, as When the room changes the player’s character shows. |
room-state-write |
A system on the client changed what the room decides of the character, such as its health. | Write it only where step.authoritative is true. |
spawn |
A system that runs on both ends spawned or destroyed an entity on the client. A predicted system would spawn again on each re-run. | Spawn or destroy only where step.authoritative is true. Draw what the client alone shows from a RunContext.Client system. |
outcome |
Another entity’s health on the client differs from the room’s, or the entity is gone from the client while the room still holds it. | Write it only where step.authoritative is true. |
hidden-input |
A game’s predicted system called Math.random, random(world) or Date.now on the client. The room reads another value, and so does each re-run. |
Draw a predicted number with step.random(character), an outcome only where step.authoritative is true, and a cosmetic in a view. Count time from step.deltaSeconds. |
room-write |
The room corrected a predicted trait while the character’s movement matched, with no command of hers before it. A system on the room alone writes the trait. | Write it in a predicted: true system, started by a predicted press where a press starts it. Put an effect the room starts in a streamed trait a predicted system reads, and make a one-off change with grantPredicted. |
No check sees state a predicted system keeps in a module variable or a closure. Keep that state in predicted traits.
The room-write check also fires for an effect the room starts on a character, such as a slow, when the room writes it into a predicted trait. Build such an effect as When the room changes the player’s character describes, and the check stays quiet. A grant through grantPredicted leaves it quiet too: the room’s next send names the traits it granted, and the client counts their correction as the grant.
Read the reports with a tool
Section titled “Read the reports with a tool”An agent reads the same reports without a console:
spawnite play stateprints the corrections on itsRoom:line, the last second’s and, in parentheses, those since the page loaded, then the correction warning, and each prediction check with its count and latest line.spawnite play state --sample 20reads the room’s readings once a second for 20 s while the session runs, or every--everymilliseconds, and prints each one’s mean, least and most: the lead, the round trip, the room’s sends a second and how far behind it draws other players, the bytes down and sent, the room’s step and the corrections, then how many times the room corrected her character over the span, and what her page’s commands came to over it: sent, accepted, refused by each reason, unknown, with no result yet, and predicted ones the room undid.--jsoncarries every sample. Runspawnite play resumefirst: a held session’s readings stand still.- A
--untilcondition reads them asroom.corrections,room.correctionsTotal,room.correctionFields,room.correctionWarningandroom.predictionChecks, so a script can step a game until a check fires, or assert that none did. spawnite play start --latency 150 --jitter 20 --stall 500/10plays the page over a slow link to its room, so a mistake that a local room’s millisecond hides shows in the same reports. TheRoom:line names the link each reading was taken on. The MCP’sstart_playtesttakeslatency,jitterandstall, and the devtools’ Performance panel sets the link while you play. Play it on a slow connection says what each setting does.
The table of tools names the tool for each other check.
How other engines report it
Section titled “How other engines report it”Unreal can log and draw a character’s corrections once correction debugging is turned on, and its Network Prediction Insights plugin shows traced mispredictions. Unity’s Netcode for Entities collects prediction-error statistics for each replicated field and shows them in its profiler. Roblox’s BindToSimulation raises an error for unsynchronized access inside its simulation callback. These checks need no setup in development: each names the mistake and the fix, and the game keeps running.