My attempt at describing the difficulty of multiplayer programming.

I sometimes see people asking for a single-player game to be turned into a multiplayer game. While the motivation is usually nice (they want to play with their friends!), those comments mistakenly assume that it’s trivial to turn a game into a multiplayer game. Making games is already hard and making it multiplayer introduces all the chaos of distributed systems.

Games work by tracking the state of everything. Things like stats (health, score), entities (characters, interactable objects), and environment. So for a multiplayer game to work you want those things to sync across the different players playing the game. Before we make that jump though let’s imagine a simpler problem: showing a replay of what just happened to the player. You don’t need to sync things to other computers. You know what happened, you just need to show it again to the user.

The problem is that somethings that the game is tracking are recalculated every frame (which means they are impacted by the frame rate changing slightly). Since this would break our replay, we have to avoid that. The normal strategy to do that is to use the “physics” loop, which calculates things in a frame-independt way, for everything that is important for the replay. That’s how I made the pitch animation work for the replays.

Replay simulation of a pitch from my game The Ump Show.

But this doesn’t work for everything. For example, in PIDs: Creating Stable Control in Games, I talked about how I implemented a tag play for a baseball game. In order to fit the characters’s movements into a fixed time window, I had to manipulate the animation speed which introduced a whole lot of indeterminism in the overall sequence. The animation couldn’t use the physics loop as it introduces visible jitter (since the frame and physics loop are out of sync).

Tag play where the character’s running animation is manipulated to arrive at the right time.

It also doesn’t work easily when randomness is used in the sequence. Since the randomness also makes the sequence non-deterministic, the sequence cannot be replayed without either storing all the random values or reseeding the random generator and being meticulous about what things access that random source.

So those are the complications if you want to give the player a replay of a sequence from the game.

Now going back to multiplayer games. Multiplayer games are like replaying the game sequences on a different computer in real time. You carry over all the problems from replaying sequence (calculation accuracy, randomness) and add all the problems of distributed system.

Games intended for multiplayer are designed with these constraints in mind. Everything that manages the core gameplay runs in the physics loop. The game client that runs on the user’s side is capable of running this physics loop and updating all the graphics based on that. The game client is also capable of accepting updated values for anything and updating its graphics based on that. The game server owns the central loop. It accepts user controls from the clients and updates its internal loop and propagates the updated values down to the individual clients.

If your game isn’t aiming for multiplayer from the start, then you likely aren’t going to build your game to allow EVERYTHING to be updated with arbitrary values at any moment. It makes your game obnoxiously complex.

Icons used in Rocket League to indicate to users when they are having network related issues that cause issues with the gameplay

And even if you build that part for fun. There would still be an enormous amount of fine-tuning so that your game can actually deal with laggy updates to/from the client in a way that the user doesn’t find frustrating. Rocket League has had years of investment and it still runs into major network issues every now and then.

So the next time you want to ask a game developer to convert their single-player game into a multiplayer game, just tell them you love the game instead.