Test my anti-exploit

Didn’t really know where to put this, so I put this here. I have developed this within 8 hours and it protects against speed and fly hacks. I want to know how reliable it is when there are other people in the server. Thanks.

Link: Anti exploit showcase - Roblox

EDIT: Back up and running, basic commands to use are :ws and :fly
Improved violation handling.

3 Likes

Are we supposed to test the exploits ourselves?

There’s an admin script in the game which you can use.

Sorry, something broke. Bumping once fixed.

Unfortunately I was unable to join.
Error code 610, (Game’s root place is not active)

Did you try to fix only walkspeed exploit or all speed exploits (CFrame manipulation, body force etc)

The anti-exploit has unfortunately broke, I’ll bump the post once fixed.

2 Likes

He shut down the game when u were joining to fix a bug that caused the anticheat to not run sometimes

rip, the script glitched.

Should be fixed now… Hopefully.

It doesn’t seem to be working.

Not even after I’ve converted it to 1 player servers?

Well it works now.
How do you reference players to monitor their movement?

I’m not sure what you mean by that? I have a class which takes in the character as an argument which monitors it every frame

Do you iterate through the player list each frame?

Let’s talk in dm’s so we don’t flood the post.

If you want people to critique your anti exploit you should post the source so that actual possible security holes can be found.

If the response to that goes anything along the lines of “but it’s local” and “but it’s supposed to be obfuscated/hidden” then it’s not going to be effective at all in a real game and people will have no problems getting around it.

Thanks for the response! This is the server-sided anti-exploit class I wrote:

local HISTORY_LENGTH = 60

local AntiCheat = {}
AntiCheat.__index = AntiCheat
local RunService = game:GetService("RunService")

function AntiCheat:Kill()
	self._updateListener:Disconnect()
end

function AntiCheat.new(character, maxSpeed)
	local self = setmetatable({}, AntiCheat)
	self._character = character
	self._humanoidRoot = character:WaitForChild("HumanoidRootPart")
	self._maxSpeed = maxSpeed
	self._isStandingHeight = character:WaitForChild("Humanoid").HipHeight + 1-- The height which is used to determine if the root part is mid air or stood on a platform
	
	self._history = {} -- Keeps a history of all the positions that the humanoid root has been
	self._heartbeatDeltaHistory = {} -- Keeps a log of the delta between heartbeat events
	self._lastValidPosition = self._humanoidRoot.Position
	
	self._updateListener = RunService.Heartbeat:Connect(function(dt)
		self:_update(dt)
	end)
	
	return self
end

function AntiCheat:_update(dt)
	self:_updateHistory()
	self:_updateHeartbeatDeltaHistory(dt)
	
	local speedIsValid = self:_validateSpeed()
	local heightIsValid = self:_validateHeight()
	
	if speedIsValid and heightIsValid then
		self._lastValidPosition = self._history[#self._history]
	else 
		self._humanoidRoot.CFrame = CFrame.new(self._lastValidPosition)
	end
end

function AntiCheat:_updateHistory()
	table.insert(self._history, self._humanoidRoot.Position)
	
	if #self._history > HISTORY_LENGTH then
		table.remove(self._history, 1)
	end
	
end

function AntiCheat:_updateHeartbeatDeltaHistory(dt)
	table.insert(self._heartbeatDeltaHistory, dt)
	
	if #self._heartbeatDeltaHistory > HISTORY_LENGTH then
		table.remove(self._heartbeatDeltaHistory, 1)
	end
	
end

function AntiCheat:_validateHeight()
	-- Check if the character is standing on a plaform
	if not self._heightRaycastParams then
		local params = RaycastParams.new()
		params.FilterDescendantsInstances = {self._character}
		params.FilterType = Enum.RaycastFilterType.Blacklist
		self._heightRaycastParams = params
	end
	
	local halfX = self._humanoidRoot.Size.X/2
	local halfY = self._humanoidRoot.Size.Y/2
	local halfZ = self._humanoidRoot.Size.Z/2
	
	local raycastOrigins = {
		self._humanoidRoot.CFrame * CFrame.new(-halfX, -halfY, halfZ),
		self._humanoidRoot.CFrame * CFrame.new(-halfX, -halfY, -halfZ),
		self._humanoidRoot.CFrame * CFrame.new(halfX, -halfY, halfZ),
		self._humanoidRoot.CFrame * CFrame.new(halfX, -halfY, -halfZ),
	}
	
	local raycastDirection = Vector3.new(0, -self._isStandingHeight, 0)
	
	local isStanding = false
	for _, v in ipairs(raycastOrigins) do
		local result = workspace:Raycast(v.Position, raycastDirection, self._heightRaycastParams)
		if result then 
			isStanding = true
			break
		end
	end
	
	-- Are they in flight?
	if isStanding then
		self._inJump = false
		self._totalHeightJumped = 0
		return true -- They are valid
	end
	
	-- Find the total height covered so far:
	self._inJump = true
	if not self._totalHeightJumped then self._totalHeightJumped = 0 end
	
	local p1 = self._history[#self._history]
	local p2 = (self._history[#self._history - 1] or p1)
	local heightDifference = (p1-p2).Y
	
	if heightDifference > 0 then
		self._totalHeightJumped += heightDifference
	else
		return true
	end
	
	if self._totalHeightJumped >= 9 then
		return false
	else
		return true
	end
end

function AntiCheat:_validateSpeed()
	-- Get the average speed if the world was on a plane:
	local speedSum = 0
	local lastPosition
	for i, v in ipairs(self._history) do
		local p1 = Vector2.new(v.X, v.Z)
		if not lastPosition then lastPosition = p1 end
		
		local thisSpeed = (lastPosition - p1).magnitude / self._heartbeatDeltaHistory[1]
		speedSum += thisSpeed
		lastPosition = p1
	end
	
	local speedAverage = speedSum / #self._history
	local isValid = speedAverage < self._maxSpeed
	
	return isValid
end

return AntiCheat
2 Likes

This looks pretty solid. I would of course advise that it’s tweaked depending on the game it’s applied to due to variations in gameplay. Just looking at it I couldn’t say if there are any weird edge cases so you should also test for those, but other than that it looks like it should work.

In the future also consider the Code Review category for things like this.

1 Like