Never trust the client: simple techniques against cheating in multiplayer and SpatialOS
Most cheats are easy to prevent - we run through the best techniques.
Making online multiplayer games today is easier than ever. You simply download a package on the Unity asset store and you’re ready to go. You don’t even have to know any programming in order to get something up and running quickly. That has become a thing of the past.
There are negative aspects to this too. More and more indie game companies don’t know what is going on under the hood of their online game. As a consequence of this, some games released today are totally unoptimized and open for cheating.
In this article, I want to bring up the basics of one thing – cheat prevention. I want to show you how simple techniques can protect your game against simple attacks and how the architecture of SpatialOS can help you overcome them.
Cheating
Depending on the type of game you’re making it might be a waste of time to implement cheat prevention. Why cheat in an online party game with your friends? Or why cheat in a cooperative online story game? The games you should focus on are online games that you can play with random people.
Because if given the chance, people will cheat. They will aimbot, they will fly over walls, they will lag switch, they will wallhack, they will team, they will stack, they will boost. The fact that the vocabulary for it is so wide is itself an indication of how widespread the behaviour is.
Here is an example of a lag-switching cheat and how you can simply modify your internet cable in order to gain an advantage:
A growing amount of startups simply don’t have the basic knowledge of the networking technology in their games, so they often leave problems as something to fix later, even after release. One thing that these cheatable games have in common is the simple fact that trust is given to the client.
You should never, ever, trust the client. You should assume that all clients are sitting with a modified version of their game and assume that all network packets sent from these clients are modified and malicious. With that in mind, hopefully, developers will be scared enough to become at least a little bit paranoid. Paranoia and an appreciation of conspiracy theories is a good trait of a network programmer.
A consequence of missing key features in cheat prevention could be catastrophic. By doing the right things in the wrong order a player might be able to single-handedly bring the whole server crashing down.
By using easily available, general purpose tools on the internet and with some youtube tutorials, you are able to grab many low-hanging fruits when it comes to cheating. Here are some examples on simple cheats anyone can do as a client if the server does not protect against it or is programmed in the wrong way.
Teleport to a position.
Run super fast.
Go through walls.
Modify or freeze the number of items you have in your inventory
Modify or freeze health/ armour/mana on yourself, essentially making you invincible.
Fly through the air.
If you are the wrong person with the right tools, you might be able to take it even further. If the server doesn’t protect against these exploits, you might be able to:
Tell the server that you took fall damage without actually taking fall damage
Tell the server that another player took fall damage. Essentially making it possible for a single player to wipe out the entire population of a server in a few seconds.
Spawn objects/projectiles on any location in the game.
Preventing cheats in SpatialOS
Movement
The following requires you to have a basic understanding of how components make up entities in SpatialOS and how write access can be given to components by certain workers. (You can read more about entities here.)
The simplest version of a player is an entity that has a transform component on itself. The client that is controlling this player is given write access to this transform component. All other workers that have this entity checked out will instantly receive changes made to this component. If you are making a game where you are not worried if people cheat, like in a social chat game or a simulation with spectators, this might be enough. It’s even encouraged to do it in this way in order to save bandwidth and resources. But if you need players to be restricted in a way to prevent cheating, this is completely unacceptable – a compromised client will be able to teleport to any location in the world at will.
Illustration where we’re allowing a player to update their transform directly. No checks are made to prevent cheating.
One simple technique to making it impossible for players to move faster than they’re allowed to (or teleport!) is to move the player movement logic to an authoritative worker. Instead of allowing the client to update the transform directly on the player, we simply make a new input component that the player can communicate through.
MOBA’s like Valve’s DOTA 2 have their movement calculated and done on the server.
Here’s an example of a Schema file making up a PlayerInput component. The client has write access to this component:
package playground;
component PlayerInput
{
id = 12001;
float horizontal = 1;
float vertical = 2;
bool running = 3;
}
Having movement logic done on a worker that you are in control of makes it completely secure. To put it simply, all the client sends to the authoritative worker is the direction the player moving in and their actions, just like controlling a remote-controlled vehicle.
Illustration where we are only allowing the player to update data on an Input component. The authoritative worker will then read that information and move the player.
One major problem with this approach is latency. By doing what’s described above with any latency at all, a player will see their character move slightly after they told it to move. Since they are waiting for the authoritative worker to move the player, they will have a delayed reaction to their input. We can, however, make simple modifications to make it acceptable.
Here’s a modified PlayerInput component that allows for client-side prediction:
package playground;
importat "improbable/vector3.schema";
component PlayerInput
{
id = 18001;
improbable.Vector3f desired_position = 1;
bool running = 2;
}
Instead of sending the client’s direction of movement to the authoritative worker and waiting for the player to move, we instead move the player object on the client instantly and send the position we’re currently at to the authoritative worker. We call this position our “desired position”. The authoritative worker then checks to see if we are allowed to move from our last position to the new desired position and updates the transform accordingly. Two simple checks can be used. One to see if we are moving too fast and another one to see if we’re moving through something we’re not supposed to move through.
This is, however, not a perfect solution. You still have to compensate for latency in order to solve the “who shot first” paradox. I recommend reading these in-depth blog posts by Improbable’s Gabriel Gambettaabout this issue.
Darkfall Unholy Wars was a first-person MMORPG with full player versus player combat – and with only partial command validation.
Command and requests
Whenever you want to do something in an online game, you send a command to the server. This command is formed as a request. The response you get back from the server is the result of that request. In other words, you could say that it behaves similarly to a simple function call or a remote procedure call (RPC). An example of a request like this could be to request to build a block in Minecraft or cast a spell in Guild Wars 2.
There are a bunch of things that could go wrong if checks are not in place to validate these commands. As I said earlier, we should always assume that the content of a request is malicious and that we should always make sure that the values of the content are within our allowed bounds.
Without proper checks in place a hacker might be able to, for example, cast spells from a location that is very far from their player or attack enemies far away from a safe location. This was the case in Darkfall Unholy Wars, an MMORPG the developers left open for this kind of cheating. In the following video, you can see a player attacking other players at a great distance, amongst other hacks.
There are no true answers to fixing these issues, but one helpful piece of advice is to always sanity-check all data that is sent with a command. Here are some examples:
Check to see if the position that is sent with a command is within the range of the player.
Whenever an index is being used, always make sure that this index is not out of bounds.
Whenever an index or a type is being used, make sure that they are allowed to use this index or type. Not doing this might allow players to spawn an admin-only item in the game if they are sending “spawn this item” commands.
Limit the rate people are allowed to use commands with cooldowns to avoid flooding or crash exploits.
Development tips
While you are developing your online game you have the perfect tool to check for cheating.- your own project. Try moving your player around in the editor while playing, delete walls, teleport, and fly through the air. If you detect even the smallest thing but you believe nobody is going to exploit it, then you are wrong. People are going to try and break the rules and boundaries of your game no matter what. What’s best is being one step ahead of them and try to break your own rules first.
Read more about:
BlogsAbout the Author
You May Also Like