Lightweight vs. Heavyweight Servers: A Question of Design
In the vein of my previous post on Box2D and explosions, I’d like to discuss a topic that has been on my mind over the past few days: that of heavyweight versus lightweight servers… and possibly even a third server-architecture contender.
Recently, I have been working on a branch of the Leges Motus code that makes a radical change to the operation of the game as a whole. One of the main purposes of our large refactor has been to make the game much more modular, less tied to the original design. This has been extremely helpful in the process of this change: I am working on making the Leges Motus server, which previously served almost exclusively to relay packets and keep track of client addresses and scores, actually execute the game logic and take precedence over the clients in that respect.
In other words, the server will control where players move, holding a canonical game state and sending information about it to the clients, rather than allowing the clients to send it all of their own game data and just passing that information around to the others.
This is the difference between a lightweight server (more of a peer-to-peer relay node than a full game server) and a heavyweight server (which runs all the game logic and controls what the clients see).
There are certainly some advantages and disadvantages to both types of server:
- Very small server storage and CPU requirements – easy to run your own server.
- Simple code on the server side.
- Very little data must be transmitted to a client about its own game state – and potentially little about other clients, if their state can be approximated somewhat by client game-logic – so network bandwidth can be reduced.
- What the player sees is exactly what he gets. Since the client controls bullet hits, movement, etc., lag is less of a factor when determining what the result of an action is.
- No canonical game state – clients can be somewhat out of sync with each other, and it might require more networking to get them to sync properly.
- There is almost no way to prevent cheating. Clients can send anything they want to the server, and it cannot verify that they are doing reasonable things while retaining the advantage of a small, lightweight server.
- Physics can act strangely when clients see different information about the current state of various physics objects. For instance, players might seem to push past each other when colliding, because they have no way to cooperate on the results – one might see the collision later, or have it resolve differently, leading to a strange-looking outcome.
- A client that is lagging or dropping packets can seem like it is cheating, e.g. shooting other players when they can’t tell that it’s moving into range.
- There is a canonical game state, so all movement and collisions, among other things, can be made to make sense.
- The server can get a better handle on cheating, because it knows everything about the game state and can tell when a client is acting strangely.
- Clients can be somewhat reduced in complexity – if CPU or RAM is a significant performance limiter, the client computers may not need to be as powerful to run the game (as long as GPU isn’t the limiting factor).
- Updates or in-game advertising may be easier, since the server can sometimes deploy new game logic or content without client version updates.
- The server will require more resources to run.
- If clients are lagging, they may feel that they can’t get snappy/proper results from actions.
- Network communication could be increased in volume, as each client must be told about relevant game state.
Given all of these factors, it is difficult to make a decision about the best type of server-client relationship for your game. Originally, we chose a lightweight server for Leges Motus, because the only important changing game-state was controlled by each client – none of it had to be shared or mediated, aside from the gates. This meant that snappy responses were more important than canonical game state. Additionally, we wanted to ensure that the server would be easy to put up and run from anywhere, to encourage the creation of new servers around the world. This situation was ideal for a lightweight server.
Now, however, we have much more intricate physics requirements. We can’t have clients disagreeing amongst themselves about the game state, or things could come out looking very strange. Therefore, a heavyweight server is looking more useful.
But a few problems remain with the heavyweight server approach – we don’t want players trying to shoot and then getting a miss from the server because they had a little bit of lag, for instance. Therefore, we’re taking a hybrid server approach. The server will control all positions and motion, and it will keep track of all the information about the game. The clients, however, will also store their own game logic and physics, and they will tell the server when they fire and who they hit. This sort of hybrid heavy-server-heavy-client approach might take somewhat more resources, but other benefits are maximized.
At the moment, it looks like this theory is paying off. The heavyweight server branch seems to be working fairly well, and most of the small problems that have cropped up were fairly simple to resolve. I will be continuing to test and improve the branch, and, if all goes well, we may see it get merged into the main trunk and become the standard Leges Motus architecture. It’s certainly been extremely helpful that we implemented the game logic in such a way that it is mostly self-contained and there was no dependency on a specific client design – it merely needs to know some information about the game, and then it can run mostly on its own. This made running it on the server quite easy, all things considered.
If you have any thoughts on this topic, please leave a comment and tell me what sorts of client-server architecture have worked well for you.