Time and Scheduling
Time and Scheduling
MUSH servers operate on an event-driven model. Commands are queued and executed in order, with timing controlled by @wait and the server’s internal scheduler. Understanding time resolution and scheduling behavior is important for writing reliable timed code.
Timer Resolution
TinyMUX tracks time internally using 100-nanosecond ticks (hectonanoseconds), stored as 64-bit integers. This is the same resolution as Windows FILETIME and provides sub-microsecond precision.
In practice, the actual resolution of @wait is limited by the operating system’s process scheduler, typically 1-10 milliseconds on modern systems. But the internal precision means that accumulated timing errors are minimal even over long periods.
@wait
The basic timing command:
@wait 5 = say Five seconds later.
@wait 0.5 = say Half a second later.
@wait accepts fractional seconds. The command is queued and executed after the specified delay.
@wait/until
Waits until an absolute time:
@wait/until [add(secs(), 3600)] = say One hour from now.
The argument is a Unix timestamp (seconds since 1970-01-01). This is useful for scheduling events at specific times rather than relative delays.
Time Functions
| Function | Returns |
|---|---|
secs() | Current Unix timestamp |
time() | Human-readable time string |
timefmt(fmt) | Formatted time string (strftime-style) |
convtime(str) | Parse time string to Unix timestamp |
starttime() | When the server started |
restarttime() | When the server last restarted |
connrecord() | Peak connection count |
Building a Timer
A repeating timer uses @trigger recursion:
@va timer = say Tick!; @wait 60 = @trigger me/va
@trigger timer/va
This says “Tick!” every 60 seconds. To stop it, @halt timer.
A more robust pattern stores state:
@va timer = @switch [get(me/RUNNING)]=1,{
say Tick [secs()];
@wait 60 = @trigger me/va
}
@RUNNING timer = 1
@trigger timer/va
Set RUNNING to 0 to stop the loop gracefully.
Timing Accuracy
Several factors affect when a queued command actually executes:
- Queue processing: Commands run between player input processing cycles. A burst of player activity can delay queue processing.
- OS scheduling: The server process may not wake up exactly when a timer expires.
- Database dumps: During a non-forked dump, the server pauses. All pending timers are delayed.
- Queue limits: If the queue is full, new entries may be rejected.
For most game purposes, @wait is accurate to within a few hundred milliseconds. Do not rely on sub-second precision for game mechanics.
Semaphore Timing
@wait can combine a semaphore with a timeout:
@wait obj/30 = say Timeout after 30 seconds.
@notify obj
The command executes when either @notify is called or 30 seconds elapse, whichever comes first. This is the standard pattern for implementing timeouts on player input or asynchronous operations.
Cron-like Scheduling
For daily or periodic events, use @wait/until with calculated timestamps:
@startup cron = @trigger me/va
@va cron = @dolist [get(me/TASKS)] = @trigger me/##;
@wait/until [add(secs(), 86400)] = @trigger me/va
This triggers daily tasks every 24 hours starting from server boot.
Related Topics: @wait, Semaphores, Action Lists, secs().