Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > Writing a Library > Script Delays

Delays in In-MUD Scripting Languages

In a MUD, mobile scripts and object scripts tend to be pretty simple. They mostly go through a set of actions in order, with an occasional conditional (like an 'if' statement) or loop. So mostly LPC works very well for them, and is far more than they need. In LPMUDs, it's usually far and away the most convenient to just use LPC for all your scripting - it's fast, it's flexible, and you've already got a nice interface to everything your MUD does.

However, there's a problem with that. Specifically, there's a problem if you want your script to delay for a few seconds or a few minutes and then start happening again. Say that you want a mobile to look at what it's been given, wait a few seconds and then give it back disdainfully. It's important to wait that few seconds. The problem is that DGD is single-threaded, which means that execution rounds need to finish quickly so that the rest of the MUD can get on with what it's doing. A three-second delay loop isn't an option, and would halt the rest of the MUD, spoiling the effect. It doesn't work.

One way to do this is to schedule call_outs, or do something roughly similar. That's a bit ugly because you need to split your function into pieces -- "give_script_start", "give_script_after_delay_1", "give_script_after_delay_2", etc. However, once you get over the syntactic ugliness it's not too bad. It does what you want it to. You just have to cut your script up into lots of little sub-scripts.

You could write an entire other language on top of LPC, or perhaps modify LPC in some way for the scripting language. Or compile the language to LPC. Then you could interpret that language, which would let you pause it for delay statements and go back to what you were doing.

You could queue up actions in advance, but have LPC functions that put actions into the queue. So you'd have functions like mobile_do_run(), mobile_do_sharpen(), etc that would queue up actions like "run" and "sharpen" to be done when the current action is finished. Your scripts could have conditionals and other interesting code when they first ran, and add actions into the queue in variable ways. Unfortunately, that means that all control structures must have finished before any of the delays happen. That's not great. It means that mobiles will get stupider as the script goes on for more clock-time. And if a mobile needs to enter a room and then respond to it, it will need to use some variation of the call_out approach above.

A third possibility is to run the script again after each delay statement, and pass some kind of argument to it to tell it where it came back. So your function would use switch() or if() to jump to where you should be after the delay. A delay would become a return statement, along with how long you should delay, and then you'd get called again with an argument for how long you've delayed or something. Your builders would hate you and want to kill you, but it would work.

A popular variation on the above is to write an LPC preprocessor which will turn you delay() statement into a case for a switch() statement, and put a switch() inside your function to jump to the right case. It's potentially more annoying to deal with restoring values of variables this way, though. Remember that local variables lose their value if you jump out of the function and then back in.

From: DGD Mailing List (Par Winzell)
Date: Sun Jan  4 11:31:00 2004
Subject: [DGD] Delay statements in scripting

Merry is Skotos' LPC-based scripting system. It's pretty much straight 
LPC with 'additions' -- e.g. delays, which are implemented with the 
switch solution given by Dworkin above.

Merry is parsed using parse_string() and a rathed mutated version of 
Felix's old LPC-grammar-for-parse_string() grammar. It's broken down 
into a binary format and stored in LWO's. The LWO can reconstruct the 
Merry source or underlying LPC source on demand (the former for editing 
the script, the latter for compiling it to a real LPC object).

The systems keeps a cache of a few hundred compiled Merry scripts 
around, but they can be destructed and recompiled on demand.

One tip for anybody implementing this: remember that the compiled 
objects can't keep any real state. For delays, for example, the callouts 
themselves can't be kept in the generated object. You -must- be able to 
destruct such objects at will.

If I execute an action that requires a delay, it's my body that should 
keep the callout, and then relayeto the appropriate script handler when 
the callout expires.

Zell