Yesterday I blogged about some great multiplayer programming resources that I had found and indicated that I knew my next step was to implement a technique called server reconciliation.
I did some reading last night and think I get it. But first I should talk a little bit about another technique called client prediction.
Client prediction is a technique that helps to deal with the fact that it will take some amount of time for each game client to call the server, have the server process the incoming commands and send the results back to the client. The problem is that even under the best network circumstances there is going to be enough delay in the round trip to effectively make the game unplayable.
So, to deal with this time delay and make sure the player’s commands show up instantly in their browser, you implement the same game engine on both the client browser and the web server. Then, the player’s commands are run instantly on their local browser AND sent off to the server for processing.
This results in the players actions happening immediately in their browser and not a fraction of a second later. Which is good because even a fraction of a second later makes for a very poor experience in an arcade style game. So this solves one problem but leads to another problem…
The problem with client prediction is that even though the player’s actions happen in their browser immediately, it takes some time to get the authoritative results back form the server. And when those results do come back they over write the current state on the client. Which is a good thing because only the server knows what the other players are doing so the servers state of the game is the only accurate one. (it is also necessary to thwart hacking on the client side)
But here is where the problem comes in… while the command is being sent to the server and the client is waiting for a response to come back the player is not just waiting around. They are issuing more commands. So, when the original command comes back and the new update from the server over writes the local state, all the commands that the player has performed since that first command cease to exist. It’s like they never happened.
The end result is that the player’s character, or ship, or whatever they are controlling will become very jerky as it moves across the screen. In the case of Space Frigates, this is very apparent.
This is where server reconciliation comes to the rescue. In essence, the client needs to re-run all the commands that get over written by incoming updates from the server. To the best of my understanding, here are the basic steps to accomplish this:
- On the client, add a sequence number to each command.
- On the client, each command being sent to the server should be recorded locally for future reference.
- On the server, each update being sent back to the clients should contain the sequence number of the most recently processed command for each client.
- On the client, when the server updates are being used to over write the current state, the client looks at the last command that the server processed and re-runs all saved commands that the server has not seen yet.
- On the client, delete all commands the server has already processed.
That is my understanding of the basic approach. I’ll be working on the implementation this week and keep you posted!