Hello. I’m currently making my first serious project and have little experience with preventing exploitation. I figured I should change my cooldowns to be server-sided, red about sever-sided cooldowns and decided attributes are a good way to do them.
However there is something I’m skeptical about. Here’s the structure of my code.
-- localscript
if input.UserInputType == Enum.UserInputType.MouseButton2 then
if chr:GetAttribute("CD") == true then return end
event:FireServer()
-- animations and such play here
end
-- serverscript
event.OnServerEvent:Connect(function(plr)
plr.Character:SetAttribute("CD", true)
task.delay(1, function()
plr.Character:SetAttribute("CD", false)
end)
-- stuff handled by server goes here
end)
This creates a cooldown, but what I’m afraid of is the local script’s function happening more than once before cooldown is set.
Is it possible for somebody to trigger it twice or more before the server manages to set the cooldown? Does the server event happen instantly? Like for instance would somebody using a auto clicker or somebody with high latency be able to do this?
-- server script
local eventInProgress = false
event.OnServerEvent:Connect(function(whateverVariables)
if eventInProgress == false then
eventInProgress = true
-- whatever goes in the function here
wait(1)
eventInProgress = false
end
end
Hey, thanks for the reply. This does prevent the remote event’s function from happening again. But like I wrote in the code’s comments, there’s some things that happen after the line where the event is fired. The event wouldn’t do anything, but the lines after still happen.
A slightly hacky but valid workaround nonetheless would be to, on the client, :SetAttribute("CD", true) right after you check whether there already is an active cooldown.
The server will likely take a few ms to respond to the event, at which point the server will also :SetAttribute("CD", true), bringing the 2 environments back into sync. Then, after your task.delay, the cooldown is removed.
Either that, or use a local debounce var and have the task.delay logic on both sides - admittedly the workaround requires less code.
Alternatively you could check whether you’re already playing the animation on the client (as it’s likely more than a few ms long) and return if you are, prior to firing an event or trying to play animations etc again.
Yup! It’s exploit proof as all code runs synchronously; even non-main threads due to processor scheduling etc. Essentially the server will only truly run one line at a time, even utilising multithreading.