Search Unity

Resolved Questions about the Prediction Loop on Client and Server

Discussion in 'NetCode for ECS' started by FaithlessOne, May 3, 2023.

  1. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    315
    I have a couple question regarding the prediction loop. The loop is used by the client and server. I am struggling a bit to understand how the loop works. If there are some resources with detailed explanation I appreciate some links.

    I use some terms in the questions so I want to explain the beforehand what I do understand for these terms. If there are more adequate terms please feel free to state them here.

    Terms:
    • Player Loop - This is the execution of all systems which are requiring an uppdate. The player loop is split in three major groups: "Initialization", "Update" and "PreLateUpdate". Systems may still get their "OnUpdate" called multiple times during one distinctively execution of a player loop.
    • Player Loop Execution - One distinctively execution of a Player Loop including all systems.
    • Prediction Loop - As far as I am aware of this are all systems running inside the PredictedSimulationSystemGroup.

    My questions are:

    1a
    While the client runs the Prediction Loop many times for adjusting the component data incrementally based on the server data, is it correct that the server runs the Prediction Loop always only one time during a Player Loop Execution?

    1b
    In case the Player Loop may also run multiple times on the server during a Player Loop execution, what are the reasons for this case?

    2a
    Is the Prediction Loop (respectively PredictedSimulationSystemGroup) on the client guaranteed to be executed exactly once for every NetworkTime.ServerTick having the NetworkTime.IsFirstPredictionTick == true? This would mean that each predicted server tick like 23, 24, 25, 26, etc. gets always exact one execution time where NetworkTime.IsFirstPredictionTick == true. Otherwise the value would be false when the specified tick is processed within the Prediction Loop.

    2b
    How about the server regarding the question 2a. Are the server systems within PredictedSimulationSystemGroup are guaranteed to be executed exactly once for every NetworkTime.ServerTick having the NetworkTime.IsFirstPredictionTick == true?

    2c
    Is the behavior of the NetworkTime.IsFinalPredictionTick the same way as NetworkTime.IsFirstPredictionTick regarding question 2a and 2b?

    3
    Before fully running the Prediction Loop on the client, all values like the fields of LocalTransform are containing non-predicted/interpolated values. After fully running the Prediction Loop for the remaining Player Loop Execution certain data is predicted then. Is there some possibility to get the predicted values (like LocalTransform or other component data) of the last Player Loop Execution _before_ the Prediction Loop runs? For example within the GhostInputSystemGroup.
     
  2. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    894
    Yes it is correct.
    The server run its SimulationGroup at fixed-time-step (based on the ClientServerTickRate.SimulationRate setting, default 60hz).
    The server does not need to neither roll-back the simulation (it is the authority) nor run "partial ticks" (running faster than the simulation rate).

    The client run the simulation at variable frame (unless v-sync is enabled) and execute both the same prediction tick multiple time because:
    1. A new server snapshot is received (and it is always in the past). So it rollback and resimulate
    2. It is rendering at faster rate than the simulation rate. That imply, it may runs the simulation multiple time for the same server tick (i.e 75hz instead of 60hz), with an increasing delta time each time. This is what the NetworkTime.IsPartial is telling you.

    No, the client can always simulate at tick multiple time. The NetworkTime.IsFirstPredictionTick is just telling you that is the the first time (i.e) tick 25 is simulated. When it is re-simulated again, the NetworkTime.IsFirstPredictionTick will be false.
    This can be used for example for predicted spawning or to execute logic only once, or similar scenario.

    The server execute every server tick only once. It never goes back. So yes.

    No, they are completely different. The NetworkTime.IsFinalPredictionTick is telling you that the current simulated tick is the last one for this player loop frame. There will be no further ones after that.
    i.e

    10 <- IsFirstPredictionTick (all these has delta time = 1f/SimulationRate)
    11
    12
    13 <- IsFinalFullPredictionTick (all these has delta time = 1f/SimulationRate)
    14 <- IsFinalPredictionTick (can be either partial or full)

    We have a good bunch of API doc on the NetworkTime component: https://docs.unity3d.com/Packages/com.unity.netcode@1.0/api/Unity.NetCode.NetworkTime.html

    No! Both predicted and interpolated ghosts has their values updated by the GhostUpdateSystem that runs before the PredictedSimulationSystemGroup.
    Interpolated ghost are not simulated inside the Prediction loop either.
    PredictedGhost if they don't have received new data, will contains the data from the last frame,
    The GhostInputSystemGroup runs after the GhostUpdateSystem and such it contains updated values for the interpolated ghosts. And for predicted the correct value of the components
     
    Richay likes this.
  3. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    315
    Thank you for taking the time answering my questions. Helped me understanding the prediction loop. There is still one thing I don't undersand fully yet.

    Especially you stated that the IsFirstPredictionTick can be used for predicted spawning or similar one-time code execution in the prediction loop. I made a test using the following system:
    Code (CSharp):
    1.   [BurstCompile]
    2.   [WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
    3.   [UpdateInGroup(typeof(PredictedFixedStepSimulationSystemGroup))]
    4.   public partial struct AnotherTestSystem : ISystem
    5.   {
    6.     private LoggerHandle _logger;
    7.  
    8.     public void OnCreate(ref SystemState state)
    9.     {
    10.       this._logger = new LoggerConfig()
    11.         .MinimumLevel.Debug()
    12.         .WriteTo.UnityEditorConsole()
    13.         .CreateLogger().Handle;
    14.  
    15.       state.RequireForUpdate<NetworkTime>();
    16.     }
    17.  
    18.     [BurstCompile]
    19.     public void OnUpdate(ref SystemState state)
    20.     {
    21.       var networkTime = SystemAPI.GetSingleton<NetworkTime>();
    22.  
    23.       if (!networkTime.ServerTick.IsValid)
    24.       {
    25.         Log.To(this._logger).Info("CLIENT: Prediction tick skipped", networkTime.ServerTick.TickIndexForValidTick);
    26.         return;
    27.       }
    28.    
    29.       if (networkTime.IsFirstPredictionTick)
    30.       {
    31.         Log.To(this._logger).Info("CLIENT: First prediction tick on {TickValue}", networkTime.ServerTick.TickIndexForValidTick);
    32.       }
    33.       if (networkTime.IsFinalPredictionTick)
    34.       {
    35.         Log.To(this._logger).Info("CLIENT: Final prediction tick on {TickValue}", networkTime.ServerTick.TickIndexForValidTick);
    36.       }
    37.       if (networkTime.IsFinalFullPredictionTick)
    38.       {
    39.         Log.To(this._logger).Info("CLIENT: Final full prediction tick on {TickValue}", networkTime.ServerTick.TickIndexForValidTick);
    40.       }
    41.       if (!networkTime.IsFirstPredictionTick && !networkTime.IsFinalPredictionTick && !networkTime.IsFinalFullPredictionTick)
    42.       {
    43.         Log.To(this._logger).Info("CLIENT: Casual prediction tick on {TickValue}", networkTime.ServerTick.TickIndexForValidTick);
    44.       }
    45.  
    46.     }
    47.   }
    48. }

    This is an excerpt from the logs of this system printed in the Unity Editor console:
    I don't know if it is of any importance here, but I used the Network Simulator for the Client with an RTT Delay of 500 ms. Also the system was burst compiled for the test.

    You can see that certain predicted server ticks like 242, 244 and 246 are not getting the "First prediction tick" log message. But when something important happens in these ticks which requires a one-time logic to be executed within the prediction loop, how this can be achieved then when these ticks don't get the IsFirstPredictionTick == true?
     
  4. CMarastoni

    CMarastoni

    Unity Technologies

    Joined:
    Mar 18, 2020
    Posts:
    894
    Hey,
    sorry! I misspelled: I was referring to the NetworkTime.IsFirstTimeFullyPredictingTick in that quote, not the NetworkTime.IsFirstPredictingTick.

    The NetworkTime.IsFirstPredictingTick just telling you that the prediction is starting from that tick.


    With 500 ms latency, I presume the server is not running at 60hz and so is simulating more than 1 tick per frame.
    So it is plenty possible you never receive tick 242 from the server.

    In this log, looks like that:

    you received 240,

    241 <- First Predicted Tick
    242
    ...
    received 242,
    243 <- First Predicted Tick
    244
    ..
    received 244,
    245 <- First Predicted Tick..
    ..
    received 246,

    I suggest to put just 80-100ms latency (is more than enough to see this, the client is ahead of 7 ticks anyway) so the editor frame rate is not going below 60hz
     
    FaithlessOne likes this.
  5. Rukhanka

    Rukhanka

    Joined:
    Dec 14, 2022
    Posts:
    204
    Am I correctly understand that PredictedSimulationSystemGroup systems will never be executed for InterpolatedGhosts?
    And another question related Simulate tag. Does this tag is set and enabled for interpolated ghosts? The only case when it is not set is when predicted ghost does not need to be predicted on client?