How to Prevent Speed Hacking

Intro

So, lately many people have been asking how to detect and stop speed hacking. I would like to help everyone by teaching them how to make a basic speed hack prevention system!

Here is the script we will make. Feel free to use it in your projects and edit it as you please:

Let’s get started!

How do players speed hack?
Before we can stop speed hackers, we need to know how they speed hack in the first place! The truh is, there’s no single way people do this. The most basic hack could alter the player’s WalkSpeed, and more advanced hacks can change their FPS. This means we need a way to detect ALL speed hacks, not just a specific method of doing it.

How to detect speed hackers
The best way to detect a speed hacker is to check their location on the server. If a player seems to be moving too fast, we stop them. We cannot rely on properties like WalkSpeed or Velocity to catch a hacker, but we can compare their positions to see if they’re cheating! Don’t freak out if that sounds too complex for you, because it’s actually really easy.

The script
To begin, place a script into StarterCharacterScripts. Let’s add some variables first:

local maxspeed = 22 --The maximum distance the player can travel within a check time. Try to keep this higher than 16.
local checktime = 1 --How many seconds we wait before we record their speed.
--If we make checktime too fast, like ".1", the script will be recording positions too fast and thus will fail to work correctly!

local root = script.Parent:WaitForChild("HumanoidRootPart") --Get HumanoidRootPart so we can keep track of the player's position.
local lastcf = root.CFrame --We will store the player's CFrame so that we can move the player there if they seem to be hacking.

Note the values for maxspeed and checktime. They need to stay somewhat proportional to each other, or we might get false readings!

Also note how I didn’t make maxspeed 17, directly above the default 16 WalkSpeed. This is because we might get false positives from slower players who are still moving at 16 WalkSpeed but appear to be moving slightly faster to the server.

Now, for the actual checking, add this beneath your variables:

while wait(checktime) do

   if math.floor((Vector3.new(lastcf.p.X, 0, lastcf.p.Z) - Vector3.new(root.Position.X, 0, 
root.Position.Z)).magnitude) > maxspeed then --We compare the length of the last known position, lastcf.p, with the current position, root.Position. If it's greater than maxspeed, they're hacking.

      --See how we ignored the Y axis in the calculation? Also notice how we rounded on the line above? This prevents an inaccurate reading.

      print("Hack detected for "..script.Parent.Name.."!")

      root.Parent:SetPrimaryPartCFrame(lastcf) --The player is hacking! Because this could be a false positive, we DO NOT KICK THE PLAYER. Instead, we peacefully move them back to their last known valid position!

   end

   lastcf = root.CFrame --We store the player's position so we can see if they're hacking later!

end

It looks messy here, but I will decipher it line-by-line so you can understand what is going on! First:

while wait(checktime) do

We use a loop that waits checktime. This is how often we will compare the player’s position.

if math.floor((Vector3.new(lastcf.p.X, 0, lastcf.p.Z) - Vector3.new(root.Position.X, 0, root.Position.Z)).magnitude) > maxspeed then

The actual logic! We are comparing the distance of the last known CFrame of the player to their current position. We only read the data we have on the X and Z axes so we don’t get a false positive when the player falls. We then round our results with math.floor so that we don’t get weird numbers like 16.4555~ or something. Lastly, we check if that value is greater than maxspeed. If it is, we might have a hacker on our hands!

print("Hack detected for "..script.Parent.Name.."!")

This will print the name of our potential hacker…

root.Parent:SetPrimaryPartCFrame(lastcf)

Instead of kicking the player like most anti-hax folks would be tempted to do, we just move them to the last good position they were in. This will prevent any rage you might get from false positives.

lastcf = root.CFrame

Lastly, we actually record the player’s CFrame! Very simple.

Caveats
Unfortunately, using this method to detect speed hacking does come with a few drawbacks:

  • It can generate false positives on slow connections. This is why we do not kick the player if the are detected hacking!
  • It prevents teleportation. To get around this, disable the script for a few seconds while legitimately teleporting players.

Outro
If you want to make your own anti-hax, now you know where to begin! If you need help, just ask. Happy anti- hacking!

96 Likes

How would players be able to alter speed if FE is on? Any changes they make like that wouldn’t replicate to other clients, correct?

2 Likes

the client has full control over their own characters physics, so say they move their character 1000 studs per second, that will replicate.

3 Likes

A system that uses a single datapoint like this is going to have a potentially unacceptable degree of false positives that could result in game-breaking unnecessary “corrections” in the case of something like an obby. There are also some subtle problems with this particular implementation. For example, wait(1) doesn’t wait exactly 1 second, not with enough reliability to assume 1 second as the denominator of a velocity calculation. Unfortunately, it is going to often be more than one second, so the error will be in favor of a false positive detection. It would be better to divide the distance traveled by the timestep argument supplied to Heartbeat.

The biggest source of false positives though is just going to be internet latency. Clients control their own characters locally, with the resulting positions replicating to the server. Because the amount of variation in time for the message to go from client to server is both unpredictable and large relative to the duration of 1 frame, a player with a slow internet connection can easily be seen by the server as moving in jumps that correspond to higher-than-normal character movement speed. You really need to take a lot of samples and compute an average velocity in order to avoid regular false positives just from irregular replication intervals (“lag spikes”).

Lastly, you have to consider that non-exploiting players can be subjected to faster-than-move-speed corrections by the physics simulation. Consider the case where a character’s physical interaction with something “flings” them. The code above, which does not zero out their root part velocity, can trap them in this loop where they vibrate rapidly because of constant CFraming back to some fixed point.

68 Likes

I thought about a lot of that. In the end this is only a tutorial showing how to get started with something like this. I just wanted to show something basic you could do to get started :slightly_smiling_face:

Any improvements are always welcome!

7 Likes

I’m going to be honest here there are quite a few things wrong with this., it will just teleport players back to their last known position and on slow connections that will just keep glitching them back, similar to the way Minecraft handles its lag.
I think the best way to do it would be

  1. Detect the hack
  2. Log it somewhere (On a discord webhook, etc)
  3. Allow for manual review, like what the game Parkour does.

This system, although it would take more resources would allow for a more player-friendly approach to hacking, temporary bans could be issued until reviewed but it could also result in a loss of players.

EDIT: Sorry for the necropost, never realised the date.

2 Likes

There’s a problem with that first suggestion.
1). Hacks aren’t able to be detected by developers due to security, unless they make it obvious and put an execution script in ScriptContext like RC7 did at one point.

What I do personally, is just check in a LocalScript for a Humanoid change, and if their walkspeed is altered to above say 50, it fires a remote that deals with the unauthorized change.

3 Likes

That’s bad way to handle speed-hacking due to:

  1. Clients being able to delete/modify localscripts
  2. Walkspeed not being only way to speed-hack

Better way would be to track Velocity on Server.

2 Likes

Your approach has a flaw. Exploiters can delete the script, disable the remote event, etc.

2 Likes

There’s a check on the server for that script. Deleted = Instant kick
And I did have a velocity check but not a lot of people are smart enough for it as they mostly just change their walkspeed.

4 Likes

The script still exists on server as far as the server is concerned. Also, it’s possible to modify game’s metatable to prevent remote calls, send false data, get false data, …

Also contradicts the best tip for preventing exploits: “Don’t trust the client”

8 Likes

This isn’t a thread to argue about the best way to prevent speed hacking. It’s literally just a basic system to help people get started with preventing it.

5 Likes

Velocity probably isn’t the best to work with on the server since if you get flung it will false detect you. Unless you are sure you wont ever get launched, this isn’t the best idea. (It would work, it would just be risky)

3 Likes

True but it’s started an interesting conversation :stuck_out_tongue:

5 Likes

You got me there

2 Likes

Yes, but it doesn’t mean that you’ll ban the player as soon as velocity is something it’s not supposed to be.
You should log position and velocity, if velocity exceeds limits, set it to 0 and put the player back.

This is a good tutorial to learn how you can prevent exploiters but it’s flawed. As stated in the OP, the server can never know the player’s exact location due to latency which can result in false detection. (and if the player gets flung which I guess isn’t really that bad of a thing) Also, if a player were to delete their HumanoidRootPart, the check would be rendered useless and would result in the server erroring.

1 Like

Couldn’t you use the Running event of humanoid server side for WalkSpeed exploits?

humanoid.Running:Connect(function(speed)
    if speed > 17 then -- speed could be 16.0059483446
        -- stuff
    end
end)

And please don’t call wait in your loop’s condition.

4 Likes

That seems like it would work, but Im pretty sure that it would give you false positives when you are falling or get hit by a bus

1 Like

Maybe. But you can do a higher number like say 20 and for that bus I’m pretty sure you would go flying. Not running. But you can do additional check like the rootPart.Velocity.Magnitude

1 Like