Welcome to EzBan Obby Anti-Cheat!
Table of contents:
Section
Features
Open-source code
Update Log
How to use
Section |
---|
Features |
Open-source code |
Update Log |
How to use |
Multiple speed hack features
- Flat Plane position detection (Moving on XZ axes combined; can be customized)
- XYZ Position detection
- WalkSpeed detection
- JumpHeight detection
Anti-area-enter feature
- Automatically punish hackers when they enter areas they shouldn’t
- Uses parts so you can easily visualize areas that you created
Timed-area feature
- Automatically punish hackers when they get from one area to another area too quickly
- Automatically punish hackers if they skip an area (teleport hacking)
- Uses parts so you can easily visualize areas that you created
Admin commands
- 4 different commands which can be used in roblox chat by admins
- /Ban - Bans a player
- /Unban - Unbans a player
- /BanNotInGame - Bans a player who isn’t in the game as you
- /AddAdmin - Adds an admin who can use all commands besides this one
- “Super admins” that can use /AddAdmin - allows secure command use
Easy to modify
- There are currently 15 configuration options
- Enabled Features - Always have the option of being able to disable features if you don’t want them
- Teleporting Value Folder - Don’t let players get punished for using a in-game teleport feature
- Custom Detection Messages - Notify players with custom messages if they area detected of hacking
- Punishment Type - 3 different ways to punish hackers
- Ban Length - Control the length of bans
- Allowed Difference - Don’t let players get accidentally punished for being laggy
- Expected Player Speed - Expected player WalkSpeed
- Expected Player Jump Height - Expected player JumpHeight
- Super Admin UserIds - Add super admins
- Update Speed - Control how often the anti-cheat will check for hacking
- Area Touching Update Speed - Control how often the anti-cheat will check for hacking for illegal/timed areas
- Max Free Fall - Control the maximum amount of time a player can free fall for before being punished
- Minimum Time Between Areas - Control the minimum time between timed areas
- Backwards Enabled - Allow players to go backwards in areas
- Flat Plane - Control what axis are checked for flat plane detection
Fully server sided
- Hackers won’t be able to edit or even view any part of the anti-cheat
Uses Roblox's Ban API
- Some benefits include:
- Alt account detection
- See specific reasoning of bans in your game page in creator hub
- For more read: Click me!
You can review the code here if you want or you can view it in roblox studio through the download link
Hierarchy
Hierarchy for the anti-cheat:
Code
"EzBanAntiCheat" code
------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
------------------------------------ Configuration options in "EzBanConfiguration" script ------------------------------------
------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
local ServerStorage = game:GetService("ServerStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local TextService = game:GetService("TextService")
local TextChatService = game:GetService("TextChatService")
local DataStoreService = game:GetService("DataStoreService")
local PlayerService = game:GetService("Players")
local antiCheatScripts = ServerScriptService:FindFirstChild("EzBanAntiCheatScripts")
local antiCheatObjects = ServerStorage:FindFirstChild("EzBanAntiCheatObjects")
local startup = require(antiCheatScripts:FindFirstChild("EzBanStartup"))
local adminDataStore = DataStoreService:GetDataStore("AdminDataStore")
local illegalParts = antiCheatObjects:FindFirstChild("Illegal area parts"):GetChildren()
local timedParts = antiCheatObjects:FindFirstChild("Timed area parts"):GetChildren()
local disableAdminDisregardForTesting = false
local printDebounceLength = 10
warn("EzBan Obby Anti-Cheat is online")
--[[ ConfigurationOptions:
[1] = superAdminUserIds
[2] = customDetectionMessages
[3] = punishmentType
[4] = banLength
[5] = maxFreeFall
[6] = updateSpeed
[7] = minimumTimeBetweenAreas
[8] = backwardsEnabled
[9] = expectedPlayerSpeed
[10] = expectedPlayerJumpHeight
[11] = allowedDifference
[12] = teleportingValue
[13] = flatPlane
[14] = enabledFeatures
[15] = areaTouchingUpdateSpeed
]]
local superAdminUserIds
local customDetectionMessages
local punishmentType
local banLength
local maxFreeFall
local updateSpeed
local minimumTimeBetweenAreas
local backwardsEnabled
local expectedPlayerSpeed
local expectedPlayerJumpHeight
local allowedDifference
local teleportingValueFolder
local flatPlane
local enabledFeatures
local areaTouchingUpdateSpeed
local configLoadSuccess = pcall(function()
local configOptions = startup.GetConfig()
superAdminUserIds = configOptions[1][2]
customDetectionMessages = configOptions[2][2]
punishmentType = configOptions[3][2]
banLength = configOptions[4][2]
maxFreeFall = configOptions[5][2]
updateSpeed = configOptions[6][2]
minimumTimeBetweenAreas = configOptions[7][2]
backwardsEnabled = configOptions[8][2]
expectedPlayerSpeed = configOptions[9][2]
expectedPlayerJumpHeight = configOptions[10][2]
allowedDifference = configOptions[11][2]
teleportingValueFolder = configOptions[12][2]
flatPlane = configOptions[13][2]
enabledFeatures = configOptions[14][2]
areaTouchingUpdateSpeed = configOptions[15][2]
end)
if not configLoadSuccess then
error("Failed to load configuration options to EZ Ban Anti-Cheat!")
end
local recentlyPrintedWarningsUserIDs = {}
local adminInfo = {}
adminInfo.playerUserIdIsAdmin = {}
adminInfo.playerUserIdIsSuperAdmin = {}
function adminInfo:getSuperAdminStatus(player:Player)
if table.find(self.playerUserIdIsAdmin, player.UserId) then
return true
else
return false
end
end
function adminInfo:getAdminStatus(player:Player)
if table.find(self.playerUserIdIsSuperAdmin, player.UserId) then
return true
else
return false
end
end
function adminInfo:updateInfo(player:Player)
local userID = player.UserId
-- Admin status --
local adminStatus = false
local success = pcall(function()
adminStatus = adminDataStore:GetAsync(tostring(userID).."_")
end)
if not success then
warn("Couldn't get admin status for "..player.Name)
end
-- Super admin status --
local superAdminStatus = false
for _, UserID in pairs(superAdminUserIds) do
if player.UserId == userID then
superAdminStatus = true
end
end
-- Update values --
if adminStatus == true then
table.insert(self.playerUserIdIsAdmin, userID)
end
if superAdminStatus == true then
table.insert(self.playerUserIdIsSuperAdmin, userID)
end
-- If player is a super admin but doesn't have regular admin status give it to them
if superAdminStatus == true and adminStatus == false then
table.insert(self.playerUserIdIsAdmin, userID)
end
end
local function hackingDetected(player: Player, displayMsg: string?, privateMsg: string?, teleportBackToPosition: Vector3?, warningMessage: string?)
local userID = player.UserId
local playerIsAdmin = adminInfo:getAdminStatus(player)
-- Check if player is admin, don't allow the ban if the player is an admin
if disableAdminDisregardForTesting == true then
playerIsAdmin = false
end
if playerIsAdmin == false then
local invalidPunishmentType = false
if punishmentType == 1 then -- Ban Player
local config = {
UserIDs = {userID},
Duration = banLength,
DisplayReason = displayMsg,
PrivateReason = ("EZBan Anti-Cheat detected this user of hacking; specifics: ".. privateMsg)
}
PlayerService:BanAsync(config)
elseif punishmentType == 2 then -- Kick Player
player:Kick(displayMsg)
elseif punishmentType == 3 then -- Teleport Player
if teleportBackToPosition then
local character = player.Character or player.CharacterAdded:Wait()
if character:FindFirstChild("HumanoidRootPart") then
character:FindFirstChild("HumanoidRootPart").Position = teleportBackToPosition
end
else
local character = player.Character or player.CharacterAdded:Wait()
if character:FindFirstChild("Humanoid") then
character:FindFirstChild("Humanoid").Health = 0
end
end
else
invalidPunishmentType = true
warn("Punishment type number "..punishmentType.." is invalid")
end
if not invalidPunishmentType then
local userIDIndex = table.find(recentlyPrintedWarningsUserIDs, player.UserId)
if not userIDIndex then
warn("HACKER DETECTED, USERNAME: ", player.Name, "REASON: ", displayMsg)
if warningMessage then
warn(warningMessage)
end
table.insert(recentlyPrintedWarningsUserIDs, player.UserId)
task.wait(printDebounceLength)
table.remove(recentlyPrintedWarningsUserIDs, userIDIndex)
end
end
-- wait until player is attempted to be punished before checking for hacking again
wait(1)
end
end
-- Commands
local function onChatted(message:string, player:Player, playerIsAdmin:boolean, playerIsSuperAdmin:boolean)
local userID = player.UserId
-- if message contains "/" and player is an admin then remove the "/" and make a table of the arguments
if message.find(message,"/") and playerIsAdmin == true then
-- remove "/""
local message = string.gsub(message,"/","")
local args = string.split(message, ",")
-- command
local command = args[1]
-- Make command lowercase
command = string.lower(command)
-- If command is /ban then continue with script
if command == "ban" then
-- Check how many arguments there are
if #args == 3 then
-- other arguments
local bannedPlayer = args[2]
local reason = args[3]
-- remove spaces for banned player's name
bannedPlayer = bannedPlayer:gsub(" ", "")
local foundPlayer = PlayerService:FindFirstChild(tostring(bannedPlayer))
local playerToBanIsAdmin = adminDataStore:GetAsync(tostring(foundPlayer.UserId).."_")
if playerIsAdmin then
if not playerToBanIsAdmin then
local config = {
UserIDs = {foundPlayer.UserId},
Duration = banLength,
DisplayReason = "An admin has banned you from this game",
PrivateReason = ("Admin named: '"..player.Name.."' with UserId: '"..player.UserId.."' banned this player")
}
PlayerService:BanAsync(config)
else
warn("You can't ban an admin")
end
end
else
warn("sytax error: wrong amount of arguments (should be 3)")
end
elseif command == "unban" then
-- Check how many arguments there are
if #args == 2 then
-- Player userid
local unbannedPlayerUserId = args[2]
local success, errorMessage = pcall(function()
local config = {
UserIds = unbannedPlayerUserId,
ApplyToUniverse = true
}
PlayerService:UnbanAsync(config)
end)
if not success then
warn("Anti-Cheat datastore failed to save!, player not unbanned!")
warn(errorMessage)
end
else
-- wrong amount of arguments
warn("sytax error: wrong amount of arguments (should be 2)")
end
elseif command == "bannotingame" then
-- Check how many arguments there are
if #args == 2 then
-- Player userid
local bannedPlayerUserId = args[2]
local playerToBanIsAdmin = adminDataStore:GetAsync(tostring(bannedPlayerUserId).."_")
if playerIsAdmin then
if not playerToBanIsAdmin then
local config = {
UserIDs = {bannedPlayerUserId},
Duration = banLength,
DisplayReason = "An admin has banned you from this game",
PrivateReason = ("Admin named: '"..player.Name.."' with UserId: '"..player.UserId.."' banned this player")
}
PlayerService:BanAsync(config)
else
warn("You can't ban an admin")
end
end
else
-- wrong amount of arguments
warn("sytax error: wrong amount of arguments (should be 2)")
end
elseif command == "addadmin" then
if #args == 2 then
if playerIsSuperAdmin == true then
local adminToAddUserId = args[2]
local success, errorMessage = pcall(function()
adminDataStore:SetAsync(adminToAddUserId.."_", true)
end)
if not success then
warn("/add admin failed")
warn(errorMessage)
end
end
else
-- wrong amount of arguments
warn("sytax error: wrong amount of arguments (should be 2)")
end
end
end
end
local oldCharacterX = 0
local oldCharacterY = 0
local oldCharacterZ = 0
local oldCharacterPosition1 = 0
local oldCharacterPosition2 = 0
local oldCharacterVector = Vector3.zero
local newCharacterX = 0
local newCharacterY = 0
local newCharacterZ = 0
local newCharacterPosition1 = 0
local newCharacterPosition2 = 0
local newCharacterVector = Vector3.zero
-- Check for hacking in previous games / check for speed hacks
PlayerService.PlayerAdded:Connect(function(player)
local Character = player.Character or player.CharacterAdded:Wait()
local userID = player.UserId
adminInfo:updateInfo(player)
local playerIsAdmin = adminInfo:getAdminStatus(player)
local playerIsSuperAdmin = adminInfo:getSuperAdminStatus(player)
if disableAdminDisregardForTesting == true then
playerIsAdmin = false
playerIsSuperAdmin = false
end
if enabledFeatures["AdminCommands"] == true then
player.Chatted:Connect(function(message)
onChatted(message, player, playerIsAdmin, playerIsSuperAdmin)
end)
end
-- Speed hacks, check if humanoid is moving too fast
-- only works for first character
local humanoidRootPart = Character:FindFirstChild("HumanoidRootPart")
local humanoid = Character:FindFirstChild("Humanoid")
local characterIsDead = false
player.CharacterAdded:Connect(function(_character)
-- Update new character components as character has been respawned with new parts
Character = _character
humanoidRootPart = _character:FindFirstChild("HumanoidRootPart")
humanoid = _character:FindFirstChild("Humanoid")
-- wait until anti-cheat has updated character CFrame and ignored respawn completly
task.wait(updateSpeed + 1)
characterIsDead = false
end)
player.CharacterRemoving:Connect(function()
characterIsDead = true
end)
local timeInFreefall = 0
local humanoidState
-- Update state everytime it changes
humanoid.StateChanged:Connect(function()
humanoidState = Character.Humanoid:GetState()
end)
local function GetPlayerTeleportingValue(folder:Folder)
if folder:FindFirstChild(player.Name) then
local boolValue = folder:FindFirstChild(player.Name)
if boolValue:IsA("BoolValue") then
return boolValue.Value
else
error("Object in teleporting folder is a "..typeof(boolValue).."; expected a BoolValue")
return false
end
else
error("Teleporting folder does not contain a BoolValue with the same name as the player")
return false
end
end
-- Checks character movement on individual axes
local function CheckCharacterXYZ(newCharacterPosition, oldCharacterPosition, expectedPlayerSpeedNew)
if enabledFeatures["XYZPositionChecking"] == true then
local playerMovementAmount = newCharacterPosition - oldCharacterPosition
local maxAllowedMovement = expectedPlayerSpeedNew + (allowedDifference * updateSpeed)
local maxNegativeAllowedMovement = (expectedPlayerSpeedNew * -1) - (allowedDifference * updateSpeed)
if playerMovementAmount > maxAllowedMovement or playerMovementAmount < maxNegativeAllowedMovement then
if teleportingValueFolder ~= "NoValueFound" then
if characterIsDead == false and GetPlayerTeleportingValue(teleportingValueFolder) == false then
return "PlayerCheating"
end
else
return "PlayerCheating"
end
end
end
return "PlayerNotCheating"
end
-- Checks character movement on flat plane added
local function CheckCharacterFlatPlane(newCharacterPosition1, oldCharacterPosition1, newCharacterPosition2, oldCharacterPosition2, expectedPlayerSpeedNew)
if enabledFeatures["FlatPlanePositionChecking"] == true then
local playerMovementAmount = (newCharacterPosition1 - oldCharacterPosition1) + (newCharacterPosition2 - oldCharacterPosition2)
--Multiply by 2 because playerMovementAmount is X+Y not only one axis
local maxAllowedMovement = expectedPlayerSpeedNew * 2 + (allowedDifference * updateSpeed)
local maxNegativeAllowedMovement = (expectedPlayerSpeedNew * -2) - (allowedDifference * updateSpeed)
if playerMovementAmount > maxAllowedMovement or playerMovementAmount < maxNegativeAllowedMovement then
if teleportingValueFolder ~= "NoValueFound" then
if characterIsDead == false and GetPlayerTeleportingValue(teleportingValueFolder) == false then
return "PlayerCheating"
end
else
return "PlayerCheating"
end
end
end
return "PlayerNotCheating"
end
local function CheckForSpeedHack()
if player.Character then
if player.Character.Humanoid then
-- Check walkspeed/jumpheight
local walkSpeed = player.Character.Humanoid.WalkSpeed
if enabledFeatures["WalkSpeedChecking"] == true and walkSpeed > expectedPlayerSpeed then
local warningMessage = (player.Name.. " has increased walkspeed")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected of increasing their WalkSpeed; "..
"Detected WalkSpeed: '"..walkSpeed.. "' Expected WalkSpeed: '"..expectedPlayerSpeed.."'")
hackingDetected(player, customDetectionMessages["WalkSpeedMsg"], specificInfo, nil, warningMessage)
end
local jumpHeight = player.Character.Humanoid.JumpHeight
if enabledFeatures["JumpHeightChecking"] == true and jumpHeight > expectedPlayerJumpHeight then
local warningMessage = (player.Name.. " has increased jump height")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected of increasing their JumpHeight; "..
"Detected JumpHeight: '"..jumpHeight.. "' Expected JumpHeight: '"..expectedPlayerJumpHeight.."'")
hackingDetected(player, customDetectionMessages["JumpHeightMsg"], specificInfo, nil, warningMessage)
end
if humanoidState == Enum.HumanoidStateType.Freefall then
-- Add update speed, ex. (updateSpeed = 0.5) time in freefall += 0.5
timeInFreefall += updateSpeed
if timeInFreefall >= maxFreeFall then
-- Placeholder, something other then freefall (doesn't actually ragdoll)
humanoidState = Enum.HumanoidStateType.Ragdoll
end
else
-- Reset time if not in freefall
timeInFreefall = 0
end
-- If updated faster then multiply variables, ex. (expected speed = 16, update speed = 0.5) 16*0.5 = 8 -> player can go 8 studs in 0.5 seconds
local expectedPlayerSpeedNew = expectedPlayerSpeed * updateSpeed
-- Update character CFrame
newCharacterX = humanoidRootPart.CFrame.X
newCharacterY = humanoidRootPart.CFrame.Y
newCharacterZ = humanoidRootPart.CFrame.Z
newCharacterVector = humanoidRootPart.Position
newCharacterPosition1 = humanoidRootPart.CFrame[flatPlane[1]]
newCharacterPosition2 = humanoidRootPart.CFrame[flatPlane[2]]
if humanoidState ~= Enum.HumanoidStateType.Freefall then
if CheckCharacterFlatPlane(newCharacterPosition1, oldCharacterPosition1, newCharacterPosition2, oldCharacterPosition2, expectedPlayerSpeedNew) == "PlayerCheating" then
if characterIsDead == false then
local concatenatedAxes = tostring(flatPlane[1])..tostring(flatPlane[2])
local commaConcatenatedAxes = tostring(flatPlane[1])..", "..tostring(flatPlane[2])
local warningMessage = (player.Name.. " is moving too quickly along "..concatenatedAxes.."-axes")
local oldCharacterPositionDisplay = (oldCharacterPosition1..", "..oldCharacterPosition2)
local newCharacterPositionDisplay = (newCharacterPosition1..", "..newCharacterPosition2)
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected speed hacking on flat plane ("..concatenatedAxes.."-axes); "..
"first recorded position ("..commaConcatenatedAxes.."): '"..oldCharacterPositionDisplay.."'"..
"second recorded position ("..commaConcatenatedAxes.."): '"..newCharacterPositionDisplay.."'"..
"time between positions: '"..updateSpeed.."'")
hackingDetected(player, customDetectionMessages["SpeedFlatPlaneMsg"], specificInfo, oldCharacterVector, warningMessage)
end
elseif CheckCharacterXYZ(newCharacterX, oldCharacterX, expectedPlayerSpeedNew) == "PlayerCheating" then
if characterIsDead == false then
local warningMessage = (player.Name.. " is moving too quickly along x-axis")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected speed hacking on X-Axis; "..
"first recorded position: '"..oldCharacterX.."' second recorded position: '"..newCharacterX.."' time between positions: '"..updateSpeed.."'")
hackingDetected(player, customDetectionMessages["SpeedXMsg"], specificInfo, oldCharacterVector, warningMessage)
end
elseif CheckCharacterXYZ(newCharacterY, oldCharacterY, expectedPlayerSpeedNew) == "PlayerCheating" then
if characterIsDead == false and timeInFreefall >= maxFreeFall then
local warningMessage = (player.Name.. " is moving too quickly along y-axis")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected speed hacking on Y-Axis; "..
"first recorded position: '"..oldCharacterY.."' second recorded position: '"..newCharacterY.."' time between positions: '"..updateSpeed.."'")
hackingDetected(player, customDetectionMessages["SpeedYMsg"], specificInfo, oldCharacterVector, warningMessage)
end
elseif CheckCharacterXYZ(newCharacterZ, oldCharacterZ, expectedPlayerSpeedNew) == "PlayerCheating" then
if characterIsDead == false then
local warningMessage = (player.Name.. " is moving too quickly along z-axis")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' was detected speed hacking on Z-Axis; "..
"first recorded position: '"..oldCharacterZ.."' second recorded position: '"..newCharacterZ.."' time between positions: '"..updateSpeed.."'")
hackingDetected(player, customDetectionMessages["SpeedZMsg"], specificInfo, oldCharacterVector, warningMessage)
end
end
end
-- Update character CFrame
oldCharacterX = humanoidRootPart.CFrame.X
oldCharacterY = humanoidRootPart.CFrame.Y
oldCharacterZ = humanoidRootPart.CFrame.Z
oldCharacterVector = humanoidRootPart.Position
oldCharacterPosition1 = humanoidRootPart.CFrame[flatPlane[1]]
oldCharacterPosition2 = humanoidRootPart.CFrame[flatPlane[2]]
end
end
end
-- repeat forever until player leaves game
if not playerIsAdmin then
repeat
CheckForSpeedHack()
task.wait(updateSpeed)
until not player
end
-- NO CODE PAST THIS POINT IN THIS FUNCTION
end)
-- Function for filtering through illegalParts
local function IsPlayerTouchingIllegalParts()
for _, currentObject in pairs(illegalParts) do
local touchingParts = workspace:GetPartBoundsInBox(currentObject.CFrame, currentObject.Size)
for _, part in pairs(touchingParts) do
if part.Parent:FindFirstChild("Humanoid") then
if PlayerService:GetPlayerFromCharacter(part.Parent) then
local player = PlayerService:GetPlayerFromCharacter(part.Parent)
local warningMessage = (player.Name.." is touching an illegal area part (".. currentObject.Name.. ")")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' touched illegal area named: '"..currentObject.Name.."'")
hackingDetected(player, customDetectionMessages["IllegalAreaMsg"], specificInfo, warningMessage)
end
end
end
end
end
-- Function for filtering through timedParts
local currentArea = nil
local timeAtLastArea = 0
local function IsPlayerTouchingTimedParts()
for _, currentObject in pairs(timedParts) do
local touchingParts = workspace:GetPartBoundsInBox(currentObject.CFrame, currentObject.Size)
for _, part in pairs(touchingParts) do
if part.Parent:FindFirstChild("Humanoid") then
if PlayerService:GetPlayerFromCharacter(part.Parent) then
local player = PlayerService:GetPlayerFromCharacter(part.Parent)
-- Only get the numbers in the name
local name = currentObject.Name
-- Remove characters
local objectNumber = string.gsub(name, "%D", "")
if objectNumber then
objectNumber = tonumber(objectNumber)
end
-- If current area is equal to the old area then disregard
if objectNumber ~= currentArea then
-- If the name is resetlist then set the current area to nil/lobby
--Allow names with uppercase letters and allow words after reset list
if string.lower(string.sub(currentObject.Name, 1, 9)) == "resetlist" then
currentArea = nil
timeAtLastArea = 0
elseif currentArea == nil or objectNumber == currentArea + 1 or (objectNumber == currentArea - 1 and backwardsEnabled == true) then
-- If time at last area plus the minimum time to get between areas is greater then the current time then punish player
if timeAtLastArea + minimumTimeBetweenAreas > os.time() then
local warningMessage = (player.Name.." arrived at a timed area too quickly; area number "..objectNumber)
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId..
"' started at area '"..currentArea.. "' and arrived at area '"..objectNumber.."' in '"..
os.time() - timeAtLastArea.."' seconds; "..(timeAtLastArea + minimumTimeBetweenAreas) - os.time().." seconds too quickly")
hackingDetected(player, customDetectionMessages["SpeedTimedAreaMsg"], specificInfo, nil, warningMessage)
else
currentArea = objectNumber
timeAtLastArea = os.time()
end
else
-- Player skipped an area
local warningMessage = (player.Name.." skipped a timed area; area number "..objectNumber"")
local specificInfo = ("Player named: '"..player.Name.."' with UserId: '"..player.UserId.."' started at area '"..currentArea.. "' and skipped to area '"..objectNumber.."'")
hackingDetected(player, customDetectionMessages["SkipTimedAreaMsg"], specificInfo, nil, warningMessage)
end
end
end
end
end
end
end
-- Run functions
while true do
if enabledFeatures["TimedPartChecking"] then
IsPlayerTouchingTimedParts()
end
if enabledFeatures["IllegalPartChecking"] then
IsPlayerTouchingIllegalParts()
end
task.wait(areaTouchingUpdateSpeed)
end
"EzBanStartup" code
------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
------------------------------------ Configuration options in "EzBanConfiguration" script ------------------------------------
------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
local configData = nil
local EzBanStartup = {}
function EzBanStartup.SendConfig(configOptions)
configData = configOptions
end
function EzBanStartup.GetConfig()
if configData then
return configData
else
error("Missing configuration data; Anti-Cheat is not active")
end
end
function EzBanStartup.CheckObjects()
local errorOccured = false
--[[ Check
* Main Folder intact
* Script Folder intact
* Illegal area parts Folder intact
* Timed area parts Folder intact
* Configuration Script intact
]]
local mainFolder
local scriptFolder
local illegalAreaPartsFolder
local timedAreaPartsFolder
local configurationScript
local success = pcall(function()
mainFolder = script.Parent.Parent
scriptFolder = mainFolder:FindFirstChild("EzBanAntiCheatScripts")
illegalAreaPartsFolder = mainFolder:FindFirstChild("EzBanAntiCheatObjects"):FindFirstChild("Illegal area parts")
timedAreaPartsFolder = mainFolder:FindFirstChild("EzBanAntiCheatObjects"):FindFirstChild("Timed area parts")
configurationScript = mainFolder:FindFirstChild("EzBanConfiguration")
end)
if not success then
errorOccured = true
error("A component of anti-cheat is missing; anti-cheat is corrupt!")
end
local function fail(missingPartName: string?)
errorOccured = true
if not missingPartName then
error("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
else
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Corrupt object: "..missingPartName)
end
end
local function checkObject(object:Instance, expectedName:string, expectedType:string, errorName:string)
if object.Name ~= expectedName or not object:IsA(expectedType) then
fail(errorName)
end
end
checkObject(mainFolder, "EzBan Obby Anti-Cheat", "Folder", "Main anti-cheat folder")
checkObject(scriptFolder, "EzBanAntiCheatScripts", "Folder", "Anti-cheat script folder")
checkObject(illegalAreaPartsFolder, "Illegal area parts", "Folder", "Illegal area parts folder")
checkObject(timedAreaPartsFolder, "Timed area parts", "Folder", "Timed area parts folder")
checkObject(configurationScript, "EzBanConfiguration", "Script", "Configuration script")
return errorOccured
end
function EzBanStartup.CheckConfig(configOptions)
local function checkOption(configOptionNumber: number, expectedType:string)
local objectValue = configOptions[configOptionNumber][2]
local objectName = configOptions[configOptionNumber][1]
local recievedType = type(objectValue)
if recievedType ~= expectedType then
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Expected a "..expectedType.." recieved a "..recievedType.." for configuration option "..objectName)
end
end
--[[ ConfigurationOptions:
[1] = superAdminUserIds
[2] = customDetectionMessages
[3] = punishmentType
[4] = banLength
[5] = maxFreeFall
[6] = updateSpeed
[7] = minimumTimeBetweenAreas
[8] = backwardsEnabled
[9] = expectedPlayerSpeed
[10] = expectedPlayerJumpHeight
[11] = allowedDifference
[12] = teleportingValueFolder
[13] = flatPlane
[14] = enabledFeatures
[15] = areaTouchingUpdateSpeed
]]
--configOptions[?][1] = name of config option; configOptions[?][2] = value of config option
checkOption(1, "table")
checkOption(2, "table")
checkOption(3, "number")
checkOption(4, "number")
checkOption(5, "number")
checkOption(6, "number")
checkOption(7, "number")
checkOption(8, "boolean")
checkOption(9, "number")
checkOption(10, "number")
checkOption(11, "number")
-- config option 12 is teleporting bool value folder but can also be nil if not used; can't use type()
if configOptions[12][2] ~= "NoValueFound" and type(configOptions[12][2]) == "userdata" then
-- Config option isn't nil
if not configOptions[12][2]:IsA("Folder") then
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Corrupt object: Teleporting value folder")
end
end
checkOption(13, "table")
checkOption(14, "table")
checkOption(15, "number")
end
return EzBanStartup
"EzBanConfiguration" code
------------------
-- Super Admins -- [1]
------------------
-- Notes: Super Admins can use the /addAdmin command, regular admins cannot
-- Type: Array containing UserIDs
-- Default: None
local superAdminUserIds = {
1609672088
}
-------------------------------
-- Custom detection messages -- [2]
-------------------------------
-- Notes: If you want to display custom messages you can change the current ones here (DO NOT DELETE MESSAGES, instead put message = "")
-- Type: Dictionary containing strings
-- Default: None
local customDetectionMessages = {
-- Player's walkspeed is too fast
WalkSpeedMsg = "You were detected using speed hacks",
-- Player's jump height is too high
JumpHeightMsg = "You were detected using speed hacks",
-- Player is moving too quickly along flat plane (configure which axes are used in "flat plane movement checking" [13])
SpeedFlatPlaneMsg = "You were detected using speed hacks",
-- Player is moving too quickly along only x-axis
SpeedXMsg = "You were detected using speed hacks",
-- Player is moving too quickly along only y-axis
SpeedYMsg = "You were detected using speed hacks",
-- Player is moving too quickly along only z-axis
SpeedZMsg = "You were detected using speed hacks",
-- Player entered an illegal area
IllegalAreaMsg = "You were detected using teleport hacks",
-- Player entered a new timed area too quickly
SpeedTimedAreaMsg = "You were detected using speed hacks",
-- Player skipped a timed area
SkiptimedAreaMsg = "You were detected using teleport hacks"
}
---------------------
-- Punishment type -- [3]
---------------------
-- Notes: Selected punishment when player is detected of hacking
-- Type: Number (Option list index)
-- Default: 2
--[[ Options:
1 = Ban player (Most severe punishment)
2 = Kick player from server (Normal punishment)
3 = Teleport player back to XYZ coordinates they were at before they were detected of hacking (Least severe punishment)
(option 3 only teleports player back if they are detected with the XYZ-Axes detection feature, otherwise the play will be killed/sent back to spawn location)
]]
local punishmentType = 2
-------------------
-- Length of ban -- [4]
-------------------
-- Notes: Ignore if punishment type is not "Ban player"; Set value to -1 for permanant ban
-- Type: Number (days)
-- Default: 1
local banLength = 1
-------------------------
-- Player Freefallling -- [5]
-------------------------
-- Notes: Maximum amount of time a player can freefall for
-- Type: Number (seconds)
-- Default: 10
local maxFreeFall = 10
--------------------------------
-- Player Speed Hack Checking -- [6]
--------------------------------
--[[ Notes:
-- How often the script will check the player's coordinates for speed hacking
-- Very low numbers (<0.2) can cause laggy players to get banned very easily
]]
-- Type: Number (seconds)
-- Default: 1
local updateSpeed = 1
--------------------------------
-- Minimum time between areas -- [7]
--------------------------------
-- Notes: Player will be punished if they get to the next "Timed area part" before the entered time
-- Type: Number (seconds)
-- Default: 10
local minimumTimeBetweenAreas = 10
--------------------------------------
-- Player moving backwards in areas -- [8]
--------------------------------------
-- Notes: Can player go backwards in timed areas (EX: player goes from area 2 to 1)
-- Type: Boolean
-- Default: true
local backwardsEnabled = true
--------------------------------
-- Expected player walk speed -- [9]
--------------------------------
-- Notes: What WalkSpeed should the player character have in your game
-- Type: Number (Studs per second)
-- Default: 16
local expectedPlayerSpeed = 16
---------------------------------
-- Expected player jump height -- [10]
---------------------------------
-- Notes: What JumpHeight should the player character have in your game
-- Type: Number (Studs per second)
-- Default: 7.2
local expectedPlayerJumpHeight = 7.2
----------------------------------
-- Player movement speed change -- [11]
----------------------------------
-- Notes: As player can sometimes move quicker than usual due to lag spikes, allow player to speed up a little
-- Type: Number (Studs per second)
-- Default: 5
local allowedDifference = 5
-----------------------------
-- Game teleport mechanics -- [12]
-----------------------------
--[[
Notes:
-- If your game contains teleporting, the anti-cheat will detect the player of hacking whenever they teleport;
To fix this follow the steps below
-- How to create player teleporting values:
1. Create a folder somewhere in "ServerStorage"
2. Index the folder below (EX: teleportingValue = game:GetService("ServerStorage"):FindFirstChild("PlayerBoolValues"))
3. When a player joins the game use a script to add a BoolValue to the folder
4. Make the BoolValue's name the same as the player who joined
5. Whenever the player is teleporting set their own bool value to true
]]
-- Type: Folder containing BoolValues that have the same name as the player
-- Default: nil
local teleportingValue = nil
----------------------------------
-- Flat plane movement checking -- [13]
----------------------------------
-- Notes: What axes does the player move along when walking
-- Type: Array
-- Default: {"X", "Z"}
--[[ Options:
1. "X"
2. "Y"
3. "Z"
]]
local flatPlane = {"X", "Z"}
---------------------------------
-- Enabled anti-cheat features -- [14]
---------------------------------
-- Notes: Enable or disable anti-cheat checking features
-- Type: Dictionary
local enabledFeatures = {
AdminCommands = true,
FlatPlanePositionChecking = true,
XYZPositionChecking = true,
WalkSpeedChecking = true,
JumpHeightChecking = true,
IllegalPartChecking = true,
TimedPartChecking = true
}
----------------------------------------
-- Player Illegal/Timed Area Checking -- [15]
----------------------------------------
-- Notes: How often the script will check if a player is touching illegal area parts or timed area parts
-- Type: Number (seconds)
-- Default: 0.1
local areaTouchingUpdateSpeed = 0.1
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
------------------------------------ End of configuration options ------------------------------------
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
mainFolder = script.Parent
local Startup
local startupSuccess = pcall(function()
Startup = require(mainFolder:FindFirstChild("EzBanAntiCheatScripts"):FindFirstChild("EzBanStartup"))
end)
if not startupSuccess then
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Corrupt object: Startup module script")
end
local configurationOptions = {}
--[[ ConfigurationOptions:
[1] = superAdminUserIds
[2] = customDetectionMessages
[3] = punishmentType
[4] = banLength
[5] = maxFreeFall
[6] = updateSpeed
[7] = minimumTimeBetweenAreas
[8] = backwardsEnabled
[9] = expectedPlayerSpeed
[10] = expectedPlayerJumpHeight
[11] = allowedDifference
[12] = teleportingValueFolder
[13] = flatPlane
[14] = enabledFeatures
[15] = areaTouchingUpdateSpeed
]]
if teleportingValue == nil then
teleportingValue = "NoValueFound"
end
local configSuccess = pcall(function()
table.insert(configurationOptions, {"superAdminUserIds", superAdminUserIds})
table.insert(configurationOptions, {"customDetectionMessages", customDetectionMessages})
table.insert(configurationOptions, {"punishmentType", punishmentType})
table.insert(configurationOptions, {"banLength", banLength})
table.insert(configurationOptions, {"maxFreeFall", maxFreeFall})
table.insert(configurationOptions, {"updateSpeed", updateSpeed})
table.insert(configurationOptions, {"minimumTimeBetweenAreas", minimumTimeBetweenAreas})
table.insert(configurationOptions, {"backwardsEnabled", backwardsEnabled})
table.insert(configurationOptions, {"expectedPlayerSpeed", expectedPlayerSpeed})
table.insert(configurationOptions, {"expectedPlayerJumpHeight", expectedPlayerJumpHeight})
table.insert(configurationOptions, {"allowedDifference", allowedDifference})
table.insert(configurationOptions, {"teleportingValue", teleportingValue})
table.insert(configurationOptions, {"flatPlane", flatPlane})
table.insert(configurationOptions, {"enabledFeatures", enabledFeatures})
table.insert(configurationOptions, {"areaTouchingUpdateSpeed", areaTouchingUpdateSpeed})
end)
if not configSuccess then
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Corrupt object: Couldn't load configuration options")
end
Startup.CheckConfig(configurationOptions)
local sendConfigSuccess = pcall(function()
Startup.SendConfig(configurationOptions)
end)
if not sendConfigSuccess then
warn("A component of anti-cheat is missing or changed; anti-cheat is corrupt!")
error("Corrupt object: Couldn't send configuration options to anticheat")
end
Startup.CheckObjects()
mainFolder:FindFirstChild("EzBanAntiCheatScripts").Parent = game:GetService("ServerScriptService")
mainFolder:FindFirstChild("EzBanAntiCheatObjects").Parent = game:GetService("ServerStorage")
local antiCheatScript = game:GetService("ServerScriptService"):FindFirstChild("EzBanAntiCheatScripts"):FindFirstChild("EzBanAntiCheat")
antiCheatScript.Enabled = true
-- Configuration script/read me script not needed anymore
mainFolder:Destroy()
Latest update
V 3.4.1 - 8/16/24
- Checks for errors in startup
- Added configuration script
- Added more configuration options
New configuration options
- teleportingValueFolder
- flatPlane
- enabledFeatures
- areaTouchingUpdateSpeed
- Scripts hidden from client in server script service
- Parts hidden from client in server storage
- Uses new roblox Ban API
- New character flat plane (X+Z) speed hack checking
- Updated Read me
- More lenient naming for timed area parts and commands
- Created DevForum community resource
Earlier updates
V 2.4.1 - 8/13/24
- Bug fix - Player death doesn’t halt script on certain exceptions anymore
V 2.4.0 - 6/7/24
- New player WalkSpeed speed hack check
- Custom detection messages
V 2.3.0 - 3/17/24
- New player JumpHeight speed hack check
- Better code readability
V 2.2.0 - 3/15/24
- Admin commands
- XYZ position checking
- Timed area parts
V 1.2.0 - 3/1/24
- All hacking detection handled in 1 function
V 1.1.0 - 2/19/24
- Variables at start of script to control things like BanLength
V 1.0.0 - 12/3/23
- Illegal area part detection
- Ban players when they get detected by anti-cheat
- Download here: Click Me!
- Then retrieve it from your “toolbox” in roblox studio
- Open the “Read me” script to learn more
Please reply to this topic if you find anything wrong with it!
- Maybe
- Yes
- Already have an anti-cheat
- No - Please reply on what needs to be fixed!
0 voters
By the way if you were wondering this post is 42,865 characters; 3,973 words; and 1,460 lines long not including this. Nice.