Skip to content

Instantly share code, notes, and snippets.

@Snowiiii
Last active July 21, 2024 08:16
Show Gist options
  • Save Snowiiii/2c306f3e8926bc7fb8acaaa8c3c105d7 to your computer and use it in GitHub Desktop.
Save Snowiiii/2c306f3e8926bc7fb8acaaa8c3c105d7 to your computer and use it in GitHub Desktop.
How to Develop an Anti-Cheat

Note

Many of the Information here is not only Minecraft related and can be used for different Anti-Cheats as well

For Beginners

Hello! With this post, I want to share my knowledge and provide tips on how to start and continue developing a Minecraft/General anti-cheat system. I'll cover tips for both Minecraft Java and Minecraft Bedrock/Pocket Edition.

Language

To start programming something, you'll need to learn a programming language. the options are Java or Kotlin, but for PocketMine Software, it uses PHP.

Note

You can skip this if you're just looking for Client-Side Cheat Detection/Protection, Go to Client Side Docs. The only thing you should consider learning are Packets and RayTracing

Software

There are plenty of Minecraft server software options available. Most Java Edition servers mostly use something implemented upon Bukkit, with the standards being currently Spigot or Paper. Here is a guide for plugin development on Spigot. For Bedrock Edition, PocketMine or Nukkit are the standard choices, and here is a guide for PocketMine and here is a guide for Nukkit.

Start

So the first thing we must understand is that we don't want to use APIs. While Bukkit, PocketMine or Nukkit provides great APIs for many use cases, we can't use that data. Why? Because we will listen for packets ourselves, providing us with more data than the APIs, and we will be able to catch many more "events." Okay, this was much. Maybe I should explain a bit in-depth.

So Why Do We Want to Use Packets?

  • We can access more information than when just using other APIs.
  • We can block or modify things before they reach Vanilla Code.
  • We can send packets too, giving us more options than when using other APIs.
  • Finally, our code will run on different threads than just on the plugin thread. In Java Edition, the library Netty provides servers & clients with useful networking features. By default, there are 4 Netty threads. On Bedrock, there is RakNet, If you're using Nukkit(Based on Java) its using RakNetty.

Packets?

I will don't go deep on this one because of the reason that the internet is literally full of explanations of what packets are or what networking, in general, is. But at the end of the day, packets are just pieces of data that will be sent from the client (player) to the server or from the server to the client. For example, if Player A moves, Player A will send a Move Packet → Server. Then the Server will send all other clients (players) a packet to tell all other players that Player A has moved. All other players will get a packet that has data, for example, the movement data, so they can set the new location for Player A and render them at the new position. Here is a more in-depth explanation of packets.

Okay, great. So how am I using these packets now? In Java Edition, we have 2 options: 1) use NMS or 2) use a library. In the end, the libraries just use Java Reflection to call NMS Functions, but yes, this makes you multi-version compatible.

Libraries:

So there are currently 2 good options for Java. Let's first talk about ProtocolLib. ProtocolLib provides a very raw experience. You can easily read & write packets using Minecraft's Protocol. But if you have a large code base and, above all, want to be multi-version compatible, this will be a nightmare. PacketEvents, on the other hand, gives you a wrapper for each packet where you can easily get all information version-independently that you need.

For Bedrock, there are the Official Bedrock Protocol or This Simpler Library, If you're using the Go Language gophertunnel is an Option too.

No APIs

So normally Bukkit or PocketMine will provide an API for us to work with, for example, handling commands, working with entities/players, entity data like health, position, rotation, and much more. So why don't we just use that? This is one of the downsides of using packets.

Anti-Cheat Coding

Finally, after setting everything up, we can start working. As mentioned earlier, we won't use APIs, so we need to create our own players and then listen for player data. Sounds awful, right? Yes, it is. But there is just no other option. Okay, start to create player data when a player joins and then start to listen for data (pre-processors). You really should look at the Minecraft Protocol. Fortunately, wiki.vg provides great resources for doing that. Here is the Minecraft Java Protocol and the Bedrock Protocol.

Okay, I added player rotation yaw (vertical) & pitch (horizontal) to our data. Now, I will listen for rotation packets and then update the data to the packet's data:

Here is an example:

 if (packetType == PacketType.Play.Client.PLAYER_ROTATION) {
       val packet = WrapperPlayClientPlayerRotation(event);
       playerData.yaw = packet.yaw;
       playerData.pitch = packet.pitch;
 }

Keep in mind that pre-processors must run BEFORE your checks, but you probably should have the option in any packet library to set the listener priority. For pre-processors, always set the priority to LOWEST.

Great! Now we have the latest rotation information.

Checks

I heavily recommend that you build a structured base for your checks and don't just put all checks into one simple file using just one single packet listener. After that, you can begin to work on the most important but also the most challenging part: the checks. In this example, we will check for illegal rotation, especially pitch rotation. In normal Minecraft, you will NEVER have the possibility to move your pitch above 90 or below -90 degrees. Like in many FPS games, this results in the fact that you can't just infinitely rotate your head up or down. So, if a modified client sets an illegal pitch value, we want to flag (send a warning message to staff) that the client is doing illegal things. I must say that this check will probably not flag many cheaters, but it's just an example, and if you're lucky, a Killaura from a bad client might just ignore the pitch limit and will flag.

Here is an example in Kotlin:

  if (packetType == PacketType.Play.Client.PLAYER_ROTATION) {
        val playerData = DataManager.getData(user)
        val pitch = playerData.pitch;
        if (pitch > 90.0F) {
            flag("Illegal Pitch, Max: 90.0")
        } else if (pitch < -90.0) {
            flag("Illegal Pitch, Min: -90.0")
        }
  }

Maybe you'll find a cheat client that has an illegal pitch module so you can test and see if it works.

So it's simple:

  • Get data
  • Check data
  • Flag/do nothing

Okay, the last part is a bit of a lie; buffering is an option too.

Buffering

Basically, if you would normally flag the player but are not sure if it was just pure luck or something, you can record if a player fails multiple times in a low amount of time. If the player did something so hard and so quickly so many times, you can be pretty sure that it's not luck and should flag.

Setback

What is a Setback? Simply put, the player will be set back... :D. By this, I mean the player's location. For example, when a player fails a movement check, sure, we could just "flag" them, and staff members would receive an alert. Some anti-cheats even do that because the cheater might think that the anti-cheat is doing nothing, enabling even more obvious cheats. But what if you have no staff online or staff members are currently unavailable? I also don't think you'd want a staff member to intervene for every flag, especially on a larger server. So setbacks are there to annoy cheaters and make it clear that they will be flagged, and they should turn off their cheat. It's important to understand that it's not just so simple to cancel the client's packet. This would mostly work when we're talking about platform events (Bukkit, Nukkit...) because they internally save the client's last position and then teleport the player back. Since we don't use events, we must save the last position ourselves. When we want to setback someone, we send a teleport packet with the last position. Sometimes, anti-cheats save multiple positions that are 1-4 ticks behind so you can teleport the player even further back. For Killaura cheats, a rotation teleport is often used, where the position stays the same, and only the rotation changes. But keep in mind that setbacks should not always be done; not all flags require them. For example, if your cheater uses a sort of automatic inventory manager cheat, there is no point in setting their position back. The better way would be to send an inventory close packet.

How to Move on?

So basically, every Anti-Cheat will need to look at the client code to make good checks and understand what the client can do and what is impossible or how the client handles certain things. That's not hard when coding for Java Edition since the client/server is written in Java. There are easy ways to decompile the bytecode into readable source code. Popular choices are the old Mod Coder Pack, which provides decompilation/recompilation for versions 1.7-1.12. For newer versions, you should use Mod Coder Pack Reborn. For Bedrock Edition, the game is written in C++, and there is literally no way except reverse engineering to get the code. I think it's a better option than just listening to what packets the client sends when something happens. Sorry, Bedrock Community. Keep in mind that you're not allowed to publish any decompiled Minecraft code. That's one of the reasons why Forge or Fabric exists.

Okay, let's say you got the code. Like already mentioned above, you should start by digging into the client and figuring out what it does when. For example, if your goal is to make movement checks, you should understand the client's logic:

Forward Key Pressed → Is allowed → Get speed → Client move (move internally) → Server move (sends a packet)

Let's go step by step:

Is Allowed?

  • Is the player not collided?
  • Is the player not inside a GUI (inventory, pause menu...)?

Get Speed?

  • Is the player inside a vehicle (minecart, boat)?
  • Is the player riding (horse, pig...)?
  • Is the player flying?
  • Is the player sneaking?
  • Is the player using an item?
  • Is the player sprinting? (Fun fact: players can't sprint when they have the Blindness effect)
  • Does the player have a speed effect?
  • Does the player have a slowness effect?
  • Is the player in liquid (lava, water)?

I think that's it for the most part. So you see that's really much stuff you must check on the server-side. One of the goals is to replicate much of the client behavior. Grim Anti-Cheat, an open-source Java Edition Anti-Cheat, is a good example. They heavily promote their "Movement Simulation Engine" and "having a 1:1 replication of the player's possible movements". It's a smart and effective solution. The same goes for combat too. So let's move on with combat.

RayTracing

You might now be thinking, "RayTracing? I'm not interested in implementing realistic light into my Anti-Cheat," but hold on. It's true that the technique is similar, but there are key differences. We're not going to work with lights; we're just going to do calculations and get results that we need. The idea is that you basically have a ray from your player's eye position and then can calculate what the player is currently aiming at. This check is used at the client-side and is very precise. And like already mentioned, we like to use things out of the client.

image.png

If you're targeting Bukkit 1.13.2+, you can make use of built-in RayTracing using the Entity or World interface. But when targeting lower Bukkit versions, normally RayCastUtility would be a good option. But since that library uses Bukkit API calls that we can't call outside the Plugin Thread, it is not an option for our packet-based Anti-Cheat. So the best thing you can do is to build some utils yourself. In the end, this is just a bunch of math and is very platform-independent. So the only thing you really need are bounding boxes.

Bounding Boxes

A bounding box (also called a hitbox) is an area that will be represented by a minimal position and a maximal position. So when a player tries to hit an entity, it will calculate if the current RayTrace is inside our box. Sounds simple, right? Yes, it basically is. So at this point, you should have your own data for every entity, right? Great, now you should add a bounding box to your data. Now comes the not-fun part. There are many entities in Minecraft, and the newer the version, the more entities. Many entities have their own sizes. A normal player size is 0.6 in width and 1.8 in height. If you want your raytrace to work correctly when a player hits an entity that has a different size, you will need to set the size for every entity yourself. Sounds fun, and it is fun. You can check out an entity hitbox list on the Minecraft wiki page.

Lag Compensation

Alright, picture this: you're deep into a Minecraft multiplayer session, battling it out with your friends. Suddenly, you notice something weird happening - your movements don't quite match up with what's happening on your screen. That, my friend, is the magic (or not-so-magic) of network lag.

Lag compensation is like the game's way of saying, "Hey, we get it, the internet can be a bit wonky sometimes." It's all about making sure that even if your internet connection decides to take a nap, your Minecraft experience doesn't suffer for it.

So, why do we need lag compensation anyway? Well, imagine if every time your connection hiccuped, you ended up getting pummeled by a creeper you never even saw coming. Not fun, right? Lag compensation steps in to smooth out those rough patches, making sure everyone gets a fair shot at survival.

Now, how does it work? It's kind of like a crystal ball for the game server. It takes a peek into the future (well, sort of) and tries to guess where you'll be next, even if your internet is dragging its feet. This way, when you swing your diamond sword, the game knows where to send that creeper flying, even if your connection is playing catch-up.

But hey, lag compensation isn't perfect. Sometimes, you might still see a bit of weirdness - like players teleporting around or blocks mysteriously reappearing. It's all part of the magic of online gaming, right?

In the end, lag compensation is like the unsung hero of multiplayer Minecraft. It might not always get the spotlight, but it's there, quietly making sure your gaming experience stays smooth and (mostly) lag-free. So the next time you narrowly escape a creeper explosion, just remember to thank lag compensation for having your back.

Alright, let's talk about how a Minecraft anti-cheat could handle lag compensation.

First off, lag compensation is a bit of a double-edged sword when it comes to detecting cheats. On one hand, it's there to make sure players have a smooth experience, even if their internet isn't cooperating. But on the other hand, it can also be exploited by cheaters to gain an unfair advantage.

So, how can an anti-cheat system deal with this tricky situation? Well, it's all about finding the right balance between fair gameplay and keeping cheaters in check.

Currently, one common solution to check for "lag" are sending and receiving transaction Packets, These are basically just like Ping request to a Client. For Bukkit there are this Cool and Easy Library called Pledge there is a great YouTube video too

Reward system

This is similar but different as the Buffering system, One Big Anti-Cheat were you can perfectly see its Reward system is Intave. A Reward system as the name already says rewards a Player or punishes a Player, This is usually not like in the Buffering system per Check level the Reward level is usually a Global indicator how trustworthy a Player is, If a player gets often flagged and maybe their Client Brand is: Forge/Fabric, and they can easily load Mods that Modify their Client and gives them some Advantages your should punish the Player. You should integrate this system in your Checks, Players that are more untrustworthy should get flagged faster, and the Checks should be stricter. Many Anti-Cheat are summarizing untrustworthy player to for punishing them using...

Ban Waves

Ban Waves are a pretty smart idea, This is how it works: Like already mentioned above sometimes flags are doing nothing to a player, No setbacks, No Packet Blocks, No Inventory closes, just nothing, besides... Flagging Silent, So The Stuff Members getting a message, maybe the Trust score gets worse and then from a certain point the Player will be set to Ban for Later. Banned when the Ban Waves come. A Ban Wave is an Event were all Remembered People will be banned at the Same time. Why we should be doing that ?, and not just banning them directly when they're breaking the rules and may ruin the fun for others. This way the Cheater has no Information what exactly banned him, He has no idea what Module or what Setting is wrong and our Anti-Cheat is detecting, without knowing the cause they can't develop any Bypasses or tweak his Config.

Legit Cheating

In the past years Legit Cheating has become more and more popular, The idea is simple, Cheaters will not use any Obvious hacks like Fly, Scaffold, NoFall. Instead, they try to have only some minimal Advantages, so it does not Look very Suspicious, Most of the time these Cheats are:

  • Auto-Clicker
  • Range
  • Rendering Modules (e.g. ESP, Fullbright)
  • Velocity
  • Inventory Helpers

So what the Should do?

Auto-Clicker

Detecting Auto-Clickers is not easy, especially on the Server-Side, Because its most likely and external Software or Hardware simulating Clicks, At the Server-Side we are really limited detecting something like this, But there are still ways.

First you should calculate the CPS (Click per Second) for every Player You should do that for both Right & Left Clicks, At the Server-Side, we do that by counting Packets for example how many Attack Packets or Interact Packets a Player sends, Using that we can calculate an inaccurate Server-Side CPS, But it will be enough for now

  • The Simplest but yet effective way is to set a max CPS limit I know there are many Click Techniques out there which can you get a lot of Clicks, But there should still be a Cap, It will also keep the fun for new Players which do not know or can't perform these Techniques.

  • You should also check for Consistency/Pattern You should check if a Player does click in a Consisted Pattern, Feel also free to add a small Threshold, When you see that the Player's CPS always are looked between 18-20 CPS, This is a bit suspicious, You should add a Buffer and Reward system to this, And run deep analysis on every information you can get, the Frequency, Left/Right Click and more.

Range

For a Range check should use RayTracing to check the distance of an Interaction the Player had made. Keep this RayTrace check can be very inaccurate due to Lag or other Factors (See Lag-Compensation), The Grim Anti-Cheat Handled it well.

Rendering Modules

There is not much we can do about Client-Side Rendering, For a Better Protection against it, you will have to make a Client-Side Anti-Cheat, But there is a very good method to Prevent Storage ESP's (e.g. Chests, Shulkers, Furnace...). So these types of ESP mostly works very simple:

↳  Scan for loaded Tile Blocks in World
  ↳    Filter them (Chests, Shulkers...)
    ↳    Render an ESP Overlay at the Blocks Position (Thorw Walls)

So the Solution is to just load the Tile Blocks when a Player can see them, And if the Player can't see them unloading them, I haven't seen an Anti-Cheat doing that by default, Maybe because of the Performance impact, I found this System on a Minecraft Anti-Cheat test server called tree.ac

Velocity

For Velocity, I recommend to check Minecraft code and see how they Calculate the Velocity for an Player and then Check if it can be.

Here is for example how the Minecraft Client calculates velocity

if (target instanceof LivingEntity) {
  ((LivingEntity)target).takeKnockback((double)((float)knockback_level * 0.5F), (double)MathHelper.sin(this.getYaw() * 0.017453292F), (double)(-MathHelper.cos(this.getYaw() * 0.017453292F)));
} else {
  target.addVelocity((double)(-MathHelper.sin(this.getYaw() * 0.017453292F) * (float)knockback_level * 0.5F), 0.1, (double)(MathHelper.cos(this.getYaw() * 0.017453292F) * (float)i * 0.5F));
}

Inventory Helpers

Things like Inventory Manager/Cleaner or Chest-Stealer are often being Used to give an Advantage over other Player, What we should do?:

  • Calculate the duration it takes the Player needs to click a slot
  • How fast the Player Opens/Closes the Inventory after/before first Click
  • Check Click Patterns

Client-Side

Until now everything was just Server-Side, This means that our Plugins are just at the Server level and less Control over the Client and our Checks are bound to the limits of the Information the Client gives us. So Lets Start with a Client-Side Anti-Cheat. But before we start let's go over the Pros and Cons.

Pros

  • No Server Performance cost (TPS)
  • Much more Control
  • Much more Information
  • Prevent Graphical Cheats / Only Client Visible (e.g. ESP, FreeCam)

Cons

  • Client Performance cost (FPS)
  • Minecraft Client is Closed Source. This means you have to rely on an ModLoader, e.g. Fabric.

    [!WARNING] For Bedrock/Pocket it's written in C++, You have the option to reverse engineering it or to consider using the Much more powerful resource packs which are able to execute Code but are "very limited" in terms of Information our anti cheat needs.

But the biggest downside is definitely that every Player must install your Client from somewhere and then play with it, It is much less straightforward than with Server Plugins where the Player can just join a Server without any extra effort.

Kernel vs App Level

So there are many Client-Side anti-cheats out there (e.g. Easy-Anti-Cheat, VAC, BattleEye) & many more

Kernel Level

Most of the current good Anti-Cheats operating at Kernel Level. What does this mean?

Every Operating System (e.g. Windows, Linux, macOS) has its Kernel, That is the Base on which everything other in the OS is build upon, This Base Layer is also the most Powerful and has the most Permissions, It's also the Layer where you Drivers are running, When you're making your own Kernel Level Anti-Cheat it's also a Driver at the end. The big disadvantages is that every OS Kernel is different and that's means to will have to rewrite many things when you want to Support another OS.

Also keep in mind that a Kernel Level anti-cheat, Uses the Kernel, Which is usually written in low level languages like C/C++, Rust, I heavily recommend you to also use a Low Level language, Using some Bindings and Making Drivers in Java or Python is just the wrong way, Not to mention the Performance decrease.

App Level

An App level anti-cheat is embedded into your Software/App/Game and Allows better compatibility with different Operating Systems

You should also try to use a low level language when Developing an App Level Anti-Cheat.

I want to code my anti-cheat in a low level language, But how I should inject the Code into Minecraft's Java Code?

  • First export your anti-cheat as a native library file (e.g. .dll, .dylib, .so)
  • Then you can easily load the library in Java/Kotlin using System.loadLibrary("anticheat.so") (don't forget to replace the .so with your native format)

There is also a not so popular but really great Website which gives you all the common Tricks and Methods how to prevent "debugging" an Application, So basic forbidding Hackers to Inject code or generally interaction with our Client. Check it out

Last Words

So we are at the end. I hope you've got a basic understanding of everything, and your first check is working fine. If you have any questions, my Discord should be in my GitHub profile. Just keep in mind that the checks were just simple examples and that developing a full anti-cheat is really not easy. You will need good math skills if combat or movement checks are coming. Don't forget some additional functions too, like commands or maybe some fancy inventory GUIs (there you are free to use the server API). Good luck.

@Janmm14
Copy link

Janmm14 commented Oct 4, 2023

  • Official Minecraft Bedrock Server is written in C++ and has no API. Several community servers exist (for example PocketMine written in PHP, or Nukkit written in Java).
  • Netty does not split threads into reading & sending.

I am unsure whether this will help potential anticheat developers.
Some basic concepts are explained, but the actual check logic for any sort of competitive anticheat is far more complex and generally AC devs dont want to talk about the concepts, algorithms and other things they invented and invested weeks or months of time.

@Snowiiii
Copy link
Author

Snowiiii commented Oct 5, 2023

  • Official Minecraft Bedrock Server is written in C++ and has no API. Several community servers exist (for example PocketMine written in PHP, or Nukkit written in Java).

    • Netty does not split threads into reading & sending.

I am unsure whether this will help potential anticheat developers. Some basic concepts are explained, but the actual check logic for any sort of competitive anticheat is far more complex and generally AC devs dont want to talk about the concepts, algorithms and other things they invented and invested weeks or months of time.

  1. Im talking about PocketMine and its API, And the fact that PocketMine Plugins will be written in PHP.

  2. There are 2 Threads for Incoming and 2 Thread for Outgoing Data. Okay fine maybe I expressed myself badly

  3. You want that im going more in depth?, Fine

@Janmm14
Copy link

Janmm14 commented Oct 5, 2023

  • Official Minecraft Bedrock Server is written in C++ and has no API. Several community servers exist (for example PocketMine written in PHP, or Nukkit written in Java).

    • Netty does not split threads into reading & sending.

I am unsure whether this will help potential anticheat developers. Some basic concepts are explained, but the actual check logic for any sort of competitive anticheat is far more complex and generally AC devs dont want to talk about the concepts, algorithms and other things they invented and invested weeks or months of time.

  1. Im talking about PocketMine and its API, And the fact that PocketMine Plugins will be written in PHP.

  2. There are 2 Threads for Incoming and 2 Thread for Outgoing Data. Okay fine maybe I expressed myself badly

  3. You want that im going more in depth?, Fine

  1. I alredy mentioned pocketmine - I just wanted to make the statement more precise.
  2. No, netty does not split its thread pool into incoming or outgoing data. There is one thread pool for handling incoming data. Outgoing data will be scheduled by netty to urn on the ame thread pool, there is no pslit of threads into incoming and outgoing. (Netty allows to set an optional second thread pool for accepting new connections.)
  3. I just stated my opinion. I do not need a guide for anticheat programming. Do not feel pressured by it.

@Snowiiii
Copy link
Author

Snowiiii commented Oct 5, 2023

  • Official Minecraft Bedrock Server is written in C++ and has no API. Several community servers exist (for example PocketMine written in PHP, or Nukkit written in Java).

    • Netty does not split threads into reading & sending.

I am unsure whether this will help potential anticheat developers. Some basic concepts are explained, but the actual check logic for any sort of competitive anticheat is far more complex and generally AC devs dont want to talk about the concepts, algorithms and other things they invented and invested weeks or months of time.

  1. Im talking about PocketMine and its API, And the fact that PocketMine Plugins will be written in PHP.
  2. There are 2 Threads for Incoming and 2 Thread for Outgoing Data. Okay fine maybe I expressed myself badly
  3. You want that im going more in depth?, Fine
1. I alredy mentioned pocketmine - I just wanted to make the statement more precise.

2. No, netty does not split its thread pool into incoming or outgoing data. There is one thread pool for handling incoming data. Outgoing data will be scheduled by netty to urn on the ame thread pool, there is no pslit of threads into incoming and outgoing. (Netty allows to set an optional second thread pool for accepting new connections.)

3. I just stated my opinion. I do not need a guide for anticheat programming. Do not feel pressured by it.

Hey, I have added some more Content, what do you say?

@ethaniccc
Copy link

A mention for Gophertunnel could also be useful for MC:BE AC Developers, as it contains documentation for packets. See here.

To add on, MCP can still be useful for MC:BE AC devs working on movement detections, but there are some disparities such as the BE client clipping into stairs sometimes before proceeding upward, etc.

@Snowiiii
Copy link
Author

Snowiiii commented Oct 8, 2023

A mention for Gophertunnel could also be useful for MC:BE AC Developers, as it contains documentation for packets. See here.

To add on, MCP can still be useful for MC:BE AC devs working on movement detections, but there are some disparities such as the BE client clipping into stairs sometimes before proceeding upward, etc.

Looks usefull, I will add that, Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment