Tutorial on how to Anti Cheat on the Client Side, I guess.
Decided to make a tutorial show casing something nobody seems to be talking about. Hope you find it useful I guess.
Mechanism
There's an unintended feature(? more like a bug) for when you use modules. If you require a module script and disable or delete the script that's running it,Whatever you put inside the module script will continue to run.
Why, IDK, it was a bug I ran into a few years back that I just so happened to have remembered after someone brought up the topic about anti-cheats.
Anyways, this allows you to have a Client-Sided anti cheat system that canât be disabled.
Example of it being done on Server Side:
Module Script
while true do
task.wait(1)
print("Running")
end
return nil
Server Script in workspace
task.wait(5)
print("Requiring ModuleScript")
task.wait(1)
require(script.ModuleScript)
Video showcasing Script: Donât mind the lag spike when deleting, my laptop is garbage
In short, just make your anti cheat on a module and require it on a client-sided script
Usage
Usage
How you use this knowledge really depends on how your game works. Though to begin with, we can make an instance detector to detect when an instance is spawned. To make this we need- A remote function (to check if instance exists in server)
- Server sided script (to return the function)
- Client Sided script (the anti cheat detector, I guess)
- Module Script (just read the Mechanism on why we need it)
Im gonna assume you know how remote functions work and not explain anything about the remote function.
Server Script Code placed inside ServerScriptService:
local RemoteEvents = game:GetService("ReplicatedStorage").RemoteEvents
local toServer = RemoteEvents.toServer
toServer.ObjInServer.OnServerInvoke = function(plr,obj:Instance)
return obj
end
What this script does is gets the RemoteFunction, in this case itâs in ReplicatedStorage.RemoteEvents.toServer
where RemoteEvents and toServer are just folders for organization, then detects when the RemoteFunction has been invoked checking to see if an obj(any instance of sort) is replicated on the server. If it is replicated on the server itâll return the instance, if not, it will return nil.
Local Script Code placed inside StarterPlayerScript:
local ModuleScript = require(script:WaitForChild("ModuleScript"))
game:GetService("StarterPlayer").StarterPlayerScripts.ClientDetector:Destroy()
script:Destroy()
What this does is require the Module Script which is parented under the Local Script, we require the module script because itâs where weâll be writing our anti cheat code. Why we write it in the ModuleScriipt? Well go read Mechanism, if you havenât. Anyways, we later destroy any trace of the Script so the exploiter doesnât have any clue we have an anti cheat system.
Gonna be explained in sections so itâs easier to understand
ModuleScript Code Parented inside the Local Script:
local toServer = game:GetService("ReplicatedStorage").RemoteEvents.toServer
local ObjInServer = toServer.ObjInServer -- RemoteFunction
local plr = game:GetService("Players").LocalPlayer
Gets the RemoteFunction, player, and character of the Client.
local function ObjExsist(obj:Instance):boolean
if ObjInServer.Parent == nil then -- Checks if the RemoteFunction has been deleted
plr:Kick("Exploiting")
end
if not ObjInServer:InvokeServer(obj) then
task.wait(.2) -- prevents Parent error happening on "obj"
obj:Destroy()
end
end
What this function first does is, check if the RemoteFunction ObjInServer is parented is nil, basically checks if the Client has deleted the RemFunc, if so, kick the player, since if the Client has deleted the RemFunc, thatâll tell us itâs an exploiter.
Then it checks if the obj exists on the Server
if not ObjInServer:InvokeServer(obj) then
will either return the obj if it exists on the Server or nil if obj doesnât exists on the server. So basically, if object not in server then do. Anyways the next line of code deletes the object since it doesnât exists on the Server, and therefore, weâll know it was spawned in the Client.
workspace.ChildAdded:Connect(ObjExsist)
Pretty short and self explanatory, if something gets added to workspace, it calls the ObjExsists function (Not the RemoteFunction) and deletes it if it doesnât exists on Server.
Entire ModuleScript Code
local toServer = game:GetService("ReplicatedStorage").RemoteEvents.toServer
local ObjInServer = toServer.ObjInServer -- RemoteFunction
local plr = game:GetService("Players").LocalPlayer
local function ObjExsist(obj:Instance):boolean
if ObjInServer.Parent == nil then -- Checks if the RemoteFunction has been deleted
plr:Kick("Exploiting")
end
if not ObjInServer:InvokeServer(obj) then
task.wait(.2) -- prevents Parent error happening on "obj"
obj:Destroy()
end
end
workspace.ChildAdded:Connect(ObjExsist)
Video the entire script working I guess:
https://youtu.be/AujNr5B8QcE
My Version of Usage
My version of Usage
I HIGHLY recommend making your own and using maybe some of my code. This is due to the fact that your game probably uses different mechanics than mine. Which may result in the AntiCheat system deleting something that you wanted to stay.I wonât be explaining in depth how mine works either since itâs very long and I donât want to get into it. Thereâs comments if you plan on using it and Iâll just list the main facts,
- It checks if player has tampered with their Humanoidâs WalkSpeed, JumpPower, HipHeight, and PlatformStand and sets the properties back to itâs ServerSide
- It checks if any object is added to client and deletes it if it isnât in the Server. It also Kicks the player if the obj was a Local Script.
- It checks if any basepartâs cancollide has been turned off and sets it back to itâs server side.
- It makes an Attribute âCanCheckâ (Boolean) on literally everything so the script doesnât repeat itself and cause additional Calls on RemoteFunction. This may lag your game depending on itâs size.
- Thereâs a ignoreTbl that makes the AntiCheat ignore whateverâs in there.
Hereâs my entire Module Script Code I used, you can just replace the old one if you were for some reason following along with the tutorial.
My anti cheat module
--[[
RULES, I guess,
- If object or property wasn't replicated on server, the object will get deleted or property set back
- Adds a Attribute "CanCheck" (boolean) to literally everything so scipt doesn't repeat, so may cause
lag upon joining depending on your device
]]
local toServer = game:GetService("ReplicatedStorage").RemoteEvents.toServer
local ObjInServer = toServer.ObjInServer
local CheckProperty = toServer.CheckProperty
local ignoreTbl = {} -- anything in ignoreTbl will get ignored by anti cheat
local plr = game:GetService("Players").LocalPlayer
table.insert(ignoreTbl,workspace:WaitForChild("Camera",5)) -- prevent camera glitching
-- Checks if object is replicated on server
local function ObjExsist(obj:Instance):boolean
if ObjInServer.Parent == nil then
task.wait(.2) -- prevents Parent error happening
obj:Destroy()
plr:Kick("Exploiting")
return false
end
if not ObjInServer:InvokeServer(obj) then
if obj:IsA("LocalScript") then
plr:Kick("Exploiting")
end
task.wait(.2) -- prevents Parent error happening
obj:Destroy()
return false
end
return true
end
-- Checks if Humanoid and sets up functions if it is
local function CheckHumanoid(obj:Humanoid):boolean
if obj:IsA("Humanoid") then
obj:GetPropertyChangedSignal("WalkSpeed"):Connect(function()
if CheckProperty then
obj.WalkSpeed = CheckProperty:InvokeServer(obj,"WalkSpeed")
else
plr:Kick("Exploiting")
end
end)
obj:GetPropertyChangedSignal("JumpPower"):Connect(function()
if CheckProperty then
obj.JumpPower = CheckProperty:InvokeServer(obj,"JumpPower")
else
plr:Kick("Exploiting")
end
end)
obj:GetPropertyChangedSignal("HipHeight"):Connect(function()
if CheckProperty then
obj.HipHeight = CheckProperty:InvokeServer(obj,"HipHeight")
else
plr:Kick("Exploiting")
end
end)
obj:GetPropertyChangedSignal("PlatformStand"):Connect(function()
if CheckProperty then
obj.PlatformStand = CheckProperty:InvokeServer(obj,"PlatformStand")
else
plr:Kick("Exploiting")
end
end)
return true
end
return false
end
-- Checks if BasePart and sets up functions if it is
local function CheckBasePart(obj:BasePart):boolean
if obj:IsA("BasePart") then
obj:GetPropertyChangedSignal("CanCollide"):Connect(function()
if CheckProperty then
obj.CanCollide = CheckProperty:InvokeServer(obj,"CanCollide")
else
plr:Kick("Exploiting")
end
end)
obj:GetPropertyChangedSignal("Anchored"):Connect(function()
if CheckProperty then
obj.Anchored = CheckProperty:InvokeServer(obj,"Anchored")
else
plr:Kick("Exploiting")
end
end)
obj:GetPropertyChangedSignal("Parent"):Connect(function()
if CheckProperty then
if obj.Parent == nil and CheckProperty:InvokeServer(obj,"Parent") ~= nil then
plr:Kick("Exploiting")
end
else
plr:Kick("Exploiting")
end
end)
return true
end
return false
end
-- Checks if part added is replicated on server and does it again for every new child added
local function partAdded(obj:Instance)
obj:SetAttribute("CanCheck",true) -- Sets this so RemoteFunctions don't run more than twince
task.spawn(function() -- checks what class it is and sets it I guess
CheckHumanoid(obj)
CheckBasePart(obj)
end)
obj.ChildAdded:Connect(function(child)
if ObjExsist(child) and not obj:GetAttribute("CanCheck") then
partAdded(child)
end
end)
for _,stuff in obj:GetChildren() do
task.spawn(function()
partAdded(stuff)
end)
end
end
-- Starts checking if obj and any object added in obj, if client has messed with it
local function mainFunc(obj)
task.spawn(function()
for _,stuff in ignoreTbl do
if stuff == obj then
return
end
end
if ObjExsist(obj) and not obj:GetAttribute("CanCheck") then
partAdded(obj)
end
end)
end
plr:WaitForChild("PlayerGui",5).ChildAdded:Connect(mainFunc)
task.spawn(function() -- deletes any object added to Freecam script(script that's added by Default by Roblox)
mainFunc(plr.PlayerGui:WaitForChild("Freecam",10))
end)
plr:WaitForChild("Backpack",5).ChildAdded:Connect(mainFunc)
plr:WaitForChild("PlayerScripts",5).ChildAdded:Connect(mainFunc)
game:GetService("ReplicatedFirst").ChildAdded:Connect(mainFunc)
for _,stuff in workspace:GetChildren() do
task.spawn(function()
for _,trackobj in ignoreTbl do
if stuff == trackobj then
return
end
end
if ObjExsist(stuff) and not stuff:GetAttribute("CanCheck") then
partAdded(stuff)
end
end)
end
workspace.ChildAdded:Connect(function(child)
for _,trackobj in ignoreTbl do
if trackobj == child then
return
end
end
if ObjExsist(child) and not child:GetAttribute("CanCheck") then
partAdded(child)
end
end)
--[[ Used to check whats in ignoreTbl feel free to delete
local UserInput = game:GetService("UserInputService")
UserInput.InputBegan:Connect(function(input,isType)
if not isType then
if input.KeyCode == Enum.KeyCode.E then
print(ignoreTbl)
end
end
end)
]]
return nil
Hereâs the model of the Anti Cheat System I made if you want to test it out:
https://create.roblox.com/store/asset/103466681912144/Anti-Cheat-System
Really the main take away is the Mechanism, you can, at least as far as my knowledge about exploiters go, have an Client Side anti Cheat without having the exploiters just turn it off.