Timers, Alarms, and the WAIT Command

Updated: January 31, 2002 by Mike Potter (Zugg)

NOTE: This document refers to zMUD v6.26 and later.

Introduction

There are many situations in playing MUDs that involve time.  The MUD itself spawns new mobs and treasures on a timer (a "tick" timer), you might want to save your character periodically, or you might just want to issue a command to a MUD after a delay (especially if the MUD has a limit on how fast it will accept commands).

There have always been ways to control time in zMUD.  While you can't use zMUD to visit ancient Greece, you can still perform quite a lot of magic if you know what you are doing.

Tick Timer

The most basic timer in zMUD, which has been in zMUD since the original version, is the tick timer.  The intent of the tick timer is to synchronize with the timer used on the MUD to control spawns, to help you predict when the next "tick" on the MUD will arrive.  Every MUD has a different tick timer interval, although one minute seems to be common.  The "tick" on the MUD controls everything from mob and object spawning, to the weather changes.

The main difference between the Tick Timer in zMUD and other timers described later is the ability of the tick timer to synchronize with the MUD.  You initially set the rough interval of the tick timer using the #TS command:

#TS 60

would set the timer to a 60 second interval.  The real trick is using the #TS command without any argument to synchronize the timer.  If you can detect the actual tick on the MUD using a trigger, you can use the #TS command to adjust the tick interval to match the MUD exactly.  For example, if the weather changes on your MUD, you can use a trigger like this:

#TRIGGER {It is raining} {#TS}

Whenever that text is received from the MUD, the #TS command will tell the tick timer that tick just happened, and it will readjust itself accordingly.

The tick timer countdown is also shown in the status bar by default.  By right-clicking on this timer, you can select the Timer Dialog box.  In this dialog box you can change the timer interval, start and stop the timer, and change the command executed on each tick.  You can also turn off the "TICK IN 5 SECONDS" message from that dialog.

More Timers

In many cases you need more than one timer.  Some people try to use the tick timer for generic timers, but that's not what it is for.  Reserve the tick timer for the application it was intended (predicting MUD ticks).

The #ALARM command can be used to set up all sorts of timers.  The most common syntax for a recurring timer is:

#ALARM *nnn {commands}

where nnn is the timer interval.  For example:

#ALARM *10:00 {save}

will save your MUD character every 10 minutes.  Note that you can specify the time as a number of seconds, or as hours:minutes:seconds.

Executing commands at a specific time

You can also use the #ALARM command to execute a command at a specific time-of-day.  For example, the alarm:

#ALARM 12:00:00 {#echo "It's lunch time!"}

Will display the string "It's lunch time!" on the screen at 12 noon.  zMUD uses 24-hour time, so to set an alarm for 1 o'clock in the afternoon, use #ALARM 13:00:00

The MUD Connection time

Instead of executing commands at a specific time of day in the real world, you can also execute commands after being connected to the MUD for a certain amount of time.  For example, the alarm

#ALARM -5:00:00 {#echo "You've been playing 5 hours!"}

will fire after you have been connected to the MUD for 5 hours.  The minus sign - at the front of the trigger tells zMUD to use "MUD Time" instead of "Real Time" and can be used in front of most alarm commands.

Specific Times and Intervals

You can set up an alarm to fire at regular intervals at specific times.  For example, to execute a command every 10 minutes, but at 15-seconds after the minute, use:

#ALARM *:*10:15 {command}

The * tells the alarm command to match anything.  So, the first * matches any hour, the second *10 matches any minute that is an interval of 10 (0,10,20,30,etc), and the 15 matches only the 15-second moment in time.

Delaying an action

Sometimes you simply want to delay an action by a certain time.  For example, perhaps you want to wait 10 seconds and then cast a spell.  You'd use an Alarm like this:

#ALARM +10 {cast spell}

The + character tells zMUD to create a "one-time-only" alarm with the specified interval.  So, after a 10 second delay, the alarm fires, executes the command, then deletes itself.

You can also use a fractional number of seconds to delay short intervals:

#ALARM +0.5 {cast spell}

will cast the spell after a delay of only 1/2 second.  Windows can only time things with a resolution of 10 ms or so, so don't try a really small delay or it probably won't work.

Delays in Triggers

Often the commands you want to delay are the result of a trigger.  You might fire a trigger on some specific text that tells you when you should execute another command after a delay.  You can use the Wait trigger states for this.  For example, the trigger:

#TRIGGER {You are tired.} {sit}
#CONDITION {} {stand} {Wait|Param=5000}

will watch the MUD for the text "You are tired.".  When this text arrives, the "sit" command is sent to the MUD.  Five seconds later (5000 ms), the "stand" command is sent to the MUD by the Wait condition added to the trigger.  For more examples of trigger conditions, see the Advanced Trigger guide.

The #WAIT Command

Ahh, the dreaded #WAIT command.  This is probably the most heavily abused command in zMUD, and the cause of many scripting problems.  Early versions of zMUD did not have the #ALARM command, so only the #WAIT command could be used.  The general rule of thumb now is: DO NOT USE #WAIT UNLESS YOU MUST!  Repeat this to yourself 10 times.

OK, if you promise not to use the #WAIT command, I'll go ahead and tell you how it works and what it is really used for.  The #WAIT command is used for adding delays to loops in zMUD.  That's all.  Any other time delay can be accomplished using the #ALARM and Trigger Wait condition.

So, if you wanted to send the numbers 1-5 to the MUD with a one-second delay between each number, you would do this:

#LOOP 1,10 {%i;#WAIT 1000}

There is no easy way to do this using the #ALARM command, so you are allowed to use #WAIT for this.  Do NOT use #WAIT within a trigger!!

Repeat: DO NOT USE #WAIT WITHIN A TRIGGER!  Why?  Here is a simple example:

#TRIGGER {You lose concentration} {#WAIT 5000;recast spell}

Now, we all know you can do this using a Wait condition like this:

#TRIGGER {You lose concentration}
#CONDITION {} {recast spell} {Wait|Param=5000}

or you could use an alarm like this:

#TRIGGER {You lose concentration} {#ALARM +5 {recast spell}}

But, since we are talking about the evils of the #WAIT command, lets look at the #WAIT example.  If you run it, it seems to work.  So what is the problem?  What happens if you get the text twice before the 5 seconds is over.  For example:

You lose concentration
<3 seconds later>
You lose concentration

Perhaps you tried to execute a spell twice?  Now, if Windows was a perfect system, what we expect to happen is that the command "recast spell" is sent 5 seconds after the first text, then sent again 5 seconds after the second text.  But this isn't what happens.  Because of how Windows works (as we'll see in a minute), you get the command "recast spell" sent twice, 8 seconds after the initial text (the 3 second delay between the text plus the 5 second delay in the #WAIT command).

So what happened?  Why did the #WAIT command from the first text get displayed 8 seconds later along with the second one?  Well, the #WAIT command tells Windows to pause for the given amount of time.  But Windows continues to process messages (to avoid freezing the application).  3 seconds later you get the second message, and this causes the #WAIT command to run again, pausing Windows for 5 seconds.  But Windows only has a single execution thread by default.  So, while that second command is waiting for 5 seconds, the original wait command is also waiting.  When the second command is done, it then returns to the first command, which notices that more than 5 seconds is up and executes.  So, both triggers wait the full 8 seconds.

If zMUD was multi-threaded, this wouldn't be a problem.  Each #WAIT command could run in a separate thread and then they wouldn't interfere.  But multi-threaded programs are *very* complex to write, and zMUD was originally written before threads were available.  In particular, different threads cannot access the same resource without complex locking and synchronization, and the screen output is one of these resources.  Trying to resolve the synchronization problem so that each thread could write to the screen would be enormously difficult.

So, just don't worry about any of this technical jargon.  Just stop using the #WAIT command and start using #ALARM and Trigger Wait states instead.  You're scripts will work a lot better then!