Hey there! I’m attempting to create a server side anti-cheat to prevent teleportation and speed exploiters. After about a day of trying different things, this works. ‘sort off’ It has MANY false positives and in particular an issue where after a player is ‘unfrozen’, the anti-cheat continues to rubber-band them back to their previous position unless they stand still for a few seconds. I’ve printed the distance values during these frequent episodes and they can range up to the hundreds which is particularly strange since there is no way the player had managed to move that distance within a frame after being frozen, resetting their velocity didn’t help much at all. Also for reference the vehicle seats are for boats that travel faster then the player so the anti cheat must account for that to.
I am not looking for ways to make this code run ‘faster’ NOR an argument on the best way to handle an anti-cheat like in many similar threads I’ve read where a conclusion was seemingly never reached. I’m just looking for a straightforward solution for this problem from hopefully someone who has made and uses a good anti-cheat. Also an anti-cheat is magnitudes of importance for this game since it is a PVP game.
ANTICHEAT CODE:
local players = game:GetService("Players")
local run = game:GetService("RunService")
local Rep = game:GetService("ReplicatedStorage")
local GU = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("GameUtil"))
local Inflictions = require(game:GetService("ReplicatedStorage"):WaitForChild("Modules"):WaitForChild("StatusEffects"):WaitForChild("Inflictions"))
_G.playerMaxSpeed = {} -- exact numbers
local playerLastPosition = {}
_G.playerTeleported = {}
local playerFreezes = {}
local frozenPlayers = {}
local FallingThresholds = {}
local Pings = {}
local FreezeTolerance = {}
local DEFAULT_WALKSPEED = 16 -- default walkspeed
local STUD_SPEED_FREEDOM = 10 -- INCREASE IF PLAYERS ARE GETTING FALSELY KICKED/FROZEN - DECREASE IF EVERYONE IS TELEPORTING
local FREEZE_TIME = 0.5 -- how long they are frozen
local MAX_FREEZES = 3 -- if they have 3 freezes, kick them
players.PlayerAdded:Connect(function(player)
_G.playerMaxSpeed[player] = DEFAULT_WALKSPEED
--_G.playerTeleported[player] = true
playerWarnings[player] = {}
playerLastPosition[player] = nil
playerFreezes[player] = {}
FallingThresholds[player] = 0
Pings[player] = {}
FreezeTolerance[player] = 0
end)
players.PlayerRemoving:Connect(function(player)
_G.playerMaxSpeed[player] = nil
playerWarnings[player] = nil
playerLastPosition[player] = nil
playerFreezes[player] = nil
_G.playerTeleported[player] = nil
FallingThresholds[player] = nil
Pings[player] = nil
end)
local ElpasedSinceLastCast = 0.2 + tick()
local ElapsedSinceLastPing = 0.3 + tick()
run.Heartbeat:Connect(function(dt)
for _,player in pairs(game.Players:GetPlayers()) do
if player.Character then
local char = player.Character
local Trackingpart = char.PrimaryPart
if Trackingpart and Trackingpart == char:FindFirstChild("HumanoidRootPart") and playerWarnings[player] then
local CheckSpeed = _G.playerMaxSpeed[player]
local hum = char:FindFirstChild("Humanoid")
if hum and hum.SeatPart then
if hum.SeatPart:IsA("VehicleSeat") then
CheckSpeed = hum.SeatPart.MaxSpeed
elseif hum.SeatPart.Parent:FindFirstChild("VehicleSeat") then
CheckSpeed = hum.SeatPart.Parent:FindFirstChild("VehicleSeat").MaxSpeed
end
end
if #playerFreezes[player] > 3 then
player:Kick("Exploiting is prohibited")
elseif #playerFreezes[player] > 0 then
for key,Freeze in pairs(playerFreezes[player]) do
if tick() - Freeze > FREEZE_TIME then
table.remove(playerFreezes[player],key)
end
FreezeTolerance[player] = CheckSpeed
Trackingpart.Velocity = Vector3.new(0,0,0)
if hum and hum.SeatPart and hum.SeatPart:IsA("VehicleSeat") then
hum.SeatPart.Parent.PrimaryPart.Velocity = Vector3.new(0,0,0)
end
end
if #playerFreezes[player] == 0 then
char.PrimaryPart.Anchored = false
end
end
playerLastPosition[player] = playerLastPosition[player] or Trackingpart.Position
local CurrentPosXZ = Vector3.new(Trackingpart.Position.X,0,Trackingpart.Position.Z)
local OldPosXZ = Vector3.new(playerLastPosition[player].X,0,playerLastPosition[player].Z)
--local DistanceCovered = (((CurrentPosXZ - OldPosXZ).Magnitude / dt)/(1+(Pings[player]["ClientTime"] or 0)))/STUD_SPEED_FREEDOM
local freedom = STUD_SPEED_FREEDOM + (Pings[player]["ClientTime"] or 0) + FreezeTolerance[player]
local DistanceCovered = ((CurrentPosXZ - OldPosXZ).Magnitude / dt) - freedom
FreezeTolerance[player] = 0
if DistanceCovered > CheckSpeed and not _G.playerTeleported[player] then
char:SetPrimaryPartCFrame(CFrame.new(playerLastPosition[player]))
char.PrimaryPart.Anchored = true
local currentime = tick()
table.insert(playerFreezes[player],currentime)
elseif _G.playerTeleported[player] then
_G.playerTeleported[player] = nil
end
playerLastPosition[player] = char.PrimaryPart.Position
if tick() > ElapsedSinceLastPing then
ElapsedSinceLastPing = tick() + 0.3
Pings[player]["ServerTime"] = tick()
Rep.Relay.GetPing:FireAllClients()
end
else
player:Kick("Exploiting is prohibited")
end
end
end
end)
Rep.Relay.GetPing.OnServerEvent:Connect(function(player)
Pings[player]["ClientTime"] = tick() - (Pings[player]["ServerTime"] or tick())
end)