Making a good speed hack detector

You know what, I’ve done some testing and I’ve actually made it pretty alright.
Now this was fully tested in Studio, I highly recommend you test it yourself in your own game but even when changing the player’s state on the client (yes, it does replicate - I tested to make sure) and the server gives lenience of 100 studs per second (I check every .4 seconds so it translates to 40 studs on the Y axis every interval if the player is falling).
I added a Speed Gain feature to the module and tested with a +100 stud conveyor, and it works really really well!

So this is the new module for it.

local SpeedhackDetector = { tracking = {}, whitelist = {}, additionalSpeeds = {tags = {}, speeds = {}}, lastCheck = tick() }

local XZ = Vector3.new(1, 0, 1)
local Y = Vector3.yAxis

function SpeedhackDetector.Add(character)
	SpeedhackDetector.tracking[character] = character.PrimaryPart.CFrame
	
	character.Destroying:Connect(function()
		SpeedhackDetector.tracking[character] = nil
	end)
end

function SpeedhackDetector.WhitelistCharacter(character, duration)
	local tag = tick()

	SpeedhackDetector.whitelist[character] = tag
	
	task.delay(duration, function()
		if SpeedhackDetector.whitelist[character] == tag then
			SpeedhackDetector.whitelist[character] = nil
		end
	end)
end

function SpeedhackDetector.BlacklistCharacter(character)
	SpeedhackDetector.whitelist[character] = nil
end

function SpeedhackDetector.ChangeSpeedGain(character, speedGain)
	SpeedhackDetector.additionalSpeeds.speeds[character] = speedGain
end

function SpeedhackDetector.IncreaseSpeedGain(character, tag, speedGain)
	if not SpeedhackDetector.additionalSpeeds.speeds[character] then
		SpeedhackDetector.additionalSpeeds.speeds[character] = 0
		SpeedhackDetector.additionalSpeeds.tags[character] = {}
	end
	
	if SpeedhackDetector.additionalSpeeds.tags[character][tag] then
		return
	end
	
	SpeedhackDetector.additionalSpeeds.tags[character][tag] = true
	SpeedhackDetector.additionalSpeeds.speeds[character] += speedGain
end

function SpeedhackDetector.DecreaseSpeedGain(character, tag, speedGain)
	local current = SpeedhackDetector.additionalSpeeds[character] or 0
	
	SpeedhackDetector.additionalSpeeds.tags[character][tag] = nil
	SpeedhackDetector.additionalSpeeds.speeds[character] = math.max(current - speedGain, 0)
end

function SpeedhackDetector.Update()
	for character, lastCFrame in SpeedhackDetector.tracking do
		local humanoid: Humanoid = character:FindFirstChild("Humanoid")
		
		if not humanoid or SpeedhackDetector.whitelist[character] then
			continue
		end
		
		--// Did a test, terminal velocity of jumping is roughly ~1.063*jumpPower.
		--// I tested on different masses, gravities, jump powers, etc. All gave this result.
		local distancePerSecond = humanoid.WalkSpeed * 1.1
		local jumpDistancePerSecond = humanoid.JumpPower * 1.063 * 1.1
		
		if humanoid:GetStateEnabled(Enum.HumanoidStateType.Freefall) then
			jumpDistancePerSecond = 100
		end
		
		local speedGain = SpeedhackDetector.additionalSpeeds.speeds[character]

		if speedGain then
			distancePerSecond += speedGain
			jumpDistancePerSecond += speedGain
		end
		
		local maxDistance = (tick() - SpeedhackDetector.lastCheck) * distancePerSecond
		local maxDistanceY = (tick() - SpeedhackDetector.lastCheck) * jumpDistancePerSecond
				
		local currentCFrame = character.PrimaryPart.CFrame
		
		local compLast = lastCFrame.Position * XZ
		local compCurrent = currentCFrame.Position * XZ
		
		local compLastY = lastCFrame.Position * Y
		local compCurrentY = currentCFrame.Position * Y
		
		if (compLast - compCurrent).Magnitude > maxDistance or (compLastY - compCurrentY).Magnitude > maxDistanceY then
			print("Player exceeded max distance")
			character:PivotTo(lastCFrame)
			SpeedhackDetector.tracking[character] = lastCFrame
		else
			SpeedhackDetector.tracking[character] = currentCFrame
		end
	end
	
	SpeedhackDetector.lastCheck = tick()
end

return SpeedhackDetector

And I added a folder that contains items that will speed you up by touching it, but the API supports anything.

local function InitializeSpeeder(speedPart)
	local gain = speedPart:GetAttribute("SpeedGain")
	
	speedPart.Touched:Connect(function(h)
		local c = h:FindFirstAncestorOfClass("Model")
		
		if c and c:FindFirstChild("Humanoid") then
			SpeedhackDetector.IncreaseSpeedGain(c, speedPart, gain)
		end
	end)
	
	speedPart.TouchEnded:Connect(function(h)
		local c = h:FindFirstAncestorOfClass("Model")

		if c and c:FindFirstChild("Humanoid") then
			task.delay(1, SpeedhackDetector.DecreaseSpeedGain, c, speedPart, gain)
		end
	end)
end

for _, speeder in workspace.SpeederUppers:GetChildren() do
	InitializeSpeeder(speeder)
end

We can walk on the conveyor
https://gyazo.com/036d1cf6e741b772ae058c0d64d7a046

But not speed hack!
https://gyazo.com/fa8f3d14aa2317e81f324a497f8c804c

This is still not perfect though, exploiters can use around up to 110 Jump Power before being detected.
And this doesn’t address the velocity you get when jumping off a Truss.

Simulating 400 ping- it works well :slight_smile:
https://gyazo.com/ae185df197f3e15683c3bed28ba61e35

1 Like