local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild(“Humanoid”);
local backpack = player:WaitForChild(“Backpack”);
local torso = character:FindFirstChild(“Torso”) or character:FindFirstChild(“UpperTorso”)
local root = character:FindFirstChild(“HumanoidRootPart”);
local function destroy()
player:Kick(“Nice try exploiter, say good bye!!”)
player.Character:BreakJoints()
humanoid.Health = 0
root.CFrame = CFrame.new(1,-10000,1)
end
coroutine.resume(coroutine.create(function()
game:GetService("RunService").RenderStepped:Connect(function()
local exploiters = {}
# humanoid.Changed:Connect(function(t)
if t == "WalkSpeed" then
table.insert(exploiters,player.Name)
print('exploiter added')
humanoid.WalkSpeed = 16
destroy()
else
if t == "JumpPower" then
table.insert(exploiters,player.Name)
print('exploiter added')
humanoid.JumpPower = 50
destroy()
else
if t == "HipHeight" then
table.insert(exploiters,player.Name)
print('exploiter added')
humanoid.HipHeight = 0
destroy()
else
if t == "Name" then
table.insert(exploiters,player.Name)
humanoid.Name = "Humanoid"
print('exploiter added')
destroy()
end
end
end
end
end)
backpack.ChildAdded:Connect(function(obj)
if obj:IsA("HopperBin") then
obj:Destroy()
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
end
end)
character.ChildAdded:Connect(function(obj)
if obj:IsA("HopperBin") then
obj:Destroy()
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
end
end)
humanoid.StateChanged:Connect(function(oldstate,newstate)
if newstate == Enum.HumanoidStateType.StrafingNoPhysics then
humanoid:ChangeState(Enum.HumanoidStateType.RunningNoPhysics)
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
end
end)
humanoid.Running:Connect(function(speed)
if speed > 17 then
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
end
end)
root.DescendantAdded:Connect(function(t)
for z,x in ipairs(blacklisted) do
if string.find(x,t.ClassName) then
table.insert(exploiters,player.Name)
print('exploiter added')
wait()
t:Destroy()
destroy()
end
end
end)
torso.DescendantAdded:Connect(function(t)
for z,x in ipairs(blacklisted) do
if string.find(blacklisted,t.ClassName) then
table.insert(exploiters,player.Name)
print('exploiter added')
wait()
t:Destroy()
destroy()
end
end
end)
character.ChildRemoved:Connect(function(obj)
if obj.Name == "Humanoid" and root ~= nil then
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
end
end)
character.DescendantAdded:Connect(function(t)
local tool1 = character:FindFirstChildOfClass("Tool")
if tool1 ~= nil and tool1 ~= t then
table.insert(exploiters,player.Name)
print('exploiter added')
destroy()
tool1:Destroy()
t:Destroy()
end
end)
repeat wait()
until game.Players.LocalPlayer and game.Players.LocalPlayer.Character and # game.Players.LocalPlayer.Character.Humanoid
There’s no need to distinguish this anymore. All games have the same protection now, since experimental mode is gone.
As for the script, I didn’t read the whole thing since I could tell by the start of it that it will only work from the client. It isn’t a very strong anti exploit at all since anything on an exploiter’s client can be modified to suit their own malicious needs, but since you already have it I would just keep it as an extra layer. It’ll almost definitely catch some exploiters, but you really need an anti exploit that runs entirely on the server.
Now, sorry to disappoint you but this is almost equal to no protection at all
First of all, if anyone in the server changed their walkspeed, every single player would get kicked, because you don’t distinguish players in your RenderStepped.
Second of all, you kick from the client. An exploiter can hook the kick function with 10 lines of code and disable your kick. So yea.
My tips:
Prioritize server-sided checks. Feel free to add client-sided ones afterwards, though don’t fully rely on them, but rather have them as a little “extra” thing. But only if you know what you’re doing!!! If you don’t know how to set up client-sided checks properly, then you should rather refrain from implementing them.
Never trust the client. This is something you will always hear when it comes to securing your games. Check most, if not all imput from the client.
Don’t make the client tell the server what to do! Don’t make some SetCash remote events or such. Don’t make the client tell the server the price of the thing you’re buying.
You don’t seem to use the exploiters table in your script (although it makes sense as Player:Kick() only works on the local player in a localscript) so all the insert lines should probably be removed. Also, I believe that a lot of speed exploiting done recently doesn’t actually change WalkSpeed, so that protection won’t protect from those specifically either.
A common Antikick will make this Antiexploit useless by simply changing the __namecall metamethod from DataModel/game slightly. (As Kiriot22 mentioned)
Also, since you index WalkSpeed, JumpPower etc. exploiters can just check if you index those properties and return a valid value, while they are changed.
Of course the simplest solution would be to just delete or disable the script.
You can always replace the coroutine wrap with the keyword “spawn” Although if you need to pause the routine and add a new input then do keep using it.
Another point to make is that, if you absolutely need a client script, make sure the server is constantly checking it exists. Using a remotefunction is a great way to check, but be sure to make the call a pcalled function with a delay (use the delay keyword to not pause the entire thread) so as to both make sure lag is accounted for and that it will still kick if there is an error.
Your feedback request is incomplete. Code Review is not a venue for open-ended asking of suggestions and showcasing code. Please read the category guidelines.
You are missing:
Explanation of code (it’s blatantly obvious, I’m aware, but one would be appreciated)
What is unsatisfying about this code to you
What improvements you have already attempted to apply
What specific improvements you seek in your code
Also, how come you have not corrected your code block? It’s been explained to you how to do so yet you have not corrected the code block above trying to reply to posts. It’s harder to read what you’re trying to post without proper formatting. Please fix your code block as soon as possible.
I’m making a hex-based multi-player, RTS wargame with Fog Of War.
The full map on the Server.
On the client you can see any action going on in a hex next to any hex, one of your units is in.
If you move your unit away; I Destroy any hex you can’t see; on your client, but a hacker, could stop the Destroy.
How do check this exploit on the Server?
Is anything replicated from Client to Server, which could piggy-back some useful information?
(i won’t kick him; just degrade his abilities enough, so he always loses)
[I don’t think any dev should Kick. Don’t we want the hacker in the dark, and him to spread a useless hack?! I hate them more than most do.]
The way I would approach this, probably not the most efficient:
Store the actual game data on the server, i.e. unit position’s, unit type, research, whatever is hidden by default
When client1 moves a unit/reveals a tile, tell the server that the unit has moved, check that they can, and once you’ve done that, the server should tell client1 any data revealed from the moving of the unit and to remove the stuff that’s been moved out of range. The server should tell all players that can see the unit that it has moved and to act accordingly.
This has the benefit that even if the client doesn’t destroy the unit on their screen, they won’t know where it’s going, so it doesn’t really have an advantage.
game.Players.LocalPlayer:WaitForChild("ClearTile").OnClientEvent:connect
(function(Obj, NegObj)
-- object is a list of Tile .Names to reveal
for i = 1, #Obj do -- reveal these Tiles
local cloud = container[Obj[i].Name]
-- container is a local model of the globe and all Tile model shells.
-- It only holds the initial cloud cover
-- or current placeholder graphic of old snapsot
cloud.OldInfo:ClearAllChildren() -- remove old snapsot
if cloud:FindFirstChild("Mesh") then cloud.Transparency = 1
cloud.Mesh:Destroy()
end
Obj[i].Parent = workspace.globe --Flip "lightswitch" to On
end
wait(.5)
for i = 1, #NegObj do -- Erase these Tiles
NegObj[i]:clone().Parent = container[NegObj[i].Name].OldInfo -- take snapsot
greyOut(container[NegObj[i].Name].OldInfo[NegObj[i].Name]) -- Turn B/W
NegObj[i].Parent = nil -- Flip switch to Off
-- This is the line which could be bypassed. But how can I check?
end
end)
So...?
Simply put, you don’t, because there is no need to, as long as you are following my instructions. The exploiter may still be able to see the unit, but they won’t see where it moves or what it does. This is not really an unfair advantage, since anyone can remember where a unit was or note it down on aper.
Sorry, did not mention: For this to work automatically a unit is Parented to the Tile it is in,
so Local Player could see if a Unit moved out of a Tile it is not supposed to see, or a unit oved into a Tile…
Unfortunately, I think the only way to make exploiter proof is to do the replication manually. I would create the actual model of the unit on the client, and have the unit’s position on the server. The server only sends remote events to the clients that can actually see it.
I hear you but there is no way I’m going to do that… That is:
100s of Parts > 1 Tile.Name(Oh, I want every unit to be an individual. People love that…)
Sent to 5 more Clients > 1 Client
for each Unit moved from one Tile to another
I just need a sanity check every once in a while on the Server, that a Client isn’t seeing a Hex which it isn’t supposed to…