Networked games: Playing in the past or future

about | archive


[ 2020-July-14 10:46 ]

A few years ago I was fascinated by how network games worked after reading a series of articles about Networked Physics that includes some great demo movies. I didn't really understand it. How can the game be playable, when the client and server are separated by approximately 100 ms of network delay? I decided to create a small demo of a networked game, to try and figure it out. However, like most projects I start, I abandoned it after spending a few hours on it. I rediscovered it a couple months ago, and got it to a reasonabl y "finished" state. The demo shows the client and server state of a "game" and lets you adjust the simulated latency. This gave me a huge appreciation for clever tricks used to create fast-paced games that are playable over the Internet. In particular, I find it amazing that the clients are playing the game either in the past or the future, but it still works to create an interactive experience.

Local latency

One important lesson is that even a local game has latency. Let's ignore things like keyboard latency or display latency, each of which can easily add ~50 ms of delay. Let's just consider the game loop, which involves reading input, simulating the world, then drawing the world. This means after a button press arrives, the game will not respond until the next frame. Many displays can show a maximum of 60 frames per second (FPS), which translate to an average of 8 ms of delay (uniformly distributed between 0-16 ms). That initial frame also might just register "player is moving forward", but won't actually move the player forward until the next frame, so that means it can be up to 33 ms between when you pressed the button and see a change. Many games run slower than that, which scales up the delay. The conclusion is that even that "fast" reaction you get in a local game is not instantaneous, and may actually have approximately 100 ms delay between inputs and seeing an update.

No prediction: playing in the past

The simplest network game model, and the one that is implemented in my demo, is to have the client send commands to the server, and have the server send state back to the client, which displays them. With this implementation, when you press a button to take an action, like moving forward, it takes one entire network round trip before you see the movement. The initial versions of Doom and Quake used this approach, because they were designed for local networks and not the Internet. Quake was released just when home Internet was becoming widespread, and people did play over the Internet anyway, which only worked if the network round trip time was low. With my demo, this feels playable up to about 50 ms of one-way latency.

The most interesting part to me is this means players are playing the game in the past, when compared to the server state. I've drawn the diagram below in an attempt to describe what I mean. Both the client and server start the game at the same instant, t=0. At this time, the player presses the "move forward" button, so the client sends this message to the server. It takes one time unit/frame to get to the server, so at t=1, the server receives and processes the button press. At t=2 the server simulates the player moving forward one unit, and sends the state to the client. Finally, at t=3 the client receives the updated position displays it. This means the client's display was updated at t=3, instead of t=1 in the "local" version. This is delayed by one network round trip time (2 units in this example). At any instant, the client is one time unit behind the server, which is what I mean that the game is being played in the past.

no prediction diagram

When I experiment with moving and firing in my simulation, I think I can notice even a small amount of latency. However, I feel like I can adapt to it and the game is playable up to about 50 ms. Above that point, it starts to feel "impossibly" slow and really unpleasant. This means this simple simulation will only work for physically "close" players. From my home connection in New York City to Google Cloud's data centers as measured by gcpping.com, I can only play games hosted on the eastern half of North America (Virgina, South Carolina, and Montreal).

Prediction: playing in the future

To hide the effects of latency, modern games predict the effect of their actions. As a result, clients are now in the future, rather than in the past. Let's reconsider our example, where a game starts and a player immediately starts moving forward, shown below. In the first instant, the client processes the move forward command, so it sends it to the server. In the next instant, the client predicts moving forward one step, and the server receives the message. In the second instant (t=2), the client has predicted moving forward another step. The server simulates the movement and sends the "official" state. In the next instant (t=3), the client receives the confirmation that the user did move forward. The tricky part is the client needs to decide if this agrees with its simulation. If it does not, then it needs to "rewind" its simulation, and replay its local actions again.

with prediction diagram

With client-side prediction, the time between an input and a display update is the as a local game. However, the client is ahead of the server's "real" state by one network delay, which is what I mean that it is playing in the future.

Further reading

I am not an expert on this subject, since I have never worked in games. If you want to learn more, you should read about how real games make this work.