Server Security
Understanding
The problem with creating anti cheats is understanding the importance of how your anti-cheat should function, to get the most secure results this will want to be handled on the server, however that makes things much more difficult and can tend to have unreliable results.
Reliability
As of right now one of the best ways to make sure that your anti cheat isn’t false flagging is to check the players ping, I will not be covering this here however, although there are many posts that will show you how to get a users ping.
Another way to make sure that your anti cheat wont false flag users is to give it some leanway, your client may do unexpected behaviors that result in false flags, however in this post specifically you’re looking for long distance teleports; which you can do quite easily without needing to deal with ping.
Pros
- The client can’t modify or remove any code to disable your anti cheat
- The client has no way to view our code in order to create a bypass
- No use of events will be needed
- The client can’t teleport themselves using our implemented functions
Cons
- Can be unreliable without the proper testing and checks
- May cause lag on the server if its being abused
- May not detect a user instantly
Code
So with that being said, lets dive right in to it! I noticed how you requested above that we use metatables and OOP
While coding this anti teleport system I realized quite soon that it truly wasn’t needed, however I included it anyways.
Player Connection
When a player joins the game we’ll want to begin our checks in order to detect teleportation.
local players = game:GetService("Players")
players.PlayerAdded:Connect(function(player) -- Connect a function when a player joins
-- Begin the anti cheat code here,
end)
Creating the Anti Cheat
Now that we know when the player joins, we’ll begin creating our anti teleport.
local cache = {}
local players = game:GetService("Players")
function teleport:Teleport(pos) -- Teleport a user to the given location without flagging our anti cheat
local player = self.player
local character = player.Character or player.CharacterAdded:Wait() -- Grabbing the character and making sure it exists
local humanoidRootPart = character:WaitForChild("HumanoidRootPart") -- HumanoidRootPart, will be used to teleport the user
self.teleported = tick() -- Saving the time that the user was teleported, that way it does flag our teleport
humanoidRootPart.CFrame = CFrame.new(pos) -- Teleporting the user to the given location
end
function teleport:Begin() -- Begin the loop checks, will use settings
local player = self.player -- The player that we'll be enforcing checks on
self.func = coroutine.create(function() -- Our function for ease of removal to prevent memory leaks
while wait(1) do
local character = player.Character or player.CharacterAdded:Wait() -- Our players character & potentially waiting for one
local humanoidRootPart = character:WaitForChild("HumanoidRootPart") -- humanoidRootPart, will be used for our positions
local newPosition, savedPosition = humanoidRootPart.Position, self.position
if savedPosition then -- Ensuring the player has a stored position, if not then set one later on
local SXZ, XZ = Vector3.new(savedPosition.X, 0,savedPosition.Z), Vector3.new(newPosition.X, 0,newPosition.Z) -- Removing the Y axis from both the saved position & new position
local distance = (SXZ - XZ).magnitude -- Distance between the two points (saved and new)
if distance > 40 and tick() - self.teleported > 3 then -- If the user hasn't recently been teleported and they've moved over 40 studs in a second; enforce a punishment
self:Teleport(savedPosition) -- Teleports them back to the previous spot!
end
end
self.position = newPosition -- Saves the new position
end
end)
coroutine.resume(self.func) -- Begins the loop!
end
function teleport:End() -- Will clear all existance of the checks to prevent memory leaks
local player = self.player
if self.func then
coroutine.yield(self.func) -- ends the loop
self.func = nil -- removes the function completely
self = nil
end
cache[player] = nil -- removes table from cache
end
function teleport.new(player) -- Creates & Returns our TeleportClass
return setmetatable({ -- Create a simple OOP
position = nil,
player = player,
teleported = tick()
}, {
__index = teleport -- Teleport table will be our class
})
end
players.PlayerAdded:Connect(function(player) -- Connect a function when a player joins
if cache[player] then
cache[player]:End() -- If the player already is stored, then remove it
end
cache[player] = teleport.new(player) -- Create the class
cache[player]:Begin() -- Call a custom function on the class that will begin our loop/checks
end)
players.PlayerRemoving:Connect(function(player) -- Connects a function when a player leaves
if cache[player] then
cache[player]:End() -- Ends it
end
end)
Video
Conclusion
I would’ve loved to make this post a lot bigger and separated the code more, however I ran out of time! If you have any questions please let me know!