Anti-Cheat with 4Players SCILL

Anti-Cheat with 4Players SCILL

The main purpose of 4Players SCILL is to empower game developers to implement gamification features like daily or weekly challenges, battle passes and leaderboards into their games in hours and not weeks.

However, today, we will show how you can leverage 4Players SCILL Events and SCILL Leaderboards to implement anti-cheat-systems into your game with a couple lines of code. The code examples here use Unity with Mirror Networking, but as the concepts are basically the same for all multiplayer games this will also work for you and your game if you don’t use Unity. SCILL also has a SDK for Unreal, so the methods described in this post work basically the same in all game-engines and applications.

Input validation is key

Your server (or host) should validate all input coming from clients to make sure that it has not been hacked.

This is a simple example: We store a float value with the next time that an attack is possible and wait for clients with authority (which own the player object) to press the space key. Then, we call a command on the server to spawn a projectile in the network.

private void Update()
{
  // Make sure this client has authority about this object
  if (!hasAuthority)
    return;

  // Make sure it's time for this client to attack again
  if (Time.time < _nextAttackTime) 
    return;

  if (Input.GetKeyDown(KeyCode.Space))
  {
    // Send a command to the server
    CmdAttack(transform.forward);
    _nextAttackTime = Time.time + 1.0f;
  }
}

[Command]
private void CmdAttack(Vector3 direction)
{
  GameObject go = Instantiate(_projectilePrefab, transform.position, transform.rotation);  
  // Setup projectile....
  // ...
  
  // Spawn the projectile on the server
  NetworkServer.Spawn(go);
}

For cheaters, or cheat programmers, this will be an easy target. They just need to remove the return in line 7. This way, on client side a lot of attack messages per second can be sent to the server and the server will execute it without any further validation.

We need to validate the data. Let's just see the code to do that validation on server side.

[Command]
private void CmdAttack(Vector3 direction)
{
  if (Time.time < _nextAttackTime)
    return;
 
  GameObject go = Instantiate(_projectilePrefab, transform.position, transform.rotation);  
  // Setup projectile....
  // ...
  
  // Spawn the projectile on the server
  NetworkServer.Spawn(go);
  _nextAttackTime = Time.time + 1.0f;
}

Remember that in a networked game, your Unity scripts (or objects) will exist multiple times: On each client and on the server. Every player has a GameObject with an instance of this script, and on the server a GameObject exists with an instance of this script exists for every player.

Therefore, we can use the same code on client and server side for data validation. And this is what we do: The client validates input, then sends data to the server. A "copy" of this script runs on the server, and here we run the same tests again. But this time, it's running on the server - a place where your cheaters will not get their dirty hands on so easily. We don't need to sync values here, as the local time will be different on server and clients, but we only use relative time here, so the absolute values of _nextAttackTime are not important. It's just important that the same amount of time passed since a projectile has been fired - and as time runs with the same speed on client and server this code will work.

Let's finalize our code by refactoring it a bit. If we want to increase fire rate, right now we need to change that in two places, which is not good at all and is prone to for errors.

private bool ValidateAttackInput()
{
    // Make sure it's time for this client to attack again
  if (Time.time < _nextAttackTime) 
    return false;

  _nextAttackTime = Time.time + 1.0f;
  return true;
}

private void Update()
{
  // Make sure this client has authority about this object
  if (!hasAuthority)
    return;

  // Make sure it's time for this client to attack again
  if (!ValidateAttackInput())
    return;

  if (Input.GetKeyDown(KeyCode.Space))
  {
    // Send a command to the server
    CmdAttack(transform.forward);
  }
}

[Command]
private void CmdAttack(Vector3 direction)
{
  // Make sure it's time for this client to attack again
  if (!ValidateAttackInput())
    return;

  GameObject go = Instantiate(_projectilePrefab, transform.position, transform.rotation);  
  // Setup projectile....
  // ...
  
  // Spawn the projectile on the server
  NetworkServer.Spawn(go);
}

That's it. The server validates the input and if attack messages come in faster than they should, they are ignored. Most likely you will have the same pattern implemented (somewhere) in your game.

Anti-cheat with SCILL

Right now, we try to prevent cheats. Cheaters will always start with the easy hacks, like speed hacks shown above. If they are not successful, they will dig deeper, and finally they will find a hole. This is certain. So, how can SCILL help to prevent that?

It's easy. The idea is this: The server side validation should never trigger, because we already do client side validation. But, if the code triggers a lot, this indicates that this specific user is playing around with your client side code or installed a cheat.

Instead of just returning, we can use that data to "mark" this user. Think of it as putting a sticker on the user if this happens. There might be occasions this happens for users without any speed hacks installed like latency or network packet loss among other reasons. So, here and there we'll see users with a couple of stickers applied. But if they look like this, something is definitely not right 😃.

This is exactly what SCILL is all about: Storing abstract atomic events attached to your users and to create value out of that data.

Before you can use this code, you'll need to:

The Unity SCILL SDK exposes the SCILLManager singleton, which you can use on server side to send events for that user. In this simple code example we assume that you use some authenticationData attached to the connection. This can be Steam, Playfab or any other authentication system. SCILL only needs to get a unique (persistent) user id like a SteamID or in simple games it can be the SystemInfo.deviceUniqueIdentifier.

[Command]
private void CmdAttack(Vector3 direction)
{
  // Make sure it's time for this client to attack again
  if (!ValidateAttackInput())
  {
    var userData = (UserData)connectionToClient.authenticationData;
    SCILLManager.Instance?.SendEventForUserIdAsync(userData.userId, "trigger-event", "single", new EventMetaData
    {
      amount = 1,
      event_type = "vf-attack"
    });
  }

  GameObject go = Instantiate(_projectilePrefab, transform.position, transform.rotation);  
  // Setup projectile....
  // ...
  
  // Spawn the projectile on the server
  NetworkServer.Spawn(go);
}

That's it. By adding a couple of lines of code, you now send events to the SCILL cloud. There are many different events you can send for different things. trigger-event is an all-purpose event that makes sense here. SCILL events can have meta data attached. In this case, we add an event_type info that we will use later. As we might have multiple input validation methods, we can use different event types to understand where validation failed. I used a prefix vf (which means validation failed) and then a name of the place in the code.

More info on events, their structure and available types can be found in our documentation.

Now, whenever a validation input error happens, we place a sticker on that user. As the stickers are persistent, cheaters will be already marked in their early stages.

Next, we use a leaderboard to visualize that data.

Identify cheaters with SCILL Leaderboards

With SCILL, you can create leaderboards listening on specific event types and summing up all events coming in. This way, you can easily create leaderboards for your players with most kills or those guys that never die. But, you can also visualize any other event with leaderboards. In this case, we just create a leaderboard listening on trigger-event events and the vf-attack meta data.

We can do that in the 4Players SCILL Admin Panel:

Please note: In the Admin Panel, you can add multiple leaderboards (depending on your tier) for different event types. But you can also sum up multiple events in one leaderboard. So, instead of just adding vf-attack you could also add another meta data value by setting vf-attack vf-move vf-collect-health to build a leaderboard for all validation events.

That's it. Now, you have a real-time leaderboard showing the cheaters in your game! You can use our leaderboard web component to add a fully functional leaderboard in your own backend tools, or just use our Playground application to show the leaderboard for this game.

Do you see the Realtime Updates section at the bottom? SCILL offers real-time notifications that your App can respond to in real-time. So, by listening in on those messages you could show the user a warning to stop cheating or else he'll get banned.

The other way to automate things is to set a Webhook in the SCILL Admin Panel calling a script on your side that you can use to implement business logic for banning users from playing your game, or kicking them from the server. It's up to you how you handle this:

  • Use SCILL Playground as a User Interface to quickly check if there are cheaters in your game
  • Add SCILL Leaderboard Web Component to your web based backend to have the tool right where your community management is
  • Implement Webhooks to automate things on your side by leveraging data collection and processing on SCILL side.

Next steps

Well, you have implemented the SCILL SDK into your game, and you have set up an account. Why not add leaderboards to your game, or daily or weekly challenges with rewards to your game - it's done in less than an hour. Even full fledged, fully functional battle passes can be added to your game in a rather short period of time. SCILL handles all the heavy lifting and we even provide fully functional, ready-to-use prefabs with user interfaces to get you started quickly.

Interested?

Additional documentation

We have extensive documentation on all these topics we touched on this blog.