Hi, Tophat here.
I’m currently trying to make an anticheat system for my game.
Currently, all I need to do is to check for changes in the Humanoid’s WalkSpeed and JumpPower.
The problem is, exploiters can change it on their client, and will not be detected by the server. So, I’ll need to know if someone is changing values on their client.
I plan on using a LocalScript to detect changes. Now here me out, I KNOW NOT TO TRUST THE CLIENT. I will use a server script to check if the local script responds, using remote functions to check if the client responds. (I am following an unofficial book guide for this, search up “The Advanced Roblox Coding Book - An Unofficial Guide”) A server-sided script to check for changes has also been created as a second safety measure.
My main concern is if exploiters can EDIT LocalScripts, as in changing the code, deleting all lines of code on the script and subsequently making my efforts go into the bin. Or is my entire plan just going to fail from the start? Is there any better way?
Is it possible to reset a player’s walkspeed and jumppower on the server by setting up a loop that sets it back to default? Or does the client override that.
That sounds like a good idea, but what if the player got teleported or is falling at a fast speed? I’m going to have to make more arguments for those, I guess.
Oh, and also what are the cons of making a loop to reset values?
local module = {}
local subjects = {}
local function RemoveY(v3)
return Vector3.new(v3.X, 0, v3.Z)
end
game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
for root, info in pairs(subjects) do
local position = RemoveY(root.Position)
local lastPos = info.LastPosition
if (position - lastPos).Magnitude >= (info.Tolerance * deltaTime) then
position = lastPos
root:PivotTo(CFrame.new(position.X, lastPos.Y, position.Z))
end
info.LastPosition = position
end
end)
function module.Start(character)
local root = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")
local info = {
LastPosition = RemoveY(root.Position),
Tolerance = 0,
Connections = {}
}
local rootConnections = info.Connections do
local function CFrameChanged() -- the server set the cframe, we dont move them back
info.LastPosition = RemoveY(root.Position)
end
local function ToleranceChanged()
info.Tolerance = (root:GetAttribute("Tolerance") or 15) * humanoid.WalkSpeed
end
table.insert(rootConnections, root:GetPropertyChangedSignal("CFrame"):Connect(CFrameChanged))
table.insert(rootConnections, root:GetAttributeChangedSignal("Tolerance"):Connect(ToleranceChanged))
table.insert(rootConnections, humanoid:GetPropertyChangedSignal("WalkSpeed"):Connect(ToleranceChanged))
task.spawn(ToleranceChanged)
end
subjects[root] = info
end
function module.Stop(character)
local root = character:FindFirstChild("HumanoidRootPart")
local info = subjects[root]
if info then
for _, connection in ipairs(info.Connections) do
connection:Disconnect()
end
subjects[root] = nil
end
end
return module
I’m not sure if this is what you’re talking about, but I whipped this up. It’s not perfect or nothing, but It’s a relatively generous anti-teleport / anti-speed if you will, it doesn’t consider the Y axis though, so fly / hover hacks would still be possible
To use it, require it via require(pathToModule) and then call .Start and .Stop to start it, and stop it respectively. Pass a players character as the argument each time
I meant setting up a while true do loop that sets Humanoid values back to a specific number, such as 16, and the loop runs every 2 seconds. What would the disadvantages be for this?
Update: This doesn’t work at all, I’m afraid. Going to have to use positions and other methods instead.
EDIT: Figured it out, it was because it doesn’t change anything because the server sees the same value all the time when edited on the client. I fixed it by making the speed shift by 0.01 and back momentarily, enough so nobody sees a difference while allowing it to stop client-sided changes!
Ignoring that polling every 2 seconds and setting them to normal only stops them for a little bit (assuming they don’t just block the property change packet anyway) and is redundant;
What are you going to do if they move with physics, not humanoid properties? E.g they create a body position with an absurd force to go everywhere, by overseeing position overall (the thing i sent) rather than just humanoid properties, you can solve that and many more things.
This wouldn’t work, as an exploiter can just set their WalkSpeed to whatever they want as soon as you change it, or do it on RenderStepped. (That’d be if you are doing that on the server)
Or, if you’re doing that on the client, they could hook __newindex and prevent your script from setting the walkspeed altogether.
You’re better off verifying how fast the player moved on the server (don’t check for WalkSpeed, as changes on the client don’t get sent to the server), so check how fast the HumanoidRootPart moved, take ping into consideration, the WalkSpeed you expect the player to have, etc.
The server’s version of the WalkSpeed property can be used reliably to determine how fast they’re expected to be, but an additional Tolerance value like you mentioned is recommended for non-walkspeed game idioms
take ping into consideration
This cannot be used reliably to my knowledge, this would only hurt more than it helps. Assuming a perfect case, you can make it so the player can’t send a time in the future (meaning they can’t lower their ping) but what does it matter if they can delay the ping time all they want, and get a really really non-strict movement pattern due to ping compensation?
Ah, ####. I’m going to have to rework my teleports and jump-boosters in my game now. Find some method to oversee position while taking in consideration for teleports, jump boosters, falling, exploding on death, people with high lag and accidental flings. RIP any freetime I have.
Or you know what? I could just make a votekick and let players deal with exploiters themselves. I could just do that as a temporary fix as I try make a legitimate automated anticheat.
Thanks for the info though, now I know so I don’t spend too much time on an easily bypassable script.
You can do what my anti-movement thing did, detect when the server itself changes the CFrame property and then reset the last position, which essentially will tell the server to shut up about movement for the current frame.
But yes, dealing with all those cases is more than it’s worth most of the time. You can just make the tolerance value absurdly high, or foregoe it entirely. Up to the creator
Yes, tolerance is definitely something that should be implemented, otherwise the slightest lag spike / unexpected movement caused by physics is gonna end up messing with all the players, however, as for WalkSpeed, you can use the server one if and only if you do all your WalkSpeed stuff on the server, but most people do sprinting, crouching, and other stuff that’d affect WalkSpeed on the client, and, since the server doesn’t really know when WalkSpeed updates (unless you specifically implement an event for that), there’s really no way to know what the player is doing or if they’re expected to go slightly faster than normal.
As for ping, it can’t be modified afaik, no exploits can mess with the value returned by Player:GetNetworkPing() since that’s calculated on the server.
Yes, they could use some form of lag switch, latency can’t be fully trusted, but it should definitely still be taken into consideration when calculating tolerance in case a player is genuinely lagging.
For anyone reading this topic and for any lurkers in the future, here are a handful of most clientside physic exploits and a solution on how to stop them from doing it.
Physic exploit 1: Changing their walkspeed. Solution: Every heartbeat frame, check their magnitude of their current and previous position. If it’s higher than their walkspeed (you’ll need to create a mathmatical formula to get these numbers) then send them back to their original position.
As a bonus, this also stops exploiters from teleporting.
Physic exploit 2: Noclipping Solution: Every heartbeat frame, send a raycast starting at their previous position and their current position. If they go through an object, send them back. For performance beneficial reasons, you can get objects in a 10 stud radius using OverlapParams and only check if they go through nearby parts instead of the whole game.
Physic exploit 3: Flying/Jump exploits Solution: Be warned, this is definitely the hardest physic exploit to prevent. In short, you have to constantly check what the player is standing on every heartbeat. If they aren’t standing on an object for an x amount of time (you’ll have to factor in jump power and gravity) then reset them to their previous position.
Also, when you send a player back to their position, I suggest also taking away NetworkOwnership of their HumanoidRootPart. This means the player can’t teleport back, walk through clientside objects, etc, for a short amount of time (Until you give it back).