Debounce
Cooldown wrappers for functions. Two variants: a global cooldown for a single shared timer, and a per-key cooldown where each unique key (typically a player) has its own independent timer.
local Debounce = RoExpress.Debounce
true when the function was called and false when it was blocked
by the cooldown. You can ignore the return value — it's there if you need it for feedback or logging.
Debounce.default
local wrapped = Debounce.default(fn)
Wraps fn with a simple toggle-based debounce state.
Each time the returned function is called, the internal debounce value
alternates between true and false. The current
state is passed as the first argument to fn.
| Parameter | Type | Description |
|---|---|---|
fn |
(Debounce: boolean, ...) → () |
Function invoked on every call. The first argument is the current debounce state. |
Returns a function with the same arguments as fn (excluding the
prepended debounce state). Calling the returned function toggles the internal
state, invokes fn, and returns true.
Debounce.fn
local wrapped = Debounce.fn(fn, cooldown)
Wraps fn with a single shared cooldown. All calls share the same timer — the function can only
fire once per cooldown seconds regardless of who calls it.
| Parameter | Type | Description |
|---|---|---|
fn |
(...) → () |
The function to wrap |
cooldown |
number |
Minimum seconds between calls |
Returns a wrapped function with the same arguments as fn plus a boolean return
indicating whether the call went through.
local announceWin = Debounce.fn(function(winner)
Bridge.Emit("round.win", { winner = winner })
end, 5)
announceWin(player) -- fires
announceWin(player) -- blocked, returns false
task.wait(5)
announceWin(player) -- fires again
Debounce.key
local wrapped = Debounce.key(fn, cooldown)
Wraps fn with a per-key cooldown. The first argument of every call is used as
the key — each unique key gets its own independent timer. This is the standard pattern for per-player
cooldowns.
| Parameter | Type | Description |
|---|---|---|
fn |
(key, ...) → () |
The function to wrap. First arg is the key. |
cooldown |
number |
Minimum seconds between calls per key |
local onShoot = Debounce.key(function(player, data)
-- player is the key — each player has their own 0.5s cooldown
fireBullet(player, data.direction)
end, 0.5)
app:Post("/shoot", function(req, res)
local fired = onShoot(req.player, req.data)
res:Send({ fired = fired })
end)
fn vs key
Debounce.fn |
Debounce.key |
|
|---|---|---|
| Timer scope | Shared across all callers | Independent per first argument |
| Use case | Announcements, global events, single-caller actions | Per-player actions: shooting, purchasing, jumping |
| First argument | Passed to fn as-is | Used as the cooldown key and passed to fn |
See also
TokenBucket | burst-capable server-side rate limiting · Maid | lifecycle cleanup · Tamper | exploit detection for rate abuse