S&box Wiki

Revision Difference

Prediction#549154

<cat>Code.Network</cat>⤶ <title>Prediction</title>⤶ # What is Prediction?⤶ ⤶ Imagine you shoot a gun in the game. Without prediction you would press the mouse button, it'd send a message to the server, the server would run the command, the gun would shoot on the server, it'd send sounds and effects down to the client, then you'd see the effects happen. We're talking about a 30ms round trip at best, half a second at worst. It would be noticeable. ⤶ With prediction it does the same thing, but while it's waiting for the server it predicts what's going to happen. It runs the command clientside, so everything appears instant. ⤶ ⤶ # What Should be Predicted? ⤶ Generally speaking anything that involves your pawn's movements or actions should be predicted. For example, movement code should match on both client and server. In your weapons, you should make things like ammo count predictable. It also makes sense to predict animations. ⤶ You can only reliably predict things that are deterministic (i.e. things that can be consistently calculated with the same end result on both client and server). ⤶ Anything involving the physics engine typically isn't deterministic, so you can't use physics in a reliably predictable fashion. Physics in this context doesn't include deterministic things like raycasts; a raycast straight down from 100 units will always hit the world with the same result whether you're on the client or the server. However, if you're raycasting from or into a physically simulated body, that's when you may run into difficulties. ⤶ # What is Prediction⤶ ⤶ Prediction is a technique used in online games that allows for smoother gameplay by predicting what will happen next instead of waiting for the server to respond. The process involves running input logic locally while waiting for the server's response to the input. The client compares the server's response with its predictions, and if there is an error, it re-runs the commands to fix the problem.⤶ # Why use Prediction⤶ ⤶ Without prediction, there is a delay between the player’s input and the game's response. For instance, in a first-person shooter game, when the player shoots a weapon, without prediction, there would be a delay between the player pressing the button, the server processing the command, and the player seeing the results. This delay can range anywhere from 30 milliseconds to half a second, depending on network latency. This delay would be very obvious to the player and would make the game seem unresponsive or laggy as a result. ⤶ # What Should be Predicted⤶ ⤶ Anything that involves direct input of the player interacting with networked entities should be predicted. For example, movement code should match on both client and server. In weapons, variables such as ammo count and animations can be predicted. Also see: [Input](https://wiki.facepunch.com/sbox/Input_System) Dynamic physics cannot be predicted as the physics engine is inherently non-deterministic. ⤶ Only deterministic things, i.e. things that can be consistently calculated with the same end result on both client and server, can be predicted. Deterministic things like [Traces](https://wiki.facepunch.com/sbox/Traces), which will always produce the same results, can be used for prediction. # Prediction Errors ⤶ When the server runs the tick and sends the results to the client, the client compares these results to what it had predicted for that tick. If any of the values are different, then a "prediction error" occurs. In that case, the client copies all the variables from the server and re-runs any subsequent ticks to try and fix it all.⤶ The prediction error that appears in scary red text in your console isn't an error in the typical sense - it doesn't mean that anything "broke". It's merely there to tell developers that they probably didn't design their code properly. Save for extreme cases, at worst, prediction errors will just result in jittery or inconsistent gameplay. ⤶ ⤶ Prediction has a certain degree of [tolerance](https://developer.valvesoftware.com/wiki/Prediction), and for certain data types like floats, the value that the server calculates doesn't have to match the value the client calculates exactly. In these cases, a prediction error is not shown. However, the client probably still takes the values from the server, just to be safe.⤶ ⤶ It is unusual to get prediction errors when testing in the editor. This is because there is no (or very very little) network latency in the editor, so in theory prediction errors should not be happening. If you are getting prediction errors in the editor, then your code is very likely not handling prediction properly.⤶ ⤶ Outside of the editor, some prediction errors are to be expected due to things like lag spikes, dropped packets, and other stuff that you cannot control. The difference between prediction errors caused by bad code and prediction errors caused by external factors is that prediction errors caused by bad code will be reproducible in a consistent fashion (e.g. only when a certain button is pressed, or only when crouching). Prediction errors caused by external factors may still occur more commonly in certain scenarios, but they will not be consistently reproducible. Prediction errors caused by bad code may also occur more often. ⤶ ⤶ A prediction error occurs when the client's prediction of an action differs from the server's actual response. This can result in jittery or inconsistent gameplay. Prediction errors can be caused by poor coding, lag spikes, dropped packets, and other factors. Prediction errors aren't inherently "bad". It just indicates that there was a difference between server and client, so the server corrected the value. **The server always has authority over networked values at the end of the day.**⤶ ## Fixing Prediction Errors ⤶ A prediction error just means that you calculated some value differently on the client than you did on the server. To fix it, you have to do the following:⤶ 1) Find what variable was mis-predicted. The prediction error should tell you this. 2) Find all the places in your code where that variable is written to. You can do this easily in most code editors by right clicking on the variable and clicking "Find All References", or whatever your editor's equivalent is. 3) In each place the variable is being written to, ask yourself: _"Is this code being run on both the client and the server? Is it being calculated in the same way, with the same values?"_. What happens if you comment out that code? Add logging to confirm it's using the values you think it's using. # How do you use Prediction?⤶ The main entry point for prediction is the `Simulate` function in your game class. ⤶ This is the default behaviour if you don't override it:⤶ ⤶ To fix a prediction error, you should:⤶ 1. Identify the property that was mis-predicted. The prediction error should reference the property name. 2. Find all the code segments that alter the property, and ensure that they run consistently on both the client and server. 3. Check whether all the logic is predictable and running on both the client and server in a predictable context. # Prediction in Practise⤶ The main entry point for prediction is the `Simulate( IClient cl )` function in your GameManager class. ⤶ By default, the client's pawn will have its Simulate function called, which is where the player's actions can be predicted. ⤶ ``` /// <summary>⤶ /// Called each tick.⤶ /// Serverside: Called for each client every tick⤶ /// Clientside: Called for each tick for local client. Can be called multiple times per tick.⤶ /// </summary>⤶ public override void Simulate( IClient cl ) { cl.Pawn?.Simulate( cl ); } ``` ⤶ As you can see the default behaviour is to call `Simulate` on the client's pawn. This is the function that you should predict the player's actions in. The idea is that `Simulate` is called on both client and server, and both run (roughly) the same code. Because they run the same code, the variables (e.g. player position) should match between client and server. ⤶ If an entity has your pawn as the owner it becomes predictable too. So, for example, you might call `Weapon.Simulate( cl )` in your pawn's `Simulate` method to allow the weapons to fire in a predicted way.⤶ ⤶ By default, things like player position and rotation are predictable variables. You can also make your own predictable variables by using the `[Predicted]` attribute:⤶ ⤶ Any entity that has the predicted client as its Owner can be Simulated this way. So, for example, you might Simulate an active weapon in your pawn's Simulate method to allow the weapons to fire in a predicted way. ⤶ Predictable properties can be created by using the [Predicted] attribute:⤶ ``` [Net, Predicted] public float Stamina { get; set; } = 100; ``` ⤶ ⤶ You could then predict it like this in your player class:⤶ ```⤶ ⤶ Predicted properties can then be Predicted in any valid predictable context:⤶ ``` public override void Simulate( IClient client ) { if ( Input.Down( InputButton.Run ) ) Stamina -= Time.Delta * 10f; } ``` # Turning off Prediction⤶ Imagine again if you shoot a gun. Normally, this would play a sound. The sound would play:⤶ ⤶ - On the client shooting the gun (in this case, you).⤶ - On the server (heard by everyone else).⤶ ⤶ The server assumes that the client has already heard the sound, so doesn't send it again.⤶ ⤶ This same concept applies to RPCs. Sometimes, however, you might want to force the client to hear a sound, for example, if you shot something and someone died; in these cases, you can turn prediction off like so:⤶ # Specific Behavior⤶ In the case of effects like sounds and particles being predicted, the client will simulate them locally, while the server will tell clients to play things such as a shot sound via RPCs. To prevent the local client from getting sound RPCs for sounds they've already heard, the RPCs get culled and won't be played again as a result.⤶ ⤶ ## Disabling Prediction⤶ ⤶ Sometimes, however, you might want to force the client to hear a sound, for example, if you shot something and someone died; in these cases, you can turn prediction off like so:⤶ ``` using ( Prediction.Off() ) { // Play sound here } ``````⤶ ⤶ <validate>RPC culling should work fine if it's predicted right. Prediction.Off should only really be for things that cannot be predicted in the first place.</validate>⤶ ⤶ ## First Time Predicted⤶ ⤶ As mentioned at the beginning. The client may sometimes re-run predicted ticks to try and correct predicted properties. In this instance, client-side effects might be run multiple times. To prevent this behavior, you can use `Prediction.FirstTime` to check if the code is currently re-running prediction or not.⤶ ⤶ ```⤶ if ( Prediction.FirstTime )⤶ {⤶ // Play client-side effects⤶ }⤶ ```⤶