How does Client Side Prediction work for shooting bullets?

Excuse the crude drawing but I need it to get my point across.

The drawing has 2 players (Capsules). Arrows representing the player movement direction. The bottom player is me on my computer with a gun on tick 1 shooting towards the enemy I perceive in my game. There are then 2 dotted versions of myself. 50ms ago the server perceived me in that location not shooting a bullet and another 50ms ago it sent that info and i am perceived at a different position on the enemy’s screen.

The enemy also has 2 dotted versions, the far right being his current location on his screen, a server version 50ms later and a version that I see/shooting at that is another 50ms behind the server.

Now moving to tick 2: Now The server has seen me shoot a bullet at the enemy’s location. But i am not shooting at where the server sees the enemy. The enemy is now 100ms away running to the right.

How can I match the server I am sending to to the enemy’s position on that server?

If I say I shot a tick 1 I will miss because the server sees the enemy 50ms after I shoot so the bullet is not heading towards them?

I hope this makes sense and maybe someone can explain it so I can understand how I can rewind the server state to check if my shot would register a hit on the enemy.

The technical term for the solution is “(server-side) reconciliation”.

The idea is that the server simulates game events in the past. Ie the client A fired at client B but that happened 20 ticks in the past, so the server needs to be able to go back in time, put every dynamic game object at their past states (position, orientation, powerups, anything) and then let client A fire the shot - this determines whether it was a hit or not and that gets sent back to the clients.

Given enough RTT (lag) from any client’s perspective the shot may look like a sure hit or miss but the exact opposite may happen because each client views the game state at a different point in time. There’s little one can do about this but it’s not normally noticable with RTTs below 100 ms.

Here’s an article on reconciliation:
https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html

And one of my favorite talks explaining how they implemented this in Rocket League with custom physics. You also get the really good sketches that drive away home the problem statement and the solution.

https://www.youtube.com/watch?v=ueEmiDM94IE

Since they run a physics simulation it’s not enough to go back in time for a single frame … instead any time the server goes back in time the game state might change, ie the ball might roll in a different direction, so the server also has to run the simulation up to current time while considering every client’s input.

In essence, the server runs multiple simulation frames in a single tick. And that happens at 120 Hz. You can imagine this puts a heavy load on the server even for a 3 vs 3 game.

The technique of matching what the client saw (predicted frame) on the server on the time the input arrives is called lag compensation. It is used in most modern FPS games such as CSGO. The article from Valve is pretty good: Lag Compensation - Valve Developer Community / Server In-game Protocol Design and Optimization - Valve Developer Community

1 Like

We built our own lag comp for our game. It was actually a fun feature to make. Also created a debugger for it.