Is this cooldown bypassable

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.