Implemented in src/lua/luatimerapi.cpp.
Backed by SunLight::Concurrent::Timer. Unlike sp_wait
(which only pauses the ScriptProcessor queue), a timer
fires its callback repeatedly on its own schedule,
independent of the queue, from a background thread —
see the important thread-safety note below.
set_timer(id, intervalMilli, callback)Register a new periodic timer. id is any integer you
choose, used later to cancel it via reset_timer.
callback is a Lua function taking no arguments, called
every intervalMilli milliseconds until reset_timer(id)
is called.
Registering a set_timer with an id that's already
active is a no-op (logs an error) — always reset_timer
before re-registering the same id.
local shotsFired = 0
set_timer(1, 1000, function()
shotsFired = shotsFired + 1
print("ticks: " .. shotsFired)
end)
reset_timer(id)Stop and remove a timer previously registered with
set_timer. Safe to call even if you're not sure the timer
is still running — logs an error (not a crash) if id
doesn't exist.
reset_timer(1)
A timer's callback runs on its own background thread,
not the main game thread. The engine already serializes
every one of its own Lua entry points (per-frame update,
wave dispatch, etc.) against timer callbacks internally, so
calling ordinary engine primitives (sound_play,
sprite_get_pos, ...) from inside a timer callback is safe.
What isn't automatically safe: don't call reset_timer
on a timer's own id from inside that same timer's
callback — behavior in that specific case isn't
gracefully handled and this pattern hasn't been exercised.
If you need a one-shot timer, prefer a plain counter guard
inside the callback (do the work only once, then leave the
timer running idle, or cancel it from the main-thread
on_update instead of from within the callback itself):
local fired = false
set_timer(2, 500, function()
if fired then return end
fired = true
print("fires exactly once")
end)