How to predict two associated Ghosts

Consider the following scenario:
There are two Ghosts, A and B, in the scene, both predicted ghosts.
When the prediction reaches Tick 10, a Snapshot of A at Tick 5 is received.So A rolls back to Tick 5 to start predicting again.
At this point, since B has already predicted up to Tick 10, B will skip predicting Ticks 5-10.
However, if A’s logic references B’s data, then during A’s rollback and re-prediction process, it will continue to reference the data of B at Tick 10. This will result in a deviation from the results of simultaneous prediction of A and B from Ticks 5-10.

How should I deal with this situation, or is there a problem with my understanding?

Thank you for any help.

From a new page that we are adding to the manual:

Interactions between predicted ghosts using partial snapshots
Timeline on a client (all this happens in a single frame):

  • A, B, C, and D have all predicted tick 20 already

  • Client receives a partial snapshot for A and B’s tick 10

  • Client resets A and B back to snapshot 10’s value, but leaves C and D at their tick 20 value

  • Client replays tick 11, 12, 13, 14, …, 20 only for A and B

  • C and D are still frozen at tick 20

  • Client simulates tick 21 for all of A, B, C, and D

This means if your ghost B (rolled back) interacts with your ghost C (not rolled back), then the interaction might be incorrect, since both won’t simulate at the same rate, with the same amount of steps. This is especially true when you’re changing a ghost’s state and expect the ghost to update itself using that state. For example, if B collides with C and changes C’s velocity, C will still only execute one tick using that velocity, while A and B will interact with a ‘frozen’ C when replaying from the snapshot to current tick. Your simulation might expect C to move with that new velocity, but that won’t be the case.

Possible mitigations

  • Use client anticipation instead of prediction.

  • Instead of an action starting instantly, the client waits for the server’s confirmation before performing the action (and plays some animation/sound to hide the lag). For example, if a player is the ghost A in the example above and the ball to pickup is the ghost C, then predicting the ball pickup could take an unexpected amount of time to correct if the ball’s state arrives only later.

  • This is just an example. In real life, having the player nearby, if correctly prioritized, is most likely going to receive the ball very soon, if not already in the current partial snapshot.

  • Change other ghosts’ states instead of waiting for them to change themselves.

  • For example, ghost A picks up ghost C. Instead of having ghost C update its own position according to ghost A, have ghost A update ghost C’s position.

  • With this, ghosts that are currently simulated (marked with the Simulate tag) will see the expected result on ghosts they affect, even if those ghosts themselves are not being simulated.

  • Use GhostGroup.

  • Since GhostGroup guarantees ghosts in that group will be sent together, this removes the partial snapshot issue. This is a more complex solution that requires careful grouping, however.

  • Prioritizing the chunk.

  • Prioritize the chunk such that ghosts near the ball get higher priority, and the ball has always the highest priority (sent every tick). Refer to Importance Scaling for more details.

  • Only allow interactions between ghosts that are being simulated.

  • Use the Simulate tag in your entity queries to filter for entities to interact with.

  • With this, A and B could only interact together and ignore C and D. This mitigation still produces mispredictions. It depends on your gameplay to see if corrections are more visible if you interact with a frozen ghost or skip it entirely.

In summary, this is a situation where mispredictions are unavoidable. Your goal is to hide corrections as much as possible so your player’s experience is not too affected, while making sure you eventually converge to a correct state.

4 Likes

Thank you very much for your prompt reply.

I’m glad my understanding of the phenomenon isn’t too off.
I think I will try using GhostGroup and prioritizing Chunk, which should solve my problem.

I asked a similar question here , will you adress this in the future? Allowing users to opt in on full history buffer (not only last full tick) & synced (all predicted ghost repredict all ticks togeather) reprediction to oldest snapshot should really be a feature if not adressed in some other way.

1 Like

Bumping this question. We’ve been doing this for a while now and solved a lot of our mispredictions cases. Think it would be great to be implemented as an opt in!

1 Like

Sad to report no update as of today, except that it is still on our roadmap.

1 Like