Will this anticheat work?

For a past 3 days, I were working on my own anti-cheat, which should detect:

  • Teleporting
  • Walkspeed increase
  • JumpHeight increase
  • Air walking
  • Air jumping

I have never dealt with anticheats and cheats before, so, I want to know - is it good? How I can improve it?
Note: Light/Heavy are gravity modifiers, Excuses added with my game specific SERVER things, like events, and impossible to get from client. Same for aleby.

local SSS = game:GetService("ServerScriptService")
local SS = game:GetService("ServerStorage")
local RS = game:GetService("ReplicatedStorage")
local Bindables = SS:WaitForChild("Bindables")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local CurrentItems = require(RS:WaitForChild("Libraries"):WaitForChild("CurrentItemsData"))
local IsLobby = (game.PlaceId == 16725804854 or game.PlaceId == 83201107517231)

local Module = {
	Players = {}
}
local ModuleMeta = {}
ModuleMeta.__index = ModuleMeta

local Vector3XZ = Vector3.new(1, 0, 1)

local CheckDelay = 0.5

local function FuzzyEq(V1, V2, e)
	local V3 = V1-V2
	return (V3.X < e or V3.X > -e) and (V3.Y < e or V3.Y > -e) and (V3.Z < e or V3.Z > -e)
end

local function Vector3abs(v)
	return Vector3.new(v.X>0 and v.X or -v.X, v.Y>0 and v.Y or -v.Y, v.Z>0 and v.Z or -v.Z)
end

local AffectorDetectParams = OverlapParams.new()
AffectorDetectParams.FilterType = Enum.RaycastFilterType.Exclude
AffectorDetectParams.FilterDescendantsInstances = {}
AffectorDetectParams.RespectCanCollide = true

local FloorDetectParams = OverlapParams.new()
FloorDetectParams.FilterType = Enum.RaycastFilterType.Exclude
FloorDetectParams.FilterDescendantsInstances = {}
FloorDetectParams.RespectCanCollide = true
FloorDetectParams.MaxParts = 1

local function OnCharacterRemoved(Player)
	if Player.Parent then
		Module.Players[Player].Character = nil
		if Module.Players[Player].Connections.CharacterFell then
			Module.Players[Player].Connections.CharacterFell:Disconnect()
			Module.Players[Player].Connections.CharacterFell = nil
		end
		if Module.Players[Player].Connections.CharacterJumped then
			Module.Players[Player].Connections.CharacterJumped:Disconnect()
			Module.Players[Player].Connections.CharacterJumped = nil
		end
		if Module.Players[Player].Connections.CharacterLanded then
			Module.Players[Player].Connections.CharacterLanded:Disconnect()
			Module.Players[Player].Connections.CharacterLanded = nil
		end
		if Module.Players[Player].Connections.Touches then
			for Part, Connection in Module.Players[Player].Connections.Touches do
				Connection:Disconnect()
			end
			Module.Players[Player].Connections.Touches = nil
		end
	end
end

local function OnCharacterAdded(Player)
	local Character = Player.Character
	Module.Players[Player].Character = Character
	Module.Players[Player].CheckData = {}

	local Primary = Character.PrimaryPart
	Module.Players[Player].AllowedSpeed = Vector3.new(Character.Humanoid.WalkSpeed, 0, Character.Humanoid.WalkSpeed)
	Module.Players[Player].Connections.CharacterFell = Primary.AncestryChanged:Connect(function()
		if Primary.Parent == nil then
			OnCharacterRemoved(Player)
		end
	end)
	Module.Players[Player].Connections.Touches = {}
	for _, Part in Character:GetChildren() do
		if Part:IsA("BasePart") and Part.CanCollide then
			Module.Players[Player].Connections.Touches[Part] = Part.Touched:Connect(function(Other)
				Module.Players[Player].Aleby.Affectors[Other] = DateTime.now().UnixTimestampMillis/1000 + (Other.AssemblyLinearVelocity.Magnitude/10)^0.5 + CheckDelay*1.1
			end)
		end
	end
	Module.Players[Player].Connections.CharacterJumped = Character.Humanoid.Jumping:Connect(function(State)
		if State then
			if Module.Players[Player].Aleby.AllowJump == 0 then
				print("Player was disallowed to jump!!!")
				table.insert(Module.Players[Player].Penalties, {
					Time = DateTime.now().UnixTimestampMillis/1000,
					Expire = DateTime.now().UnixTimestampMillis/1000 + 60,
					Value = 2,
					Data = {
						Type = "DisallowedJump"
					},
				})
			else
				local HitboxCFrame, HitboxSize = Character:GetBoundingBox()
				local WeightModifier = (Module.Players[Player].Aleby.Heavy and Module.Players[Player].Aleby.Heavy.Power or 1) / (Module.Players[Player].Aleby.Light and Module.Players[Player].Aleby.Light.Power or 1)
				local JumpHeight = (Character.Humanoid.JumpPower*Character.Humanoid.JumpPower)/(workspace.Gravity*WeightModifier*2)*1.1
				if Module.Players[Player].Aleby.Bounce then
					JumpHeight += Module.Players[Player].Aleby.Bounce.Power
				end
				FloorDetectParams.FilterDescendantsInstances = {Character}
				local CorrectJump = false
				if Module.Players[Player].Aleby.AllowJump then
					if Module.Players[Player].Aleby.Climbing then
						local FloorJump = workspace:GetPartBoundsInBox(HitboxCFrame*CFrame.new(0, -(HitboxSize.Y+JumpHeight)/2, -(HitboxSize.Z+JumpHeight)/2), Vector3.new(HitboxSize.X, JumpHeight, JumpHeight), FloorDetectParams)
						CorrectJump = #FloorJump == 1
					else
						local FloorJump = workspace:GetPartBoundsInBox(HitboxCFrame-Vector3.new(0, (HitboxSize.Y+JumpHeight)/2, 0), Vector3.new(HitboxSize.X, JumpHeight, HitboxSize.Z), FloorDetectParams)
						CorrectJump = #FloorJump == 1
					end
				end
				if not CorrectJump then
					print("Player is air-jumping! " .. (Module.Players[Player].Aleby.Climbing == 1 and "Climbing" or "Ground"))
					table.insert(Module.Players[Player].Penalties, {
						Time = DateTime.now().UnixTimestampMillis/1000,
						Expire = DateTime.now().UnixTimestampMillis/1000 + 60,
						Value = 2,
						Data = {
							Type = "AirJump"
						},
					})
				else
					Module.Players[Player].Aleby.Jumping = {
						Time = DateTime.now().UnixTimestampMillis/1000,
						StartHeight = Character.PrimaryPart.Position.Y,
						Power = JumpHeight,
						Expiration = DateTime.now().UnixTimestampMillis/1000 + (Character.Humanoid.JumpPower/(workspace.Gravity*WeightModifier)) + CheckDelay*1.1
					}
				end
			end
		end
	end)
	local JumpAllowStates = {
		Enum.HumanoidStateType.Landed,
		Enum.HumanoidStateType.Seated,
		Enum.HumanoidStateType.GettingUp,
		Enum.HumanoidStateType.Swimming,
	}
	local JumpDisallowStates = {
		Enum.HumanoidStateType.FallingDown,
		Enum.HumanoidStateType.Ragdoll,
		Enum.HumanoidStateType.Flying,
		Enum.HumanoidStateType.PlatformStanding,
		Enum.HumanoidStateType.Dead,
		Enum.HumanoidStateType.Physics,
	}
	local ClimbNotCancelStates = {
		Enum.HumanoidStateType.Climbing,
		Enum.HumanoidStateType.Freefall,
		Enum.HumanoidStateType.Jumping,
	}
	Module.Players[Player].Aleby.AllowJump = true
	Module.Players[Player].Connections.CharacterLanded = Character.Humanoid.StateChanged:Connect(function(PrevState, NewState)
		if NewState == Enum.HumanoidStateType.Climbing then
			Module.Players[Player].Aleby.AllowJump = true
			Module.Players[Player].Aleby.Climbing = {
				Time = DateTime.now().UnixTimestampMillis/1000,
			}
		elseif table.find(JumpAllowStates, NewState) then
			Module.Players[Player].Aleby.AllowJump = true
		elseif table.find(JumpDisallowStates, NewState) then
			Module.Players[Player].Aleby.AllowJump = false
		end
		if not table.find(ClimbNotCancelStates, NewState) then
			Module.Players[Player].Aleby.Climbing = nil
		end
		if NewState == Enum.HumanoidStateType.Landed then
			local WeightModifier = (Module.Players[Player].Aleby.Heavy and Module.Players[Player].Aleby.Heavy.Power or 1) / (Module.Players[Player].Aleby.Light and Module.Players[Player].Aleby.Light.Power or 1)
			local Velocity = -Character.PrimaryPart.AssemblyLinearVelocity.Y
			local JumpHeight = (Velocity*Velocity)/(workspace.Gravity*WeightModifier*2)*1.1
			Module.Players[Player].Aleby.Bounce = {
				Time = DateTime.now().UnixTimestampMillis/1000,
				StartHeight = Character.PrimaryPart.Position.Y,
				Power = JumpHeight,
				Expiration = DateTime.now().UnixTimestampMillis/1000 + (Velocity/(workspace.Gravity*WeightModifier)) + CheckDelay*1.1,
			}
		end
	end)
end

local function Initialize(Player)
	local self = setmetatable({}, ModuleMeta)
	self.Penalties = {}
	self.Excuses = {}
	self.Aleby = {}
	self.Aleby.Affectors = {}
	self.CheckData = {}
	self.Connections = {}
	self.Connections.CharacterAdded = Player.CharacterAdded:Connect(function()
		OnCharacterAdded(Player)
	end)
	self.Connections.PlayerRemoved = Player.Destroying:Connect(function()
		for k, v in pairs(self.Connections) do
			v:Disconnect()
		end
		Module.Players[Player] = nil
	end)
	self.LastCheck = DateTime.now().UnixTimestampMillis/1000
	Module.Players[Player] = self
	OnCharacterRemoved(Player)
end

function ModuleMeta:GetAffectors()
	local HitboxCFrame, HitboxSize = self.Character:GetBoundingBox()
	AffectorDetectParams.FilterDescendantsInstances = {self.Character}
	local Nearby = workspace:GetPartBoundsInBox(HitboxCFrame, HitboxSize, AffectorDetectParams)
	for i = 1, #Nearby, 1 do
		self.Aleby.Affectors[Nearby[i]] = DateTime.now().UnixTimestampMillis/1000 + (Nearby[i].AssemblyLinearVelocity.Magnitude*2/workspace.Gravity)^0.5 + CheckDelay*1.1
	end
end

function ModuleMeta:CheckTeleport(Player:Player)
	if not self.CheckData.TeleportData then
		self.CheckData.TeleportData = {
			LastCheck = DateTime.now().UnixTimestampMillis/1000,
			PrevCFrame = self.Character.PrimaryPart.CFrame,
		}
		return
	end
	local CheckData = self.CheckData.TeleportData
	local PrevCFrame, CurCFrame = CheckData.PrevCFrame or self.Character.PrimaryPart.CFrame, self.Character.PrimaryPart.CFrame
	local LastCheck = CheckData.LastCheck
	local Delta = DateTime.now().UnixTimestampMillis/1000 - LastCheck
	local Ping = Player:GetNetworkPing()
	if Ping >= Delta then return end
	local MovedDistance = (CurCFrame.Position - PrevCFrame.Position)
	local MovedHorizontal = MovedDistance * Vector3XZ
	local MovedVertical = MovedDistance.Y

	local AllowedHorizontal = (self.AllowedSpeed*Vector3XZ).Magnitude * 1.2 --self.Character.Humanoid.WalkSpeed * 1.2
	AllowedHorizontal *= (Delta + Ping)
	local AllowedVertical = self.Character.Humanoid.WalkSpeed * (self.Character.Humanoid:GetState() == Enum.HumanoidStateType.Climbing and 1 or 0.7071067811865476) --math.cos(math.rad/4)
	AllowedVertical = AllowedVertical + self.AllowedSpeed.Y --AffectorImpact.Y
	if self.Aleby.Jumping then
		local AddedHeight = math.max(self.Aleby.Jumping.Power - (CurCFrame.Position.Y - self.Aleby.Jumping.StartHeight), 0)
		AllowedVertical = AllowedVertical + AddedHeight
	end

	if AllowedHorizontal <= MovedHorizontal.Magnitude then
		print("Player cheated (horizontal), teleported from: " .. tostring(PrevCFrame) .. " to " .. tostring(CurCFrame))
		local Power = MovedHorizontal.Magnitude / AllowedHorizontal
		table.insert(self.Penalties, {
			Time = DateTime.now().UnixTimestampMillis/1000,
			Expire = DateTime.now().UnixTimestampMillis/1000 + 60 * Power,
			Value = 1,
			Data = {
				Type = "Teleport/Walkspeed",
				Start = PrevCFrame,
				End = CurCFrame,
			},
		})
	end
	if AllowedVertical <= MovedVertical then
		print("Player cheated (vertical), teleported from: " .. tostring(PrevCFrame) .. " to " .. tostring(CurCFrame))
		local Power = MovedVertical / AllowedVertical
		table.insert(self.Penalties, {
			Time = DateTime.now().UnixTimestampMillis/1000,
			Expire = DateTime.now().UnixTimestampMillis/1000 + 60 * Power,
			Value = Power,
			Data = {
				Type = "Teleport/HiJump",
				Start = PrevCFrame,
				End = CurCFrame,
			},
		})
	end

	CheckData.PrevCFrame = CurCFrame
	CheckData.LastCheck = DateTime.now().UnixTimestampMillis/1000
end

function ModuleMeta:CheckAirWalk(Player:Player)
	if not self.CheckData.AirWalkData then
		self.CheckData.AirWalkData = {
			LastCheck = DateTime.now().UnixTimestampMillis/1000,
			PrevCFrame = self.Character.PrimaryPart.CFrame,
			NotFallStack = 0,
			Acceleration = 0
		}
		return
	end
	local CheckData = self.CheckData.AirWalkData
	local CurCFrame = self.Character.PrimaryPart.CFrame
	local LastCheck = CheckData.LastCheck
	local Delta = DateTime.now().UnixTimestampMillis/1000 - LastCheck
	local Ping = Player:GetNetworkPing()
	if Ping >= Delta then return end
	local HitboxCFrame, HitboxSize = self.Character:GetBoundingBox()
	FloorDetectParams.FilterDescendantsInstances = {self.Character}
	local FloorBelow = false
	if Module.Players[Player].Aleby.Climbing then
		local FloorJump = workspace:GetPartBoundsInBox(HitboxCFrame*CFrame.new(0, -(HitboxSize.Y+2)/2, -(HitboxSize.Z)/2), Vector3.new(HitboxSize.X, HitboxSize.Y, 2), FloorDetectParams)
		FloorBelow = #FloorJump == 1
	else
		local FloorJump = workspace:GetPartBoundsInBox(HitboxCFrame-Vector3.new(0, HitboxSize.Y/2, 0), Vector3.new(HitboxSize.X, 2, HitboxSize.Z), FloorDetectParams)
		FloorBelow = #FloorJump == 1
	end
	if not FloorBelow then
		print("No floor found")
		local MovedDistance = (CurCFrame.Position - CheckData.PrevCFrame.Position)

		local MovedVertical = MovedDistance.Y
		local WeightModifier = (Module.Players[Player].Aleby.Heavy and Module.Players[Player].Aleby.Heavy.Power or 1) / (Module.Players[Player].Aleby.Light and Module.Players[Player].Aleby.Light.Power or 1)
		local Acceleration = workspace.Gravity*WeightModifier*Delta*0.9
		local Allowed = -CheckData.Acceleration
		if self.Aleby.Jumping then
			local AddedHeight = math.max(self.Aleby.Jumping.Power - (CurCFrame.Position.Y - self.Aleby.Jumping.StartHeight), 0)
			Allowed = Allowed + AddedHeight
			Acceleration = 0
			print(self.Aleby.Jumping, DateTime.now().UnixTimestampMillis/1000)
		end
		print("Acceleration needed: " .. tostring(Acceleration))
		if MovedVertical >= Allowed then
			CheckData.NotFallStack += 1
			if CheckData.NotFallStack >= 10 then
				print("Airwalked")
				table.insert(self.Penalties, {
					Time = DateTime.now().UnixTimestampMillis/1000,
					Expire = DateTime.now().UnixTimestampMillis/1000 + 60,
					Value = 1,
					Data = {
						Type = "Airwalk",
					}
				})
			end
		else
			CheckData.Acceleration += Acceleration
		end
	else
		CheckData.NotFallStack = 0
		CheckData.Acceleration = 0
	end
	CheckData.PrevCFrame = CurCFrame
	CheckData.LastCheck = DateTime.now().UnixTimestampMillis/1000
end

Players.PlayerAdded:Connect(Initialize)

function Module.AddExcuse(Player, Id, Excuse)
	Module.Players[Player].Excuses[Id] = Excuse
	for i = #Module.Players[Player].Penalties, 1, -1 do
		local Penalty = Module.Players[Player].Penalties[i]
		if Penalty.Time >= Excuse.Time and Penalty.Time <= Excuse.Expire then
			if typeof(Excuse.Target) == "function" and Excuse.Target(Penalty) or table.find(Excuse.Target, Penalty.Reason) then
				table.remove(Module.Players[Player].Penalties, i)
				print("Excused penalty " .. tostring(i))
			end
		end
	end
end

function Module.RemoveExcuse(Player, Id)
	Module.Players[Player].Excuses[Id] = nil
end

function Module.AddAleby(Player, Id, Excuse)
	Module.Players[Player].Aleby[Id] = Excuse
	for i = #Module.Players[Player].Penalties, 1, -1 do
		local Penalty = Module.Players[Player].Penalties[i]
		if Penalty.Time >= Excuse.Time and Penalty.Time <= Excuse.Expire then
			if typeof(Excuse.Target) == "function" and Excuse.Target(Penalty) or table.find(Excuse.Target, Penalty.Reason) then
				table.remove(Module.Players[Player].Penalties, i)
				print("Excused penalty " .. tostring(i))
			end
		end
	end
end

function Module.RemoveAleby(Player, Id)
	Module.Players[Player].Aleby[Id] = nil
end

coroutine.wrap(function()
	while true do
		local Waited = task.wait(CheckDelay)
		for Player, Data in pairs(Module.Players) do
			if not Data.Character then
				continue
			end
			local CurTime = DateTime.now().UnixTimestampMillis/1000
			if IsLobby then
				local LobbyPlotCenter = Vector3.new(-14, 2.525, 51)
				local Diffirence = Module.Players[Player].Character.PrimaryPart.Position - LobbyPlotCenter
				if FuzzyEq(LobbyPlotCenter, Module.Players[Player].Character.PrimaryPart.Position, 40) then
					Module.Players[Player].Excuses.LobbyPlot = {
						Time = CurTime-Waited,
						Expire = CurTime+Waited,
						Target = function(Penalty)
							return true
						end,
					}
				end 
			end
			local AllowedVelocity = Vector3.max(Vector3.new(Data.Character.Humanoid.WalkSpeed, 0, Data.Character.Humanoid.WalkSpeed), Vector3.min(Vector3abs(Data.Character.PrimaryPart.AssemblyLinearVelocity), Data.AllowedSpeed))
			for Affector, Time in pairs(Data.Aleby.Affectors) do
				if Time < CurTime then
					Data.Aleby.Affectors[Affector] = nil
				else
					AllowedVelocity = Vector3.max(AllowedVelocity, Vector3abs(Affector.AssemblyLinearVelocity))
				end
			end
			Data.AllowedSpeed = AllowedVelocity
			for Id, Aleby in pairs(Data.Aleby) do
				if typeof(Aleby) == "table" and Aleby.Expiration and Aleby.Expiration <= CurTime then
					Data.Aleby[Id] = nil
				end
			end
			Data:GetAffectors()
			Data:CheckTeleport(Player)
			Data:CheckAirWalk(Player)
			local Total = 0
			for Id, Excuse in pairs(Data.Excuses) do
				if Excuse.Expire <= CurTime then
					Data.Excuses[Id] = nil
					continue
				end
			end
			for i = #Data.Penalties, 1, -1 do
				local Penalty = Data.Penalties[i]
				if Penalty.Expire <= CurTime then
					table.remove(Data.Penalties, i)
					continue
				end
				for Id, Excuse in pairs(Data.Excuses) do
					if Penalty.Time >= Excuse.Time and Penalty.Time <= Excuse.Expire then
						if typeof(Excuse.Target) == "function" and Excuse.Target(Penalty) or table.find(Excuse.Target, Penalty.Reason) then
							table.remove(Module.Players[Player].Penalties, i)
							print("Excused penalty " .. tostring(i))
						end
					end
				end
				Total += Penalty.Value
			end
			if Total >= 20 then
				warn("Cheater detected, bounty is " .. tostring(Total))
			end
		end
	end
end)()

return Module
Is this anticheat good?
  • Yes
  • Somewhat
  • No (tell me why please)

0 voters

1 Like

Before voting, I wanted to ask if you did a tolerance check.
Some players may have issues with connection lag or fps that prevent the character from performing the physics as expected. (It’s common to see characters on older devices taking a long time to jump).
I really like the scripts that combat exploiters, but any action the server takes needs a very precise sieve to filter out what is a potential cheat or an unlucky player.

This is extremely overdone, things like JumpPower and infinite jump are almost never used by exploiters, and there’s many methods to make them work without actual jumps

Yes, I accounted for player ping. That’s done with ping addition itself and if it’s bigger than check interval (0.5), it will also skip ping/0.5 iterations.

Agreed, but even rare cheats should be punished. In case if exploiter begins to jump without firing jumps themselves, server will treat that like TPing up or airwalk.