Hexolus' Server-sided Anticheat

NOTICE

The parent project, Hexolus, is discontinued as well as the anticheat itself. As for the rewrite I mentioned below, I currently have other stuff going on right now and I can’t make promises on anything.
This anticheat is getting replaced pretty soon by a very big rewrite I’m doing. The new anticheat version will go by the name Polygon, its not really ready for use yet but it should offer much better performance for very large servers and will improve a lot of issues, especially organization.

Anticheat

NOTE: The anticheat itself is not called Hexolus, that’s the name of the game that it was originally created for, seems to be common that people confuse that. You should read the FAQ section on the GitHub page, you can get to it below.

Hello! :smile:
I’ve started to open source some of the bigger features in my ongoing project, Hexolus.

It will be quite a while before most things in the game become open source, and I likely won’t be open sourcing the whole game, but I will eventually open source most of the big fundamental pieces of code in the game.

These will include the game’s structure placer, which is partially available in its own repo, its entity management system, which isn’t available yet but will be as time goes on, the code sandboxing system the game will use, and likely much more.

Quickstart

  1. Go to the github releases
  2. Download the latest release or pre-release
  3. Insert it into your game somewhere under ServerScriptService, it doesn’t really matter where
  4. If it might impact your game, test potentially incompatible features, and disable or adjust incompatible checks/thresholds

The releases can be found here:

Features

The anticheat for the game includes several server sided checks for common movement exploits which act as a filtering enabled for physics, and it has been fine tuned based on my article on anticheat development.

I’ve been working on improving it for several months and continue to make frequent improvements to its stability, compatibility, and the range of exploits it protects against.

The anticheat has the following (stable) checks within it and more to come, without any client code, or any reliance on the client at all:

  • Speed, vertical and horizontal separately
  • Teleportation, vertical and horizontal separately
  • Noclipping
  • Tool deletion (Deleting tools on the client)
  • Invalid tool dropping (Dropping non-CanBeDropped tools)
  • FE Godmode (Humanoid deletion)

The anticheat will eventually fully support the following checks:

  • Flight, anti-gravity, no gravity, high jump, etc
  • Accessory dropping/deleting
  • Humanoid state checking, and explicit compatibility with existing checks (Air climbing/air swimming, pseudo-invincibility, air jumping, ladder speed, etc)
  • And much more

And, of course, an enormous thank you to @grilme99 below for their explanation of how Changed events work for CFrame/Position. This allowed the anticheat to support server side teleportation, and scripted physics updates to velocity, which would have been impossible without their help.

How does it work, and how good is it at stopping exploits?

Every physics tick it uses simple physics prediction to make a “guess” at where the player should be based on physics information from the last physics tick. Then it makes measurements to decide if the player has violated the prediction enough to be a concern.

If the anticheat deems the player’s movement to be invalid, the anticheat will bring them back into a valid area, and will then try to do a simple prediction for the client’s next behavior to help account for rubberbanding. This results in a almost completely seamless experience for players, especially in games which don’t rely on complex physics.

To make things better, because this happens every physics tick, no lag back, stuttering, etc will appear for other players or your scripts when a player is trying to exploit. If they are noclipping, they will appear as if they are running in place against the wall, if they are speed hacking they will appear to be moving at their normal WalkSpeed.

Use in your games

Hexolus’ anticheat is designed to be a drop-in. Its purpose is to be easy to implement, smart enough to avoid compatibility issues, and, most importantly, should have as little negative impact on your players as possible while still stopping exploits.

The anticheat currently works best for simple games that don’t customize physics much, such as minigames, though, over time I am improving compatibility, and reducing its impact on players even in places where it would be considered minimal. It is also licensed under CC0, which means you have the ability to do anything with the code you like with no extra strings attached.

Github repositories

As I open source more code this post will be updated to reflect changes.
You can find the anticheat repository with the latest releases here:

I encourage you to create issues on the repository and otherwise contribute if you have any compatibility concerns with the anticheat.

Additionally, you can find the other components I open source in this “hub” repository, which will always contain the latest stable versions of each repository:

101 Likes

This is a great resource! My only issue with it is the structure of it; it doesn’t feel ‘ergonomic’ having all the code in one file. Maybe split it up into separate modules?

2 Likes

I haven’t coded an anti-cheat myself, but I can imagine that it would be difficult to modularize a script that has a lot of interrelated internal states tracking players. You could pass these states between modules or have a container module be single source for grabbing these states, but for around 400-500 lines I don’t think it would necessarily be beneficial to split everything up. At that point I think it is a subjective difference but again, I’m not too experienced. I figured I’d share my viewpoint.

1 Like

There is actually a funky trick with the way that CFrame properties replicate to detect when the server teleports the player. This should allow you to get around the teleportation caveat and is what I use in my server-side anti-cheat.

As you may know, CFrame or Position changes do not fire any Changed events due to the rate that they change (physics). However, setting an objects CFrame does. This means that you can listen to when the CFrame of the players root part is changed by the server with a simple GetPropertyChangedSignal and stop the anti-cheat from teleporting them back.

Take the following code as an example:

Character.HumanoidRootPart:GetPropertyChangedSignal('CFrame'):Connect(function()
    print('Player teleported by the server')
    -- This is where you would stop the anti-cheat teleporting the player back
end)

while wait(5) do
    Character.HumanoidRootPart.CFrame = CFrame.new(0, 10, 0)
end
18 Likes

I actually agree with you there, the code is not as organized as I want it to be. The way its set up to run outside of my game is a little hacky. Right now my priorities for my game are related to my next update I think, but, most likely I’m going to want to do a big restructure of the anticheat between the next update and the one after.

@tralalah You’re also pretty on point there with why I didn’t originally break it up into modules.
Most likely to make it modular would add a ton of benefits to the anticheat in terms of both organization and editability but a good structure to do so would be difficult to design. I am thinking that likely each check will exist in its own module with its relevant settings and will be passed the necessary data from the main module whenever its invoked (for example, the physics data). This is pretty much how a lot of my game works, every module is independent in a way, but each module “talks” to a main module.

It’s actually really important that I do that as well, because the game is designed to be able to load an entirely new version of itself at runtime, and then in the future I need it to be able to easily convert its data into newer formats (and actually, it already can load new versions at runtime which is implemented in the form of a few commands for changing server flags/switching versions, and eventually this’ll become automated updating of running servers)

A lot of that is very fundamental to some of the features I have plans for in very long term development, one of my really ambitious goals is to make persistent (technically they’re not truly persistent) servers that can seamlessly transition between versions while you play.

@grilme99 That is absolutely INCREDIBLE news and I can’t believe I never knew that. That’s going to be an enormous help! (Now I’ve gotta implement it immediately)

5 Likes

Don’t you think open-sourcing an anti-cheat is just pointless? Couldn’t the exploiters just look at a code and find a loophole?

Absolutely! This is actually really part of why I have open sourced my anticheat. People who find issues with it can report them. The same thing applies to all open sourced code, its easier for issues to be located, and, either taken advantage of by someone malicious, or improved upon by someone non malicious.

Because the anticheat is entirely server-sided and relies on no client-side behaviour beyond humanoid states being used to guide it a little in some areas, exploiters have no control over its functionality like they would if it were on the client. Everything is state based, and an exploiter has no control over the state of the anticheat beyond what the code explicitly allows for. The reason that I feel safe with the code being completely public is that there are no easy loopholes like there are with something client sided. Loopholes you find will be areas outside of the anticheat’s coverage, and will generally be possible to prevent in the future.

My methodology is essentially prevent as much as possible and never punish users. There are benefits to this and caveats, the caveats obviously being that exploiters can continue with whatever exploits they find, but, I think that the benefits in this case outweigh the negatives. My anticheat’s focus in my game was essentially to act like FilteringEnabled. Filter out bad behaviours. This means that false positives are okay as long as they aren’t hurting user experience at all, and is part of why, for example, the speed check is completely unnoticeable by someone who accidentally triggers it.

All I do is effectively correct for bad movement. For example, if the player is moving too quickly, I just slow them down in that frame and instead move them to where I expected them to be. The loopholes in the anticheat are entirely defined by what the anticheat does not look for, or what it explicitly allows for, and, every update to my game I make improvements to the anticheat that cover more areas.

The anticheat is essentially not meant to be perfect, its only meant to cover as many areas as it can and prevent as much as possible in the least intrusive way I can come up with.

7 Likes

Any benchmarks? How would it perform with an open world game with a Jailbreak-sized map of let’s say about 20 people? (Not that I’m going to use it, I’m just curious)

I’ve not run any benchmarks yet, but, I have rough estimations of how this may effect performance. In the future I’ll be optimizing it far more, probably when my game is more complete.

Overall, it loops over each player in the game and does each check each frame, and there are no sub loops. So, the time complexity is pretty much linear. As for how complex the calculations actually are, 20 people would likely not even be taking more than a ms or two of frame time, and likely would be taking less if I had to guess. The most expensive things I do are raycasts, and I do raycasts for the flight check (which is disabled by default since it behaves very undesirably for most games, and on top of that is too leniant), as well as the noclip check. The speed and teleportation checks use Magnitude and some extra math. Overall Vector3s are interacted with quite a lot, which can have some bad effects on performance in high quantities, but, I follow some code practices which do help. (Particularly making sure to reuse variables whenever I can make it possible)

You should expect to see performance issues becoming more noticeable at probably around 50 players or more, and likely will cost around 4-6 ms or so at most, which would probably, just based on some educated guessing, bring your server performance down to 53-57 tps on its own.

I’ll eventually do benchmarking and optimization when I feel the anticheat is more polished structurally, at the moment I think the mechanics are quite polished, but the code structure itself is not so polished.

2 Likes

Is there a way to exclude it for some players, like admins? If not, can you add that? Sometimes I fly in my own games

3 Likes

Yeah, that’s absolutely possible to do. You can just add an if statement inside the player loop so that the checks only run for the players who pass the check. That means you can make non admins just not pass the check somehow. Here’s how I might do that:

local whitelisted = {
    [userId] = true
}

-- In player loop
if whitelisted[player.UserId] then
   continue -- Skip this part of the loop since they're an admin
end

Depending on how your game’s admin works, it might actually allow you to fly perfectly fine as long as the admin code is updating your CFrame server sided and the client isn’t the one doing the flight stuff. The flight check at the moment is disabled by default as its rather buggy, and because of that you shouldn’t enable it, so, you shouldn’t have any problems with getting sent back to the ground or anything like that as long as you haven’t enabled it.

5 Likes

I have a question, does your anticheat support custom flagging?

What do you mean by that? Do you mean implementing custom checks? If so at some point I’m going to add a more official way to do that but at the moment you can just add your checks in the main section.

Custom punishments/repurcussions.

Ah. Well, actually, my anticheat doesn’t punish players which is actually completely intentional.

I use the mindset that punishing the player is bad if there can be false positives, and false positives are okay as long as it doesn’t detriment the user. It’s extremely hard, actually impossible in a multiplayer environment, to make an anticheat that’d be perfect enough to never have any false positives, so, I take an approach of sort of embracing that there will be false positives. The anticheat is designed in a way where its very effective at preventing a lot of movement exploits altogether without effecting the user at all under normal conditions. Speed, teleportation, and noclip (even non exploit kinds) are the biggest three, and the three the anticheat is most effective at stopping.

For example, for speed and teleportation checks, I try to place the user where I expect them to be if I think they made an invalid movement. Since this happens each server tick, and thus every time physics occurs, other players and server code will work completely normally even if they’re trying to exploit, and things will look completely normal to other users (its effectively completely prevented). If they teleport, it will appear like they’ve simply moved forward a little, if they speed hack, it will look like they’re walking completely normally other than a little stuttering, and if they noclip, they’ll appear like they’re just smooshing their face on the wall.

It might sound like it can’t really be effective, because what’s stopping exploiters from continuing to exploit? And the answer to that is really just general game security.

As long as your game is properly built, your remotes are set up correctly, etc, you should really never have to worry about exploiting at all. The concept is the same as filtering enabled, but, instead of filtering changes the client makes to certain objects, I’m filtering movements the client makes to their character.

Just like filtering enabled, what is not explicitly disallowed can absolutely be abused, and just like filtering enabled, even if the client is trying to make changes that aren’t allowed, they’re not punished for it.

2 Likes

Yes, you have a point there. However, my opinion tends to differ slightly. With enough programing skills (and a bunch of time), one can make an anti-exploit with nearly no false positives. Which would make 99% of the bans completely accurate. While the 1% can actually appeal to a moderator or etc.

My viewpoint is, if an exploiter gets a weak punishment from anti-exploits. It will allow them more time to bypass the exploit. So I prefer banning as a way to deal with possible exploiters. Although realistically, I normally make a discord webhook that notifies me of the username, so we can go to the exploiters server and deal with it appropriately.

I suppose you’re technically correct, but, since my anticheat is entirely server sided, there’s not really any easy “bypass” its more so just finding what isn’t checked for and working from there.

And, while this is not really any sort of proof that that is the case, I’ve also had a couple friends help me pentest the anticheat and so far we’ve not been able to find anything we can abuse.

1 Like

This isn’t how my anticheat works, what you are describing isn’t the speed prevention, its simply an extra. On top of that, a loop like you are describing is not nearly fast enough, you need your loop to be synchronized with network changes (which currently doesn’t exist, but is coming as PreSimulation), otherwise you need a loop running at at least 180 tps (3 * client fps) to get around it ~100% of the time (2 * client fps will work about 60-80% of the time iirc).

Running a loop (even on Stepped/Heartbeat which should be synced with physics) will not quite work since the speed is getting updated by the network, and, you aren’t currently able to run code immediately after this happens. The effect is that, regardless of how many loops you set, or what events you use for sync, you will never ever be able to perfectly sync WalkSpeed with physics. The effect is that their speed stutters every frame or so and effectively averages out to some value very close to their WalkSpeed depending on how many times the change made by the exploiter succeeds.

As I mentioned before, I believe the new PreSimulation even that will be coming out soon will be the first time this is possible. The sync code is basically just a very simple, very low cost way to break most “speed hack” scripts you’ll find off Google, and, its unlikely that someone without knowledge on what they are doing will be bale to get around this. (The reason for this is because Velocity is updated using WalkSpeed before your Stepped code will run, meaning if you really want to get around this the only consistent way is to update Velocity on Stepped)

However, regardless, this behavior inside of the anticheat is simply an extra to make it more annoying for an exploiter to speed hack by trying to force the WalkSpeed to stay synchronized and mess up behavior, its intended purpose is not to prevent speed hacking and this is not related to the speed check in the anticheat. The actual speed check itself is completely unrelated and works as intended with or without the sync code.

What you are describing with punishment is impossible to do in a networked physics scenario without false positives occurring, if you want to punish players for detections, you should do that yourself, but, there is a very good reason I do not want to implement this into my anticheat for physics exploits.

You will always end up detecting players as cheating no matter what you do. There is no way around this without the ability to accurately monitor and account for network speeds, which you can’t do as they can be spoofed quite easily. If a player is lagging, or their physics are a little desynchronized, e.g. from throttling, or their internet is slow, they likely won’t even know they are getting flagged, but they might end up randomly being punished because of these false positives.

The only way to make this more reliable is to add a ludicrous amount of additional threshold to reduce detections due to smaller discrepancies, but, in my opinion this defeats the purpose of the anti exploit and still doesn’t solve the problem.

Here is an example scenario. Say that a player’s internet freezes up for a moment, say 4 or 5 seconds (this happens often and you might not even notice). The player will have moved approximately 80 studs with a default WalkSpeed in this time, and, to the server this will appear as if the player has teleported about 80 studs in a single frame. This is a ludicrously high amount for a player to move under normal standards, but, this sort of thing happens all of the time due to network instability or server instability. If the server itself is lagging this can happen too.

4 Likes

Ok yeah I get your point. It worked in studio so I just assumed exploiters could exploit it.

I have updated the main post with a new link. I made a change last night to the repository, separating each feature into a separate repo and adding them as sub modules in the main repo, turning it into a sort of “hub” repo.

My intent is to keep the main repo updated with the latest stable commits to each feature. Likewise, those will correspond to releases on the github page, that way the main repo is never out of date with the latest stable release.

Additionally, since I last updated this post, the anticheat has seen a lot of big improvements to compatibility and its overall effectiveness thanks to the members of my game’s Discord server and some contributors on the github repo who have all been very good at helping me find areas of improvement and various compatibility issues, bugs, etc.

The anticheat is close to receiving a full overhaul, modularization of checks for ease of use and easier modification, and a first fully fledged release. I have a lot planned.

2 Likes