Some questions about Prediction and Interpolation

Hi everyone,

I have read Netcode docs about Prediction and Interpolation but I still don’t quite understand how they work in Unity. So I’m reaching out to ask for clarification.

  1. How can I detect when a rollback occurs and to which tick does Netcode rollback? Can I use NetworkTime.IsFirstPredictionTick for this?
  2. What are the reasons for a rollback to occur? It’s due to misprediction or are there other reasons?
    Why NetworkTime.IsFirstPredictionTick = true even though there isn’t any input? (I don’t press any key)
  3. How does Remote Player Prediction work? How can Local Player predict Remote Players when it doesn’t own the latest input of Remote Players?
  4. How is the interpolation delay calculated? Are NetworkSnapshotAck.RemoteInterpolationDelay and CommandDataInterpolationDelay.Delay the same?

Hey Carlos!

Prediction loop in its essence is quite simple. When the client receive new data from server, the state of ghost that has been received is updated to the new snapshot. The client calculate the oldest tick at which the simulation should start re-simulating and the PredictedSimukationSysteGroup execute multiple ticks in the same frame, up to the next/current simulated Server Tick.

This logic does not depend upon the fact there is a mis-prediction or not, input pressed or not. It is always happening as soon as data are received. So in the ideal scenario, pretty much every frame (because server send data at the same rate as the simulation rate).

On top of that, client also have to deal with the fact the simulation is not running at fixed time step, and he can run the prediction loop also for “partial ticks”, frame for which the elapsed time is less than the simulation frame rate.
For example, the client running at 90hz but the game logic running at 60hz.
For partial ticks, the prediction loop always restart the simulation from the “last full prediction tick”, for which the state of the ghost has been backup. Netcode internally backup and restore the state of the predicted ghost for the last full simulation tick

  1. You can check the NetworkTime.IsFirstPredictionTick to discern if this is the the tick the prediction is starting from. If it is a “rollback”, the IsFinalPredictionTick should also be false.
  2. The reason for a rollback occurs are two:
    a) When a new snapshot is received from the server and contains data from predicted ghost
    b) When the client need to continue simulating a partial tick (it restarts from the last full simulated tick backup)
  3. The Remote Player Prediction work by replicating the received input data on the server to all players. With that information, the other clients do two things:
    a) for the tick for which the player input apply, they will use the input data.
    b) for the ticks he does not have the input, the last input in the buffer is used. So the prediction logic (default one) is that the remote player use the last input
  4. The interpolation delay calculation is a little complex, but in general it is calculated on the client and it is the delta in between the current value of the Interpolated Tick and the predicted ServerTick. This is passed by the client to the server as part of the command stream. The NetworkSnapshotAck.RemoteInterpolationDelay and CommandDataInterpolationDelay.Delay are the same value. The CommandDataInterpolationDelay.Delay is a convenient for iterating over the entities.

Hi Marastoni,
Thank you for the explanation! :slight_smile:

Now I understand a bit more about Prediction and Interpolation. But there are still a few parts that I’m not quite clear about.

  1. How does the client calculate “the oldest tick at which the simulation should start re-simulating”?

I’ve read the prediction docs and it said “Once Netcode applies the snapshots, the PredictedSimulationSystemGroup runs from the oldest tick applied to any entity, to the tick the prediction is targeting”
What does “the oldest tick applied to any entity” mean?
Is it the earliest tick for which the client has received and applied a snapshot from the server into its local simulation?
So it’s when the client received the first snapshot from the server?
And where can I get “the oldest tick applied to any entity” from code?

  1. Why does Netcode roll back when the client needs to continue simulating a partial tick?

We should share a doc about all this in the current documentation, because the story is quite long to write :smile:

  1. The oldest tick from which the prediction start is usually the tick of the latest snapshot received from the server. So for example if the client is simulating currently tick 10 and received data for tick 5, it will re-simulate tick 6,7,8,9,10. It is important to understand that ONLY THE GHOSTS FOR WHICH NEW DATA HAS BEEN RECEIVED ARE RESIMULATED (the Simulate tag is used for that), that can happen if the snapshot can’t contains all the predicted ghosts data (so the predicted ghost server updates are time-sliced). There are certain condition (rare) where it may be necessary to restart the simulation of a predicted ghost using an older tick but they are edge conditions.
    Also, the prediction start tick is clamped to be always maximum 32-64 ticks behind (that is the maximum number of commands the client can have in its own queue before overlap).
    The GhostUpdateSystem collect all the latest updates received and add the prediction ranges into a queue that is then used by the PredictedSimulatuonSystemGroup rate manager to calculate the prediction start tick.

  2. Because the way the server simulation work and how input are applied on the server. The server only simulate at fixed time step, using the input the client send/apply for that tick.
    This is the same problem you have/face when using FixedUpdate and Update, but with some little networking nuances.
    On the client, the frame time is not fixed, and the prediction for the same simulated tick may run multiple time, with a fraction of the required delta time. The input is sampled every frame (at variable rate), and it may have changed (or it may be accumulated) in respect the last time you predicted the player movement. The client, in order to simulate the game as the server does, need to apply the current input from the BEGINNING OF THE LAST CALCULATED FRAME, up to the current time.

Example (simplified)

Client:
(Tick 10 ← state backup, the last full simulated state.)
Tick 10.1
sample input,
partial simulate from tick 10 state
Tick 10.3
sample input
partial simulate from tick 10 state
Tick 10.7
sample input
partial simulate from tick 10 state

Tick 11.2 ← CHANGE IN TICK DETECTED
Sample input
Send input to the server
FullTick 10 - > 11
Backup state 11
Partial simulate 11.2

Server:
Tick 9 …
Tick 10
get input for tick 10 for all clients or apply the last received input
FullTick 10 - > 11

Hi Marastoni,
Thank you for taking the time to answer my questions. :slight_smile:

As I understand, there are two cases where the rollback happens as follows:
a) When a new snapshot is received from the server and contains data from predicted ghost.
- In this case, Netcode will re-simulate from the tick of the received snapshot to the current tick, right?
b) When the client needs to continue simulating a partial tick.
- Does the client re-simulate from the last full prediction tick (Is it the tick with IsFinalFullPredictionTick = true) to the current tick?

  • How does Netcode handle the input when the prediction runs multiple times for the same simulated tick? As you said, the input may have changed or been accumulated. How does Netcode handle such situations? For example, When the client predicts tick 10.7, it needs to apply the input from tick 10.1 to tick 10.7, right?
  • Is there a way to detect whether the cause of the rollback is due to receiving a new snapshot or continuing to simulate a partial tick from code?

Sorry if my questions are simple, but I’m new to Unity Netcode and I’m still confused and don’t quite understand how it works under the hood. :slight_smile:

Can someone help me?

a) Yes, it will do
b) it is not always the tick with the IsFinalFullPredictionTick, but it is most correctly the last IsFirstTimeFullyPredictingTick simulated. Usually they tend to coincide.
c) The client will use the current sampled input (in this frame). For simulating 10.7, the client will use the prediction backup to restore the predicted ghost state to tick 10 and then simulate up to tick 10.7 (passing delta time of 0.7/SimulationTickRate). The last simulation result for the predicted is completely overwritten with the last input data. How you accumulate the input for partial ticks is up to you. Event set in a partial tick, but not in another, are still detected as pressed, so they are not lost.
d)We don’t expose it in an easy way. Is there a specific reason you want to know that?

Thank you for your answer :smile:

When I put my system into PredictedSimulationSystemGroup. Sometimes my code runs multiple times per frame, sometimes it runs once per frame. It’s very frustrating not knowing why my code is running like that. I think you understand how I feel.
I’m just curious about how it works under the hood and I want to understand the framework as much as possible. I hope Unity will add more docs about these things in the future

I think I gave you the information about how it work that way.
only once: is a continuation of the last prediction and not message received from the server
multiple time: new message received.

I think, documentation apart, providing at this point an information about if you are re-simulating because of a new snapshot or continuing the last prediction would probably clarify any doubt.