I’d like to point out that no matter how much client security/hacky tricks you do, there’s always the possibility for an exploiter to get past it. If someone is dedicated enough, odds are they will write a whole script just to fight your clientside anti exploit.
For example, pretending I’m an exploiter, once I figure out that PlayerHandler is the antiexploit using a decompiler I can just disable or delete the script everytime it is cloned into my PlayerGui. I’m not entirely sure the contents of the AntiExploit local script itself, but let’s pretend you’re sending my client walkspeed to the server. I could just delete the RemoteEvent or I could create a script that modifies the RemoteEvents arguments (changing the walkspeed argument to what it was originally before I exploited and changed it) before the server recieves them. I could stop other local scripts from reading my data on the client.
Now odds are, you’ll never run into this type of exploiter. Exploiters that know how to do what I just illustrated are rare, but they definitely do exist. All it takes is for them to write it then mass release it on exploiter forums.
The safest thing you can do is just have server sanity checks. Check how fast the player is moving. Check if the player is going through walls with raycasting then punish them accordingly by pushing them back.
Note:
People were talking about performance issues and odds are you’re going to run into them. In 20 player servers every second you’re cloning 20 scripts, naming 20 scripts, destroying the previous 20 scripts and parenting the 20 scripts. I suggest changing wait(1) to wait(5) because then atleast you’re giving the engine some time to breathe.
This is definately true, I’m doing my best to handle all of the logic securely so it works properly. My game is very physics/gun system based, which is why I worry about humanoid walkspeed (when altering it from the server, it doesn’t seem to replicate). However, as you said, I will do sanity checks from the server to check the distance traveled (and rubber-band them back).
@TESLAC0IL That isn’t a very valuable answer, and no what you said is wrong. It isn’t not very “performant”. I suggest you read about the Task Scheduler.
The above syntax is considered better. In a case where wait doesn’t return anything, the below syntax would evaluate to false and wouldn’t run. This is of course, high unlikely but it’s better to be on the safest side. Rather on the safestsafest side.
Replying to @Infinite_Visions, client sided anti exploits will never work. A true anti exploit will be always server sided. If you can’t make your own, here is mine which is open sourced and very reliable and performant:
@TESLAC0IL That isn’t a very valuable answer, and no what you said is wrong. It isn’t not very “performant”. I suggest you read about the Task Scheduler to get a gist of how they work. However, here is a much better answer.
The above syntax is considered better. In a case where wait doesn’t return anything, the below syntax would evaluate to false and wouldn’t run. This is of course, high unlikely but it’s better to be on the safest side. I would rather say, on the safestsafest side which is honestly unnecessary.
Replying to @Infinite_Visions, client sided anti exploits will never work. A true anti exploit will be always server sided. If you can’t make your own, here is mine which is open sourced and very reliable and performant:
Thanks for the tip! I understand client sided anti-exploits do not work against advanced exploiters, I guess my goal was to stump the “noob” exploiters (who don’t actually know anything about how the game runs). However, I will switch to fully server sided.
Your “anti-cheat” can be bypassed in a couple of lines.
Last time I checked, :Destroy() disconnected all events.
local plr = game.Players.LocalPlayer
plr:WaitForChild("PlayerGui").ChildAdded:Connect(function(obj)
if obj:IsA("LocalScript") then obj:Destroy() end
end)
Also, if this doesn’t actually stop your script first, exploiters can just use the getconnections function to disable all connections on an event (which includes your .Stepped and all of your .ChildAdded and .ChildRemoved)
If they wanted to go a different (smarter) route, the can just:
local plr = game.Players.LocalPlayer
plr.CharacterAdded:Connect(function(char)
char:WaitForChild("Humanoid").Name = "YourAnticheatWillHangForeverNow"
end)
A fourth way they can bypass this altogether is to just hook __newindex and prevent your localscripts from actually setting any humanoid property that can affect them (walkspeed, jumppower, health).
Do not waste your time on client anticheats. They can be bypassed in 1/10th of the time it took to make them. Secure your server instead.
It might stop skids that can’t find a script for your game, however overtime exploit developers would discover your game and that exploit they made could prevent your script from re adding it. My best bet is just try adding a RemoteFunction that checks your script every minute and if something is wrong, you take action, and add sanity checks to your RemoteFunction. But please, don’t take my word for it, I’m not an expert at exploits.
Thanks for your feedback! I did end up scrapping the script, it was mostly a test. I ended up doing it all server sided, which has worked out well so far.
Lets say you do end up doing your local script client-sided (not recommended). If you parented it to nil, would exploiters ever be able to delete it? I’m just curious.
Exploits can see whats inside the mysterious place of nil. Basically they can still delete it if their injector api has getnilinstances or something similar. So setting a local script’s parent to nil won’t help
Exploits are often packed together so that an attacked system is checked against a wide range of vulnerabilities; once one or more are detected, the appropriate exploits enter. Exploit kits also widely use code obfuscation to avoid detection and encrypt URL paths to prevent researchers from unrooting them. Malicious developers even create exploit kits, which are collections of exploits often bundled with other software
local LocalPlayer = game:GetService("Players").LocalPlayer
local Character = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local HumanoidRootPart = Character.PrimaryPart
local RunService = game:GetService("RunService")
local function Disable()
for i,v in pairs(getconnections(RunService.Stepped)) do
if v.Function and typeof(v.Function) == "function" then
if string.lower(getfenv(v.Function).Name) == "antiexploit" then
v:Disable()
end
end
end
for i,v in pairs(getconnections(Character.ChildRemvoed)) do
if v.Function and typeof(v.Function) == "function" then
if string.lower(getfenv(v.Function).Name) == "antiexploit" then
v:Disable()
end
end
end
for i,v in pairs(getconnections(HumanoidRootPart.ChildAdded)) do
if v.Function and typeof(v.Function) == "function" then
if string.lower(getfenv(v.Function).Name) == "antiexploit" then
v:Disable()
end
end
end
end
Disable()
LocalPlayer.PlayerGui.ChildAdded:Connect(function(v)
if v:IsA("LocalScript") then
Disable()
end
end)