TinyMUX

Action Lists

Softcode

Action Lists

An action list is a sequence of commands separated by semicolons, stored in an attribute and executed when triggered. Action lists are the basic unit of MUSH programming.

Format

@va me = command1 ; command2 ; command3

Each command in the list is executed in order. The semicolons separate commands at parse time—they are not operators that can be nested or escaped inside function calls (function arguments may contain semicolons safely).

How Action Lists Execute

When an action list runs, each command in the list is added to the command queue as a separate entry. The queue processes entries in FIFO (first-in, first-out) order.

This has an important consequence: commands from other objects can interleave with yours between semicolons. If object A runs cmd1 ; cmd2 and object B runs cmd3 at the same time, the execution order might be cmd1, cmd3, cmd2. For atomic operations, see Semaphores.

The Command Queue

Every queued command is stored as a queue entry containing:

  • Executor (%!)—the object whose attribute is running
  • Enactor (%#)—the object or player that caused the action
  • Caller (%@)—the immediate caller (affected by u() and zones)
  • Command text – the command string to evaluate and execute
  • Positional arguments (%0 through %9)—set by @trigger or wildcard matches
  • Registers (%q0-%q9, %qa-%qz)—temporary variables carried from the triggering context
  • Wait time – for @wait entries, when to execute
  • Semaphore – for semaphore waits, which object/attribute to wait on

Queue Limits

The server enforces several limits to prevent runaway code:

LimitDefaultDescription
Function nesting depth500Maximum depth of nested [ ] calls
Notify nesting20Maximum depth of @notify chains
Queue entries per playerConfigurableMaximum pending commands per player
Function invocationsConfigurableMaximum function calls per command

When a limit is exceeded, the current command is aborted and an error message is generated.

Immediate vs. Deferred Execution

When a player types a command at the keyboard, it executes immediately—it is not queued. This is why interactive commands feel responsive.

Commands generated by action lists (from @trigger, $-commands, @aconnect, etc.) are queued. The queue is processed between player commands, giving interactive input priority.

@wait creates entries that are deferred until a specified time or semaphore signal. The timer resolution in TinyMUX is 100 nanoseconds internally, though practical resolution depends on the operating system’s scheduler.

@trigger

@trigger is the primary way to invoke an action list:

@trigger me/va = arg0, arg1, arg2

The arguments become %0, %1, %2 in the triggered attribute. The current register state (%q0-%qz) is copied to the new queue entry, allowing data to flow between action lists.

@wait and Timing

@wait defers a command:

@wait 10 = say Ten seconds have passed.
@wait obj/attr = say Semaphore was notified.
@wait obj/5 = say Either notified or five seconds, whichever comes first.
@wait/until 1700000000 = say Unix timestamp reached.

The semaphore form blocks until @notify obj/attr is called. Combined with the timeout form, this provides both synchronization and failsafe timeout behavior.

@halt and Queue Management

@halt removes all queued commands for an object. @halt/all (wizard-only) clears the entire queue. @ps displays the current queue contents. These are essential debugging tools for runaway code.

Related Topics: Semaphores, Queue, Looping, Stack.