Sign in to follow this  
simonlourson

Client / Server Lag compensation

Recommended Posts

Hello all. I have designed and developped a small number of singleplayer games using C# and XNA, and I now want to take my hobby to the "next level", multiplayer. After a "naïve" implementation of a client server small game, (the server sends all objects positions to all clients each frame to the clients, and the clients send their input each frame to the server), I realized this was NOT a clever way to go. The game played fine over a lan, but was downright unplayable over the internet. And by unplayable, I mean each player controlled object would seem to move every other second on the clients. So I have done a bit of research, and I have come across some good articles about lag compensation, both client and server wise. The following is what I have gathered from those articles, and how I plan on implementing my game so it can be playable over the internet. I am posting it here because it is my first attempt, and I hope to receive constructive criticisism from you. I will be testing these concepts on a VERY simple 2D multiplayer game, 2 square boxes controlled by players using wasd... Each press on a key adds velocity to the boxes. Boxes can bounce off walls, themselves, and level design. == Architecture == Client / Server: Multiple clients will connect to a single server. The clients will send only their input to the server, and the server will send back usefull info to the clients, so they can draw the scene to the player. The server will update its game state 30 times per second. It will send the world info to the client 10 times per second. The server will not send ALL the world info each time. Static objects, such as the world layout, will only be sent once, reliably, unless they change. (delta compression). The server will send the following info about each player controlled object: * current position * current velocity == Lag compesation == Let's assume there is 200ms of roundabout lag between the server and the client. ie, the server receives the client's input 100ms after they are sent by the clients, and the clients receive the feedback of their actions 200ms after they pressed the button. Obviously, this is not acceptable. Because of this, we have to compensate for lag, both client wise and server wise. === Client side prediction === To compensate for lag client wise, the client must be able to do more than just read the object's position. The client receives from the server the position and velocity of the objects it must draw. Now, the problem is, when the client receives this info, it is already outdated, because it was what was happening on the server 100 ms ago. Because the client knows the value of its roundabout ping to the server (200ms), it knows approximately when the values were sended. (100ms ago). Now the client also knows how many updates per seconds the server does (30), so the client can compute an approximative position for the objects, by adding the velocity to the position 3 times. (because in the 100ms the data takes to get from the server to the client, the server will have run 3 more updates. To be able to do this, the client must be able to reproduce the code executed by the server. To achieve this I plan on passing a parameter to all my update methods, and in this parameter I would put a flag, either "SERVER", or "CLIENT". If the parameter is "SERVER", the method would run the full simulation, ie adding velocity, checking collisions, firing weapons, etc. If the flag is "CLIENT", the method would only add velocity. Of course this is only an approximation, so when the client receives new values for the objects positions, it should replace it's computed values by the "real" ones, send by the server. The quickest way to do this is to simply replace the values. But if I do that, the objects will seems to "snap" around. So I will be interpolating the "real" values with my computed ones. There is a great article on this site about cubic splines, so that is what I'll be using. With this method, we are able to calculate to position of all moving objects. But for the client controlling his own player object, we can do much better, because the client knows what keys are pressed in real time! So instead of blindly taking the position and velocity sended by the server, we can add directly the user input to them. That's it for client side lag compensation. But it doesn't change the fact that our input still takes 100ms to get to the server... That's where server side lag compensation comes in. === Server side lag compensation === As I said, the input from the clients arrives to the server 100ms after it was sent by the client. To do things right, the server should apply the actions send by the client to the world state as it was 100ms ago, in server time. The way to do this is simple: we store the world state in an array a. a[0] is the present world state a[1] is the world state one update ago. (33ms ago, if our server is 30htz) a[2] is the world state two updates ago. (66ms ago, if our server is 30htz) .... And so on. The more updates we store, the more "far back" we are able to apply the changes, but the more CPU and memory it takes. An example: the server knows its ping to each of the clients. When it receives a command, the server knows hows long ago the command was sent. In our case, when the command arrives 100ms after it was sent, the server would apply the command to a[3], then a[2], then a[1] to obtain the current position of the object -> a[0]. *** That's all I got. If I apply all these concepts to my game, I'm farly confident it'll run smoothly at accepteble pings. It would be awesome if you guys could correct anything wrong I've said because I haven't yet implemented those.

Share this post


Link to post
Share on other sites
Thanks, I found this helpful since I too am in the process of developing a multiplayer game, although in my case it is Poker so the lag issue is not as much of a concern.

I have only one minor suggestion, if you are coding this in an OO style instead of procedural, I would not use a SERVER and CLIENT flag, since this would require you to put
if (flag == SERVER) 
...
else if (flag == CLIENT)
...
all over your code. It might be better to use polymorphism or some technique to make the code "cleaner".

Share this post


Link to post
Share on other sites
Quote:
Original post by simonlourson...
=== Server side lag compensation ===...To do things right, the server should apply the actions send by the client to the world state as it was 100ms ago, in server time...An example: the server knows its ping to each of the clients. When it receives a command, the server knows hows long ago the command was sent...


#1
If you do it this way, you also have to send time hints with each message from the server, because client A don't know the ping of client B. This will bloat your message protocol.

Example:
Client A has ping of 100 ms
Client B has ping of 600 ms

Client A receives movement message from server, that client B started moving. What should client A predict, if he only knows its own ping (100/2 + ? = 50 + ? ms back in time)? Client A have to know the ping of B too to predict the right time when B started moving (600/2 + 100/2 = 350ms back in time).



#2
Attention! This way you will open up your server to something like "lag hack" if you make your game public and can't trust every user.

Example:
Client A has Ping of 100 ms to Server.
Client B has Ping of 2000 ms to Server but in reality 100 ms. (How that? Because client B got ping requests from server but stalled the direct answers "just a little bit" so the server thinks the ping is that high)

Now client A shoots at client B and hits. Client B receives that hit from server and immediately sends a message to move away from impact location. Server thinks client B has ping of 2000 but got move request 300 ms after client A shot at B. Server updates client B position 1000 ms back in time, so 700 ms before client A shoots. Client B is save because of that "lag hack".


#3
At least to give you an advice:
Don't use server side prediction at all. Just drop it. This will ease your development process and debugging a lot. And in most cases (clients with fast to normal ping) users won't recognize a difference to a server with server side lag compensation.
Just rely on the following: an event happens when the server receives it (and not back in time). This way you never need to store outdated world states at the server. The client predicts the world state in between its own ping!

[Edited by - Anntor on April 28, 2010 12:22:59 PM]

Share this post


Link to post
Share on other sites
Thank you for the advice. I think I'll start with client side prediction, as it's easier.to implement. If I'm not satisfied, I'll toy around with server side though.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this