-- TeleportAntiCheat (ModuleScript in ServerScriptService)
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TeleportAC = {}
--// SETTINGS
TeleportAC.Settings = {
MaxSpeedStudsPerSecond = 120, -- Max legit speed (walk + jump + small lag)
MaxVerticalJump = 60, -- Max legit vertical change in one frame
MinCheckDelta = 0.05, -- Don't check if dt is too tiny
KickOnDetection = true, -- true = kick, false = just call callback
DebugPrint = false, -- print detections to output
}
-- callback: function(player, reason)
TeleportAC.OnFlag = nil
-- internal player data
local playerData = {}
--// UTIL
local function getRoot(character)
return character and character:FindFirstChild("HumanoidRootPart")
end
local function flagPlayer(player, reason)
if TeleportAC.Settings.DebugPrint then
warn("[TeleportAC] ".. player.Name .. " flagged: " .. reason)
end
if TeleportAC.OnFlag then
TeleportAC.OnFlag(player, reason)
end
if TeleportAC.Settings.KickOnDetection then
player:Kick("[AntiCheat] Illegal teleport detected. (" .. reason .. ")")
end
end
--// PUBLIC: mark a legit teleport destination
-- TeleportAntiCheat:AllowTeleport(player, destinationPosition, radius, duration)
function TeleportAC:AllowTeleport(player, position, radius, duration)
local data = playerData[player]
if not data then return end
table.insert(data.AllowedTeleports, {
Position = position,
Radius = radius or 10,
ExpiresAt = tick() + (duration or 2),
})
end
--// INTERNAL: check if current pos matches any allowed teleport
local function isInAllowedTeleport(player, position)
local data = playerData[player]
if not data then return false end
local now = tick()
local allowed = data.AllowedTeleports
for i = #allowed, 1, -1 do
local info = allowed[i]
if now > info.ExpiresAt then
table.remove(allowed, i)
else
if (position - info.Position).Magnitude <= info.Radius then
-- consume this allowed teleport so it can't be abused
table.remove(allowed, i)
return true
end
end
end
return false
end
--// INTERNAL: update loop
local function updateAll(dt)
if dt < TeleportAC.Settings.MinCheckDelta then
return
end
for player, data in pairs(playerData) do
local character = player.Character
local root = getRoot(character)
if root then
local pos = root.Position
if data.LastPosition then
local dist = (pos - data.LastPosition).Magnitude
local speed = dist / dt
local verticalDelta = math.abs(pos.Y - data.LastPosition.Y)
-- check for allowed teleports first
if isInAllowedTeleport(player, pos) then
data.LastPosition = pos
data.LastTime = tick()
continue
end
-- speed check
if speed > TeleportAC.Settings.MaxSpeedStudsPerSecond then
flagPlayer(player, "Speed/Teleport: " .. math.floor(speed) .. " studs/s")
-- vertical spike check (like suddenly way up in the air)
elseif verticalDelta > TeleportAC.Settings.MaxVerticalJump then
flagPlayer(player, "Vertical teleport: ".. math.floor(verticalDelta) .. " studs")
end
end
data.LastPosition = pos
data.LastTime = tick()
end
end
end
--// PLAYER HANDLERS
local function setupPlayer(player)
playerData[player] = {
LastPosition = nil,
LastTime = tick(),
AllowedTeleports = {},
}
player.CharacterAdded:Connect(function(char)
-- reset tracking when they respawn
local root = getRoot(char)
if root then
local data = playerData[player]
if data then
data.LastPosition = root.Position
data.LastTime = tick()
end
end
end)
end
local function removePlayer(player)
playerData[player] = nil
end
--// START
function TeleportAC.Start()
for _, plr in ipairs(Players:GetPlayers()) do
setupPlayer(plr)
end
Players.PlayerAdded:Connect(setupPlayer)
Players.PlayerRemoving:Connect(removePlayer)
RunService.Heartbeat:Connect(updateAll)
end
return TeleportAC
To embed this project on your website, copy the following code and paste it into your website's HTML: