I made this system to prevent exploiters spamming remote events and kicking/banning them if the server catches them firing faster than the cooldown. From my testing, I had this running for about 8 hours while the client was firing the event every 0.5 seconds (which is the cooldown) and it hasn’t had a problem.
client:
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local cooldown = 0.5
local lastCooldownTime = -cooldown
task.wait(8)
RunService.Heartbeat:Connect(function()
local currentTime = tick()
if currentTime - lastCooldownTime >= cooldown then
lastCooldownTime = currentTime
local serverTime = workspace:GetServerTimeNow()
ReplicatedStorage.RemoteEvent:FireServer(serverTime)
end
end)
server:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local cooldown = 0.5
local lastTime = -cooldown
ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function(player, clientTime)
local serverTime = workspace:GetServerTimeNow()
local timeTaken = serverTime - clientTime
local timeSinceLast = serverTime - timeTaken - lastTime
if clientTime > serverTime then
warn("Time too high by: ", clientTime - serverTime)
end
print(string.format("Event took %f seconds; time since last: %f", timeTaken, timeSinceLast))
if timeSinceLast < cooldown then
warn(string.format("Event fired %f seconds too early", cooldown - timeSinceLast))
else
lastTime = serverTime - timeTaken
end
end)
local timeSinceLast = serverTime - timeTaken - lastTime
Redundant to take serverTime - timeTaken
because that’s the exact same thing as clientTime
.
Thanks!
Is this bypassable though?
Definitely. If someone is aware of your cooldown script working like this, they can set their clientTime
to -2^30
then keep incrementing by 1.
I just tried this but the script caught the act instantly:
also tested with tick() and same thing
The print message “Event took…” still went through. In what cases do you expect the actual event body to be done?
the print and the warning popped up at the same time so it didn’t pass it
even if I added a print where the else block is, it wouldn’t print that
1 Like
Let’s try 0.5, 1.0, 1.5…
All in a sequence.
not sure what you mean but this
1 Like
In your client:
for i = 0.5, math.floor(tick()), 0.5 do
FireServer(i)
wait()
end
wow, that actually worked. thanks for showing this problem
3 Likes
This doesn’t relate to the post but I’d like give something similar I made
local tblDebouce = {}
local PlayerService = game:GetService("Players")
PlayerService.PlayerAdded:Connect(function(plr)
tblDebouce[plr.Name] = {} -- adds storage when player joins
end)
PlayerService.PlayerRemoving:Connect(function(plr)
tblDebouce[plr.Name] = nil -- removes storage when player leaves
end)
function addWaitPlr(plrName, WaitName, timeWait)
tblDebouce[plrName][WaitName] = true -- sets something for player which'll be used to detect if waiting
task.spawn(function() -- task.spawn used to allow script to keep running
task.wait(timeWait)
tblDebouce[plrName][WaitName] = nil -- removes the *wait* I guess after specified time
end)
end
RemoteEvent.OnServerEvent:Connect(function(plr)
if not tblDebouce[plr.Name]["Name Here"] then
addWaitPlr(plr.Name,"Name Here",0.25)
-- Code here :p
end
end)
This is basically cooldown for firing remote events, it doesn’t kick but you can easily add a else statement to kick the player.
Good point with the player table.
If you have many players in the same server, with the original poster’s script, then cooldowns would be violated all the time.
If theres a sudden ping spike for players who have bad internet, it may fire events that all get to the server at the same time once the client catches up and stops lagging. Maybe you dont want to kick them but just ignore the events.