SableAC
Hey Developers!
Today I present something I’ve been working on recently, this is SableAC or better yet: Sable AntiCheat
SableAC is my first ever project I have released to the public so I apologise if this is not the best format for me to present it!
SableAC - https://create.roblox.com/store/asset/17565484699/SableAC
SableAC is a free open-source AntiCheat module capable of the following:
Noclip Detection,
Fly Detection,
Miniumum Join Requirements,
Admin Bypasses,
Remote Security Checks
Click below for Setup instructions
How to Setup SableAC
First off you’re going to want to aquire the asset from the creator store which you can then add it to your toolbox.
From there, you want to make a SERVER script to require the module (which should be located in ServerScriptService
.
Inside the server script you want the following:
local SableAC = require(game:GetService("ServerScriptService".SableAC)
SableAC:Init()
That’s all the code you need to startup the AntiCheat!
If you don’t use a server script the script will be automatically terminated (See below)
if not RunService:IsServer() then
warn("SableAC is not running on the server. This script will be terminated shortly.")
for i = 3, 1, -1 do
warn(i)
wait(1)
end
warn("Terminated.")
script:Destroy()
else
print("\nAuthor: FBIagent9903\nVersion: 1.0.0\nBuildID: 1.0.0.202405211852\nSableAC")
end
The Settings!
Full Settings
local Settings = {
ENFORCE_SECURE_EVENTS = true, -- whether or not to check if someone is firing remote events properly
FLY_DETECTION = true,
NOCLIP_DETECTION = true,
SPEED_DETECTION = true, -- NEXT RELEASE
JUMP_DETECTION = true, -- NEXT RELEASE
ENFORCE_JOIN_REQUIREMENTS = true, -- if people need to pass the listed requirements to join the game
USER_BYPASS = {
{289394644, false} :: UserIdBypass
},
LIGHT_REQUIREMENT_CHECK = { -- bypass people requirements IF they are given the "true" value as the second index
Account_Age = 100
},
FULL_REQUIREMENT_CHECK = { -- if ENFORCE_JOIN_REQUIREMENTS is true then these will be the requirements that are checked
Account_Age = 100,
Friend_Count = 15,
Group_Count = 5
},
FLY_SETTINGS = {
RefreshStrikes = 6, -- how many times player can exceed max height until they are refreshed
KickStrikes = 5, -- how many times a player gets refreshed before they are kicked from anticheat
MaxHeight = Vector3.new(0, -15, 0), -- how high can the player go before being refreshed :: -15 - (-20) recommended
HBTime = 15, -- how many HeartBeats until the game checks all players
PlatformStandingStrikes = true
},
NO_CLIP_SETTINGS = {
KickStrikes = 2, -- How many times the game can flag an individual for noclipping before kicking them.
HBTime = 15
}
}
The main settings you want to change are these:
FLY_DETECTION = true,
NOCLIP_DETECTION = true,
SPEED_DETECTION = true, -- NEXT RELEASE
JUMP_DETECTION = true, -- NEXT RELEASE
ENFORCE_JOIN_REQUIREMENTS = true, -- if people need to pass the listed requirements to join the game
These toggle whether certain parts of the AntiCheat are actually used. So if you disable the NOCLIP_DETECTION value the noclip detection will not work and will be :Disconnect()
'ed.
Another setting you might want to change is…
FULL_REQUIREMENT_CHECK = { -- if ENFORCE_JOIN_REQUIREMENTS is true then these will be the requirements that are checked
Account_Age = 100,
Friend_Count = 15,
Group_Count = 5
},
Basically changes what the requirements are for people to join the game if ENFORCE_JOIN_REQUIREMENTS is equal to true
.
Module Specific Settings:
FLY_SETTINGS = {
RefreshStrikes = 6, -- how many times player can exceed max height until they are refreshed
KickStrikes = 5, -- how many times a player gets refreshed before they are kicked from anticheat
MaxHeight = Vector3.new(0, -15, 0), -- how high can the player go before being refreshed :: -15 - (-20) recommended
HBTime = 15, -- how many HeartBeats until the game checks all players
PlatformStandingStrikes = true
},
Here everything is already prelabeled, and is pretty clear. For MaxHeight
only change the Y Axis
otherwise it will flag people for flying even though they’re on the Z
or X
axis. Finally, ensure that the Y Axis
value always remains as a negative otherwise it will flag someone as flying even if they are not as it tracks the players distance from the floor which is a negative increase as they are in the air!
Anything below 15 could possibly cause the game to think they are flying when they are just doing a basic jump!
Thanks For Reading This Post and Trying Out SableAC.
As I said earlier, this is my first ever open-source script and it is in alpha version 1.0.0-alpha.
Full Script
--------------------------------
---------- CREDITS -------------
--------------------------------
--[[
Author: FBIagent9903
Version: 1.0.0
BuildID: 1.0.0.202405211852
Sable Anti Cheat
DO NOT CHANGE ANY OF THE CODE OUTSIDE OF SETTINGS UNLESS YOU UNDERSTAND WHAT YOU ARE DOING!!!
---------------------------------------------------------------------------------------------
To use SableAC, you must make a SERVER script in ServerScriptService and do require(path.to.SableAC) and then call the :Init() method.
Example:
require(game:GetService("ServerScriptService").SableAC):Init()
Outside Credit:
AntiFly Script: Ph4ntomize :: https://devforum.roblox.com/t/how-to-make-a-basic-anti-fly-script/1389277/5
]]
--------------------------------
---------- SERVICES ------------
--------------------------------
local GroupService = game:GetService('GroupService')
local PlayersService = game:GetService('Players')
local RunService = game:GetService('RunService')
--------------------------------
-------- METAMETHODS -----------
--------------------------------
local SableAC = {}
SableAC.__index = SableAC
SableAC.__tostring = function(self)
return ("\nAuthor: FBIagent9903\nVersion: 1.0.0\nBuildID: 1.0.0.202405211852\nSableAC")
end
--------------------------------
-------- DEPENDECIES -----------
--------------------------------
local random = Random.new()
--------------------------------
-------- CUSTOM TYPES ----------
--------------------------------
export type UserNameBypass = {
Name: string,
doCheck: boolean
}
export type UserIdBypass = {
Id: number,
doCheck: boolean
}
--------------------------------
------- USER CONTAINER ---------
--------------------------------
SableAC.UserContainer = {}
--------------------------------
----------- SETTINGS -----------
--------------------------------
local Settings = {
ENFORCE_SECURE_EVENTS = true, -- whether or not to check if someone is firing remote events properly
FLY_DETECTION = true,
NOCLIP_DETECTION = true,
SPEED_DETECTION = true, -- NEXT RELEASE
JUMP_DETECTION = true, -- NEXT RELEASE
ENFORCE_JOIN_REQUIREMENTS = true, -- if people need to pass the listed requirements to join the game
USER_BYPASS = {
{289394644, false} :: UserIdBypass
},
LIGHT_REQUIREMENT_CHECK = { -- bypass people requirements IF they are given the "true" value as the second index
Account_Age = 100
},
FULL_REQUIREMENT_CHECK = { -- if ENFORCE_JOIN_REQUIREMENTS is true then these will be the requirements that are checked
Account_Age = 100,
Friend_Count = 15,
Group_Count = 5
},
FLY_SETTINGS = {
RefreshStrikes = 6, -- how many times player can exceed max height until they are refreshed
KickStrikes = 5, -- how many times a player gets refreshed before they are kicked from anticheat
MaxHeight = Vector3.new(0, -15, 0), -- how high can the player go before being refreshed :: -15 - (-20) recommended
HBTime = 15, -- how many HeartBeats until the game checks all players
PlatformStandingStrikes = true
},
NO_CLIP_SETTINGS = {
KickStrikes = 2, -- How many times the game can flag an individual for noclipping before kicking them.
HBTime = 15
}
}
local Strikes = {
KickStrikes = {},
GlobalStrikes = {},
NoClipStrikes = {}
}
--------------------------------
------ PRIVATE FUNCTIONS -------
--------------------------------
local function typeEqualsUserIdBypass(entry: any): boolean
return typeof(entry) == "table" and entry[1] ~= nil and entry[2] ~= nil and typeof(entry[1]) == "number" and typeof(entry[2]) == "boolean"
end
local function typeEqualsUserNameBypass(entry: any): boolean
return typeof(entry) == "table" and entry[1] ~= nil and entry[2] ~= nil and typeof(entry[1]) == "string" and typeof(entry[2]) == "boolean"
end
function doCheck(player: Player): boolean
return player.AccountAge >= Settings.LIGHT_REQUIREMENT_CHECK.Account_Age
end
function bypass(player: Player): boolean
for _, entry in pairs(Settings.USER_BYPASS) do
print(entry, type(entry))
if typeEqualsUserIdBypass(entry) then
if entry[2] == true then
return doCheck(player)
else
if player.UserId == entry[1] then
return true
else
continue
end
end
elseif typeEqualsUserNameBypass(entry) then
if entry.doCheck == true then
return doCheck(player)
else
if player.Name == entry[1] then
return true
else
continue
end
end
end
end
return false
end
function userCanJoinGame(user: Player): boolean
local requirements = Settings.FULL_REQUIREMENT_CHECK
local attempts = 0
local function group()
return #GroupService:GetGroupsAsync(user.UserId) >= requirements.Group_Count
end
local function friends()
local s, r = pcall(function()
return PlayersService:GetFriendsAsync(user.UserId)
end)
if s then
local userFriends = 0
while true do
wait(0.1)
userFriends += #r:GetCurrentPage()
if r.IsFinished then
break
else
r:AdvanceToNextPageAsync()
end
end
return userFriends >= requirements.Friend_Count
else
attempts += 1
warn(r)
if attempts < 5 then
return friends()
else
user:Kick("AntiCheat ran into an error evaluating player! Please attempt rejoining")
return
end
end
end
local function age()
return user.AccountAge >= requirements.Account_Age
end
return group() and friends() and age()
end
local function ResfreshPlayerPos(player: Player, Pos: Vector3)
player:LoadCharacter()
player.Character:SetPrimaryPartCFrame(CFrame.new(Pos))
if player.Character:FindFirstChildWhichIsA("ForceField") then
player.Character:FindFirstChildWhichIsA("ForceField"):Destroy()
end
warn("Refreshed",player.Name)
end
local function newCode()
local lettersIndex = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}
local code = ""
for i = 1, 20 do
local rl = lettersIndex[random:NextInteger(1, #lettersIndex)]
if random:NextNumber() > .5 then
rl = string.upper(rl)
end
code = code .. rl
end
end
local function listenForInvoke(event: RemoteFunction)
event.OnServerInvoke = function()
return SableAC.securityCode
end
end
--------------------------------
------ PUBLIC FUNCTIONS --------
--------------------------------
function SableAC:Init()
local int = 0
local nint = 0
game.Players.PlayerAdded:Connect(function(player)
if not userCanJoinGame(player) and not bypass(player) then
player:Kick("You do not meet the minimum requirements for this experience!")
else
print("Game requirements met")
Strikes.KickStrikes[player.Name] = 0
Strikes.GlobalStrikes[player.Name] = 0
Strikes.NoClipStrikes[player.Name] = 0
end
end)
PlayersService.PlayerRemoving:Connect(function(plr)
Strikes.KickStrikes[plr.Name] = nil
Strikes.GlobalStrikes[plr.Name] = nil
Strikes.NoClipStrikes[plr.Name] = nil
end)
--------------------------------
------ FLIGHT DETECTION --------
--------------------------------
local FlightDetection = RunService.Heartbeat:Connect(function(dt)
--print(dt)
if int >= Settings.FLY_SETTINGS.HBTime then
int = 0
local Characters = {}
for _, player in pairs(PlayersService:GetPlayers()) do
if player.Character then
table.insert(Characters, player.Character)
end
end
local Params = RaycastParams.new()
Params.FilterType = Enum.RaycastFilterType.Blacklist
Params.FilterDescendantsInstances = Characters
for _, player in pairs(PlayersService:GetPlayers()) do
if player.Character then
local root = player.Character:FindFirstChild("HumanoidRootPart")
local hum = player.Character:FindFirstChildWhichIsA("Humanoid")
local head = player.Character:FindFirstChild("Head")
if hum and hum.Health > 0 then
if not root then
ResfreshPlayerPos(player, head.Position)
continue
end
local ground = workspace:Raycast(root.Position, Settings.FLY_SETTINGS.MaxHeight, Params)
if not ground then
if Settings.FLY_SETTINGS.PlatformStandingStrikes then
if hum:GetState() == Enum.HumanoidStateType.PlatformStanding then
Strikes.GlobalStrikes[player.Name] += 2
else
Strikes.GlobalStrikes[player.Name] += 1
end
end
if Strikes.GlobalStrikes[player.Name] >= Settings.FLY_SETTINGS.RefreshStrikes then
if Strikes.KickStrikes[player.Name] >= Settings.FLY_SETTINGS.KickStrikes then
player:Kick("AntiCheat has suspected you for cheating.")
continue
end
local groundPos = root.Position
local g = workspace:Raycast(root.Position, Vector3.new(0, -300, 0), Params)
if g then
groundPos = g.Position + Vector3.new(0, 5, 0)
end
Strikes.GlobalStrikes[player.Name] = 0
ResfreshPlayerPos(player, groundPos)
Strikes.KickStrikes[player.Name] += 1
end
end
end
end
end
end
int += 1
end)
--------------------------------
------ NOCLIP DETECTION --------
--------------------------------
local noClipDetection = RunService.Heartbeat:Connect(function(dt)
if nint >= Settings.NO_CLIP_SETTINGS.HBTime then
nint = 0
for _, player in pairs(PlayersService:GetPlayers()) do
if not player.Character then
continue
else
for _, v in pairs(player.Character:GetChildren()) do
if v:IsA("BasePart") then
if v.CanCollide == false then
v.CanCollide = true
Strikes.NoClipStrikes[player.Name] += 1
if Strikes.NoClipStrikes[player.Name] == Settings.NO_CLIP_SETTINGS.KickStrikes then
player:Kick("AntiCheat has suspected you for cheating.")
continue
end
end
end
end
end
end
end
end)
if not Settings.FLY_DETECTION then
FlightDetection:Disconnect()
elseif not Settings.NOCLIP_DETECTION then
noClipDetection:Disconnect()
end
end
function SableAC.onStartup()
local SableRemoteFunction = Instance.new("RemoteFunction"); SableRemoteFunction.Parent = game.ReplicatedStorage
SableRemoteFunction.Name = "SableACRemote"
SableAC.securityCode = newCode()
listenForInvoke(SableRemoteFunction)
if Settings.ENFORCE_SECURE_EVENTS then
for _, remote in pairs(game:GetDescendants()) do
if remote:IsA("RemoteEvent") or remote:IsA("RemoteFunction") then
local s,r = pcall(function()
remote.OnServerEvent:Connect(function(player, securityCode, ...)
if securityCode ~= SableAC.securityCode then
player:Kick("AntiCheat has suspected you for cheating.")
end
end)
end)
if not s then
local s2, r2 = pcall(function()
remote.OnServerInvoke = function(player, code, ...)
if code ~= SableAC.securityCode then
player:Kick("AntiCheat has suspected you for cheating.")
end
end
end)
if not s2 then
warn(string.format("SableAC | Encountered an error evaluating remote %s with error\n%s", remote.Name, r2 or r))
end
end
end
end
end
end
if not RunService:IsServer() or RunService:IsStudio() then
warn("SableAC is not running on the server. This script will be terminated shortly.")
for i = 3, 1, -1 do
warn(i)
wait(1)
end
warn("Terminated.")
script:Destroy()
else
print("\nAuthor: FBIagent9903\nVersion: 1.0.0\nBuildID: 1.0.0.202405211852\nSableAC")
end
SableAC.onStartup()
return SableAC
[I’m adding a small poll to see if people like it]
- FBIagent9903
- SableAC is Good!
- SableAC is Meh
- SableAC is Bad
0 voters