Prediction is a technique commonly used in multiplayer games to make the player not feel the lag by simulating his actions as they're sent to the server.
For a list of predicted hooks, click here.
So when I shoot at a player, is that prediction?
No that’s lag compensation, it’s used in conjunction with prediction mainly for weapons.
Why do we need prediction?
Without prediction any action the player executes would need to be acknowledged by the server first, and then sent back to the player.
With prediction the server validates the commands as usual , but in the mean time allows the player to freely move around the world.
Now explain that to me in layman's terms
Basically , if you don't want your weapon or movement system to break for players with high ping ( making their view shake and teleport around ) or whenever there's a connection hiccup, you better have prediction support.
How is it implemented?
Every tick, the player generates a CUserCmd, which contains information on what buttons the player pressed and such.
As they're sending it to the server, the client starts simulating the movement before a response is received from the server.
The global variable CurTime() is always in sync with the server depending on the player’s ping, in general you should use this for anything delay based in prediction.
In singleplayer, the client skips the prediction simulation as he’s also the server, so the response time is pretty much nil (however prediction is still applied when hosting a Listen server ).
That means that hooks such as GM:Move and WEAPON:PrimaryAttack are NOT called clientside in singleplayer, because there’s no need to. The only exception to this rule is WEAPON:Think since garry added a hack to make it call clientside even in singleplayer.
How powerful is prediction?
Prediction does not solve everything, it still has heavy limitations which will only be fixed as internet connections will get better and better.
Prediction is here to merely mask them while still trying to provide a smooth experience.
For instance, let's say you have a ping of 300 and you're equipped with a shield weapon that requires you to hold the mouse down to gain invulnerability.
With prediction you're going to see the effects immediately , but on the server your weapon hasn't done anything yet, due to the packets still arriving.
If a player shoots you during that timespan, you're going to die, and the server won't listen to your "but I was shielded already" complaint.
Why are the clientside predicted hooks called more times than the server ones?
Because in order to smooth out the experience the client simulates multiple times at different intervals, interpolating the results. The client doesn’t send all of them though (it mainly depends on the client’s update rate), usually it only sends the first one ( and IsFirstTimePredicted returns true ), the others are for smoothing.
There’s a function called IsFirstTimePredicted, what is it?
IsFirstTimePredicted returns whether the command from this prediction is going to be sent to the server, if it’s false it means that it’s just one used for smoothing out the simulation, as explained just above. This always returns true serverside, as of course the server has no need to smooth it out.
Predicted variables need to be set regardless if the function returns true or false.
The function is already used internally for SendWeaponAnim and EmitSound, so you should only use it to encase effect dispatches like this.
function SWEP:PrimaryAttack() self:EmitSound( "Weapon_Pistol.Single" ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextPrimaryFire( CurTime() + 0.5 ) if ( IsFirstTimePredicted() ) then local effect = EffectData() effect:SetOrigin( self:GetOwner():GetEyeTrace().HitPos ) util.Effect( "Explosion" , effect ) end end
Do NOT use the function like this.
-- THIS IS AN EXAMPLE OF WHAT _NOT_ TO DO function SWEP:PrimaryAttack() if ( not IsFirstTimePredicted() ) then return end self:EmitSound( "Weapon_Pistol.Single" ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextPrimaryFire( CurTime() + 0.5 ) local effect = EffectData() effect:SetOrigin( self:GetOwner():GetEyeTrace().HitPos ) util.Effect( "Explosion" , effect ) end
This only breaks the prediction on the weapon.
This prediction stuff still doesn’t explain why my weapon glitches out when firing or when the user lags!
You're most likely using timers and/or non networked variables for something that should be predicted.
This does NOT happen if you use variables such as self.NextFire, which are most likely cause for your prediction errors.
So, can I still use timer.Simple for my burst fire?
No , you really shouldn't, just do your logic inside of the weapon’s Think hook instead. Timers are not reliable at all during prediction, and they wouldn't be restored when the server tells you what’s wrong during a prediction error.
Also lag compensation does not work in a timer, so there's another reason why you shouldn't do that.
For instance, here's how you'd replace a timer for an idle animation.
function SWEP:SetupDataTables() self:NetworkVar( "Float" , 0 , "NextIdle" ) end function SWEP:Initialize() self:SetNextIdle( 0 ) end function SWEP:PrimaryAttack() self:SetNextPrimaryFire( CurTime() + 1 ) self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) self:SetNextIdle( CurTime() + self:SequenceDuration() ) end function SWEP:Think() if self:GetNextIdle() ~= 0 and self:GetNextIdle() < CurTime() then self:SendWeaponAnim( ACT_VM_IDLE ) self:SetNextIdle( 0 ) end end
That's stupid, it's redundant code, timers are like 1 line of code!
Timers already work like that internally, except in this case they would have prediction support.
Isn't it expensive to have multiple checks like that in a think hook?
No, comparing two floats is nothing compared to firing a trace in a HUDDraw hook.
What is SuppressHostEvents?
This function prevents the player from receiving duplicated effects during prediction.
When you're doing a weapon attack you're going to emit an effect ( for instance the explosion from the example above ) on both client and server.
For other players, these are only going to be sent once, but since the predicting player is also emitting those on his client, the server prevents that from happening by discarding whatever the player is about to send to himself during that attack.
What should be called serverside and what shouldn’t with prediction?
This depends mostly on what you’re doing, if you’re creating an entity, that should obviously be only serverside.
If you’re affecting other entities like doors and other players then that should be serverside as well.
If you’re affecting an entity that has Entity:SetPredictable enabled (which is the normal case for weapons, and entities used in the drive system garry made), such as modifying the internal or custom DT vars then that should be shared.
- cl_showerror (doesn’t require cheats, takes 0,1,2)
Shows prediction errors caused by an entity, setting it to 1 shows the messages on the player’s hud on the top right (where the old Lua errors hud used to be). Setting it to 2 is what you generally want, it enables the printing of the error, the affected variable and the difference from the server.
- cl_predictionlist (requires cheats, takes 0 or 1)
Shows all the entities currently being predicted on the client, entity index, classname and it's origin ( either predicted or client created) It's not that useful alone though.
- cl_pdump (requires cheats, takes an entity index number)
Shows all the dt vars currently being networked to the client, there's a legend on the bottom right.
There will be some prediction examples linked down below at some point. This will be filled when I get some good examples.
For movement related stuff, read the Game Movement page