What do you want to achieve?
I would like to be able to hinder exploiters from ruining game experiences.
What is the issue?
The issue is that somehow and I don’t know-how exploiters run code that allows them to get 20k strength ever second. The way for players to get this strength is through activating a tool, which then sends a remote event to the server and then the servers check if they did lift and then add strength to a bool value, which is then made equal to equal the leaderstats.
The exploiter went from 0 strength to 100k strength in less than a minute…
CODE:
--Varaibles
local RS = game:GetService("ReplicatedStorage")
local aB = RS.remoteE:WaitForChild("addBanana")
--IngameEvents
local GlobalEvent = 1
--Events
aB.OnServerEvent:Connect(function(plr)
if plr.Character.Humanoid or plr.Character:WaitForChild("Humanoid") then
local pSChildren = pS:GetChildren()
local depth = plr.Character.Humanoid.BodyDepthScale.Value
local height = plr.Character.Humanoid.BodyHeightScale.Value
local width = plr.Character.Humanoid.BodyWidthScale.Value
local head = plr.Character.Humanoid.HeadScale.Value
local humMaxHealth = plr.Character.Humanoid.MaxHealth
local humSpeed = plr.Character.Humanoid.WalkSpeed
local msize = 0.003 --For testing = 0.05
local gPassSize = 0.009
local bsize = 0.003 --For testing = 0.07
local gHeadSize = 0.003
local health = 0.155
local speedV =0.0055
local punchpowerV = 0.0045
local twogamepass = 2
local morehealthpass = 1.5
for i,v in pairs (game.ServerStorage.playerStats:GetChildren()) do --LOOPS THROUGH A FOLDER WITH VALUES
if v.Name == plr.Name then
if v.gamePasses.twoSP.Value == true then
v.strength.Value = v.strength.Value + (1 * GlobalEvent * twogamepass)
plr.leaderstats.Strength.Value = v.strength.Value
else
v.strength.Value = v.strength.Value + (1 * GlobalEvent )
plr.leaderstats.Strength.Value = v.strength.Value
end
v.totalStrength.Value = v.strength.Value
end
end
end
end)
What solutions have you tried so far?
I have tried passing the values through different places to hinder the addition to the leaderboard however that did not help.
I hope you guys can help me out and show me where I might be able to better my code and try to fix this problem!
Remotes need to be protected meaning that you must make a Check to see if Remote is allowed because Client are the area that Exploiters can attack and so they can send any remotes: AddBanana must have a ServerSide Check, Can we see you localscript that run that function?
As @Trystanus has said, you need some server sided checks, which are called sanity checks. Since you don’t check any logic in your Server it’s open to all exploiters to just fire your remote. To answer if anyone has the question, no, you can’t prevent exploiters from firing your remote event as they have full control over their whole client. Use those sanity checks to your advantage and prevent exploitation of your remotes.
An example would be to check if the player is holding a tool if your game is based off tools or something along the lines that would be logical to check for. Another example would be, a player would not be able to back-stab someone from 100 studs away, so they would check the magnitude of the player.
You also want to be mindful to not give the exploiter too much detail when you are firing your remotes, if you have loads of parameters it’ll be much easier for them to figure out a way to bypass your system.
--Variables
local RS = game:GetService("ReplicatedStorage")
local aB = RS.remoteE:WaitForChild("addBanana")
local BananaTool = script.Parent
local playeZ = game.Players.LocalPlayer
local deb = false
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://04915073005"--04869325755 (Glitched Anim)
local sound1 = Instance.new("Sound")
sound1.SoundId = "rbxassetid://990183576"
sound1.Volume = 0.3
sound1.Parent = BananaTool
BananaTool.Activated:Connect(function()
if playeZ.Character.Humanoid or playeZ.Character:WaitForChild("Humanoid") then
if not deb then
deb = true
local animload = playeZ.Character:WaitForChild("Humanoid"):LoadAnimation(anim)
animload:Play()
sound1:Play()
RS.remoteE.addBanana:FireServer()
wait(2)
deb = false
end
end
end)
However I do check the remote event on the server side?
If it’s a tool, you can access the Tool.Activated event through a server script.
I would just have a server script parented to the tool and do everything in there
On the client side they can spam fire a remote so if you have any remote you can spam to get coins you maybe need to add an debounce. with a remote event they will fire the function on the server
A debounce needs to be implemented on both the client and server. One on the client so that it may reduce spamming, and one on the server for sanity checks to make sure they haven’t bypassed the denounce on their client. Another check to implement is checking if they have the tool equipped on the server
That’s just going to put everyone on a cooldown. What you can do is make a table for the player upon joining and when they fire the event on their client, we add the strength on the server and apply a cooldown. During the cooldown, strength will not be added if the exploiter managed to bypass the cooldown on their client:
local cooldowns = {}
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
cooldowns[player] = false
end)
-- code in the RemoteEvent on the server
if not cooldowns[player] then
cooldowns[player] = true
-- add strength and do whatever
wait(5)
cooldowns[player] = false
end
-- end of code for the event
Players.PlayerRemoving:Connect(function(player)
cooldowns[player] = nil
end)
That’s how you make a normal cooldown but if it’s a server event, this would make all players share that cooldown.
If you had a single script inside a Tool object, you could do something like this:
-- Server Script
Tool = script.Parent
inCooldown = false
Tool.Activated:Connect(function()
plr = game.Players:GetPlayerFromCharacter(Tool.Parent)
if not inCooldown then
inCooldown = true
local pSChildren = pS:GetChildren()
local depth = plr.Character.Humanoid.BodyDepthScale.Value
local height = plr.Character.Humanoid.BodyHeightScale.Value
local width = plr.Character.Humanoid.BodyWidthScale.Value
local head = plr.Character.Humanoid.HeadScale.Value
local humMaxHealth = plr.Character.Humanoid.MaxHealth
local humSpeed = plr.Character.Humanoid.WalkSpeed
local msize = 0.003 --For testing = 0.05
local gPassSize = 0.009
local bsize = 0.003 --For testing = 0.07
local gHeadSize = 0.003
local health = 0.155
local speedV =0.0055
local punchpowerV = 0.0045
local twogamepass = 2
local morehealthpass = 1.5
local v = game.ServerStorage.playerStats:FindFirstChild(plr.Name)
if v then
if v.gamePasses.twoSP.Value == true then
v.strength.Value = v.strength.Value + (1 * GlobalEvent * twogamepass)
plr.leaderstats.Strength.Value = v.strength.Value
else
v.strength.Value = v.strength.Value + (1 * GlobalEvent )
plr.leaderstats.Strength.Value = v.strength.Value
end
v.totalStrength.Value = v.strength.Value
end
wait(2)
inCooldown = false
end
end)
(I changed the for loop to a FindFirstChild)
Though even this has potential security flaws.
Namely, if the user got their hands on multiple copies of the tool, they could spam all of them at once.
so this final method is a little better
-- Server Script
Tool = script.Parent
Debris = game:GetService('Debris')
Tool.Activated:Connect(function()
plr = game.Players:GetPlayerFromCharacter(Tool.Parent)
local v = game.ServerStorage.playerStats:FindFirstChild(plr.Name)
inCooldown = plr:FindFirstChild('inCooldown')
if not inCooldown then
cooldownObj = Instance.new('BoolValue')
cooldownObj.Name = 'inCooldown'
cooldownObj.Parent = plr
-- Add the cooldown object to Debris service's wait list so that it's deleted after 2 seconds
Debris:AddItem(cooldownObj,2)
local pSChildren = pS:GetChildren()
local depth = plr.Character.Humanoid.BodyDepthScale.Value
local height = plr.Character.Humanoid.BodyHeightScale.Value
local width = plr.Character.Humanoid.BodyWidthScale.Value
local head = plr.Character.Humanoid.HeadScale.Value
local humMaxHealth = plr.Character.Humanoid.MaxHealth
local humSpeed = plr.Character.Humanoid.WalkSpeed
local msize = 0.003 --For testing = 0.05
local gPassSize = 0.009
local bsize = 0.003 --For testing = 0.07
local gHeadSize = 0.003
local health = 0.155
local speedV =0.0055
local punchpowerV = 0.0045
local twogamepass = 2
local morehealthpass = 1.5
if v then
if v.gamePasses.twoSP.Value == true then
v.strength.Value = v.strength.Value + (1 * GlobalEvent * twogamepass)
plr.leaderstats.Strength.Value = v.strength.Value
else
v.strength.Value = v.strength.Value + (1 * GlobalEvent )
plr.leaderstats.Strength.Value = v.strength.Value
end
v.totalStrength.Value = v.strength.Value
end
end
end)