xiao.timer New
xiao.timer runs a function after a delay or at a fixed interval, and runs coroutines that can wait between steps.
A sequence of actions then reads top to bottom instead of being spread over counters in the think signal.
At a glance
Time base
- The clock is the sum of the frame
dt(the value given toon_frame(dt), a negative or non finitedtcounts as 0), starting at 0 when the VM starts. It only moves while the client renders frames. - A timer fires on the first frame whose clock reaches its due time.
after(0, f)fires on the next frame, never during the call. - Every timer runs on the render thread, at step 6 of the order of a frame: after the game signals (
world_change,entity_add,entity_remove,chat,damage), beforeon_frame,thinkand the paint listeners. Timers that are due on the same frame run by due time, then in creation order. - A timer that loops forever stops the VM like a blocking
on_frame. - A timer created while the timers of a frame are running fires on a later frame at the earliest.
- Clear VM drops every pending timer and coroutine without calling them.
A callback or a coroutine that raises an error is removed and the error is printed in the console (timer 3 removed: ..., timer 4 coroutine removed: ...), like a signal callback fired by the client. The other timers keep running.
Packet hooks and cmd hooks
after, every and cancel work inside a packet hook or a cmd hook, and their callback runs later on the render thread: this is the way to reach xiao.paint, xiao.config, xiao.notify or xiao.workspace from a hook. spawn raises an error there, because it starts running its function at once. See Execution contexts.
Functions
xiao.timer.after(seconds, fn): integer
xiao.timer.after(seconds: number, fn: function(id: integer)): integer
Calls fn(id) once, on the first frame at least seconds after the call. The timer is removed before fn runs.
Returns: the timer id, an integer that is never reused in the VM. Errors:
bad argument #1 to 'after' (non-negative finite number expected):secondsnegative,NaN, infinite or not a numberbad argument #2 to 'after' (function expected)too many timers (4096): pending timers and spawned coroutines together
xiao.timer.after(2, function()
print("two seconds later")
end)
xiao.timer.every(seconds, fn): integer
xiao.timer.every(seconds: number, fn: function(id: integer)): integer
Calls fn(id) every seconds until it is cancelled. It fires at most once per frame: when frames are slower than the interval it fires once per frame and does not catch up. every(0, fn) fires every frame.
Returns: the timer id.
Errors: same as after, with 'every' in the message.
local count = 0
xiao.timer.every(1, function(id)
count = count + 1
print("tick", count)
if count == 5 then xiao.timer.cancel(id) end
end)
xiao.timer.cancel(id): boolean
xiao.timer.cancel(id: integer): boolean
Stops the timer or the spawned coroutine id. A cancelled coroutine is never resumed. Works inside a hook.
Returns: true if id was pending, false otherwise (already fired, already cancelled, unknown id or not a number).
Errors: none.
local id = xiao.timer.after(10, function() print("never") end)
print(xiao.timer.cancel(id), xiao.timer.cancel(id))
true false
xiao.timer.spawn(fn, ...): integer
xiao.timer.spawn(fn: function(...), ...: any): integer
Creates a coroutine running fn(...) and starts it at once, in the caller, until its first wait, its first coroutine.yield() or its end. Later steps run on the render thread with the other timers.
Returns: the id of the coroutine, valid for cancel while it waits. The id is returned even when fn finished or failed during the first run (the failure is printed, not raised).
Errors:
bad argument #1 to 'spawn' (function expected)xiao.timer.spawn is not available inside a packet hook or a cmd hooktoo many timers (4096)
A plain coroutine.yield() inside a spawned coroutine waits one frame.
xiao.timer.spawn(function(name)
print("hello", name)
coroutine.yield()
print("one frame later")
end, "steve")
xiao.timer.wait(seconds): number
xiao.timer.wait(seconds: number): number
Pauses the running spawned coroutine until the clock has moved seconds further. wait(0) waits one frame.
Returns: the time actually waited, in seconds (seconds rounded up to whole frames).
Errors:
bad argument #1 to 'wait' (non-negative finite number expected)xiao.timer.wait must be called inside xiao.timer.spawn: called from the main chunk, a callback, or a coroutine created withcoroutine.createorcoroutine.wrap, even inside a spawned one
xiao.timer.spawn(function()
print("3")
xiao.timer.wait(1)
print("2")
xiao.timer.wait(1)
print("1")
end)
Complete example
A sequence written top to bottom: announce, wait for a world, then report the position every 5 seconds until the player leaves.
local timer = xiao.timer
local job = timer.spawn(function(label)
print(label, "waiting for a world")
while not xiao.game.world().in_world do
timer.wait(0.5)
end
print(label, "in world")
while xiao.game.world().in_world do
local me = xiao.game.player()
if me ~= nil then
print(("%s %.0f %.0f %.0f"):format(label, me.x, me.y, me.z))
end
timer.wait(5)
end
print(label, "left the world")
end, "tracker")
xiao.signal.add("chat", "stop_tracker", function(message)
if message == "!stop" then
print("tracker cancelled", timer.cancel(job))
end
end)