Fundamental Game Networking Concepts
There are many different types of online systems for supporting all kinds of multiplayer games, from MMORPGs to simple peer-to-peer co-op titles. Leaving aside the complexity and implementation details of each system, we can detect some common properties among them. In this post, we’ll go through the fundamental concepts of multiplayer systems, from a gameplay programmer’s perspective.
The Server
In order to establish meaningful communication between different players of a game, a server is required.
This can be either a dedicated server or a hybrid machine acting both as server and as client/player (you might remember the days of Warcraft 3 online, where you had to do some port-forwarding in your router in order to be able to host games. In that case your PC acted both as a client and a server).
They both run a server version of the game. The most notable difference between them is that a dedicated server does not require visual feedback (rendering) of actions while a hybrid machine does (since it is tied to a player).
In either case, the main responsibilities of the server are the following:
Ensure that all players/clients are performing valid actions
This means that the server has to go through the client requests and perform a sanity check. In simple words: “Is the action possible, given the current state of the world and the requesting player?”
Why do we have to overload the server with such checks? Can’t they happen on the client side?
It is important to understand that, game logic aside, there is a layer of security tied to the server code. We have to assume that all client code can be modified/compromised.Synchronize and propagate information between players/clients
After a player/client performs a valid action, there is usually a number of other clients that want to know. The server has to find them and propagate this information.
The server is also responsible for letting a client know when a requested action is rejected. In that case, the client has to undo any decision that was made based on successfully performing the action. This is known as rubber-banding or network corrections. It can happen for multiple reasons, the most famous being lag.
When the server and a client are not in sync…
An important part of the synchronization process is the prediction system, which we’ll talk about in the connection section.
The Clients
Of course, making a multiplayer game suggests that multiple players exist (wow!). We usually refer to the players as clients.
These clients have their own version of the game code, which is slightly different from the server version. Hopefully, the why is clear by now (remember that server has to do all these extra checks and syncing/propagation routines).
An important concept of the client side is defining the relevancy of a client. The relevancy is basically a set of rules that describe what piece of information a client needs to know at any given moment. The simplified idea, from a player’s perspective, is: “For every action that can happen in the game, and is not caused by me, under which conditions do I need to know about it?”
For example, in an online FPS game, when another player switches weapons and starts aiming, it makes sense to propagate that information to us so we can see it on our screen, right? Not necessarily. This is where the “under which conditions” part of the question comes in. Maybe the player is not in our line of sight, so there’s no need to know. But yet again, maybe they’ll become visible soon enough so that we need to know beforehand. It’s up to the creator to decide.
Of course, the relevancy-related game logic ends up running on the server. After all, it’s the server that has to decide who should know what. However, when we are trying to form this set of rules it is important to think about the game logic from the perspective of a client.
The Connection
The connection specifics between the server and the clients are mostly based on technical choices. We will not discuss these, but rather just mention some parts that are important to keep in mind.
Communication happens using the UDP protocol, for the most part. There are some special cases where we use TCP to transfer information, but these are very rare. The reason behind this is that a game runs in real-time and we can’t afford the extra overhead of a reliable protocol such as TCP. Of course, that means that our online system has to account for UDP problems (unordered data, packet loss, etc.).
When it comes to the connection part, the most important thing to know is that most online games are relying on some sort of prediction system. No matter what protocol we use, it takes some time to reach the server from a client and vice-versa. If that’s the case, how can these two ever be truly aligned? The prediction system takes into account all the specifics of networked communication and attempts to solve this problem. A very simplified idea of how this works is: “A client says that they fired a rocket, but that happened 20ms ago (time needed to reach the server). Therefore the rocket has already moved X units in the world.”
What difference do 20ms make? So what if the server is 20ms “behind” the client?
When it comes to data syncing, disasters can happen for much shorter time intervals. If you choose to work on a multiplayer game you’ll spend a significant portion of your time trying to solve synchronization problems.
Programming for Multiplayer
All of the above sounds like a nightmare to code. Thankfully most modern game engines already have a solid multiplayer system in place. The developer doesn’t need to worry about the implementation details of the network layer (unless your interest lies in online/network programming).
The main job of the gameplay programmer is to define what data needs to be propagated and which code snippets have to be executed, on what end (server/client). This selection process is mostly based on the following factors:
- Game logic (our choices have to be aligned with how the game should operate)
- Load optimization (we want to select the least possible amount of data for propagation and remove any unnecessary piece of code from the server side)
- The type of the game (we can get away with a lot more optimization discrepancies when creating a MOBA rather than an MMORPG)
An Example
In a typical MOBA game (League of Legends, Dota, etc.) when a player wants to activate an ability, a process similar to the following happens:
1) The player presses a button to activate an ability. A local sanity check happens on their machine. This is mostly for UX reasons at this point (ex. play a failure sound when the ability is on cooldown).
2) If the local checks are satisfied the player/client communicates this request to the server. It basically says “Hey, I want to activate ability X and I claim that I can do that. Can you validate and inform anyone interested in knowing?”.
3) The server checks if the action request is valid. The validation process is always based on data stored on the server (never trust what a client says). Is the ability not on cooldown? Is the player alive and in a state to activate it? Does the player have enough resources?
- If all requirements are satisfied the server has to inform all interested/relevant clients (The allies of the player. Enemies are considered relevant if the current player is visible/not in fog of war).
- If any requirement is not satisfied, the server rejects the request and lets the client know, so they backtrack any progress made based on executing the ability.
Conclusion
Thank you for taking the time to read this post.
Hopefully by now you have a decent idea of how a multiplayer system works at its core. Perhaps, you can start noticing all these concepts in the games you are playing. Ideally, it is clear that it’s unlikely your opponents are launching DDOS attacks against you, it’s just a problematic connection between you and the server that causes network corrections.