Shared

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.

Import local Debounce = RoExpress.Debounce
Both wrappers return 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.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.

ParameterTypeDescription
fn(...) → ()The function to wrap
cooldownnumberMinimum 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.

ParameterTypeDescription
fn(key, ...) → ()The function to wrap. First arg is the key.
cooldownnumberMinimum 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)
For server-side rate limiting use TokenBucket — it supports burst capacity, refill rates, and integrates with the request pipeline. Debounce is better for client-side or lightweight one-liner cooldowns.

fn vs key

Debounce.fnDebounce.key
Timer scopeShared across all callersIndependent per first argument
Use caseAnnouncements, global events, single-caller actionsPer-player actions: shooting, purchasing, jumping
First argumentPassed to fn as-isUsed 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