TinyMUX

Time and Scheduling

Softcode

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

FunctionReturns
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:

  1. Queue processing: Commands run between player input processing cycles. A burst of player activity can delay queue processing.
  2. OS scheduling: The server process may not wake up exactly when a timer expires.
  3. Database dumps: During a non-forked dump, the server pauses. All pending timers are delayed.
  4. 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().