Jason@Dragoness Home | MUDL documentation
This overview has a mixture of material aimed at people familiar with Sloth MUD and material aimed at people interested in the author's musings on languages.
MUDL stands for "MUD language", and is pronounced "muddle". It is a scripting language for making monsters, objects, and rooms in Sloth MUD that do stuff that is too complicated to handle with existing techniques.
Historically, we have done this sort of work in Sloth MUD by hard-coding it in C as part of the MUD source code. The result is that Sloth now has about 30,000 lines of very non-generic code, 15% of the total body of code, to make monsters behave in certain ways. Most of the content in Sloth MUD was generated by experienced players, but if that content was going to have complex behavior then the players had to be given access to the source code. This was not good. They could steal the source code and start their own competing MUDs, which has happened several times over the history of Sloth, but even worse it was all in C with pointers and segmentation faults and everything. A bunch of novice programmers with minimal supervision were writing an embedded system in C. Stability suffered as a result.
There was also the problem that many of the people creating content for the MUD didn't have access to the source code, and so they had very limited capabilities to put "special" behaviors in their content. They had to use one of the very specific and limited custom routines already present in the C code. For example, if they wanted their wizard to cast the "fireball" spell there was a way to do it, but if they only wanted him to cast "fireball" if he encountered an ice elemental there was no way to do that. With MUDL, everybody can make content with this sort of basic logic, and the C coding is reserved for the core functionality of the game.
The primary design goal of MUDL, more important even than functionality, was that it be difficult to break the MUD with it. It has been in place for about a year, and is doing pretty well. Now I will explain how I made it easy to use and hard to break.
MUDL is a large language (at least as MUD scripting languages go) which includes a library of more than a hundred system functions. It is, however, not Turing-complete. This was done deliberately; the missing elements are that (1) there is nothing in MUDL analogous to a goto and (2) there is no universal ability to set the values of variables.
The only construct in MUDL that allows looping is a foreach loop that executes a block of code once for each of the elements in an array. The first argument of the foreach statement is an expression that must have an array type (that is, it must evaluate to an array). When the interpreter encounters a foreach, the first thing it does is evaluate that expression and stick the resulting array someplace where nobody can mess with it while the loop is looping. Due to the lack of a general ability to set variable values, the loop index variable cannot be altered from inside the loop.
While MUDL code can't set loop index variables or most other variables, the user has the ability to create and set as many of a special type of writeable variable as he likes. MUDL only allows the user to create string variables. This is a deliberate design feature. The problem with storing the more complex data types like characters (in the sense of "Cast of Characters", not in the sense of "letters") and objects (in the sense "shovels, rakes, and implements of destruction") is that they can come and go and their identity isn't well defined. What if I kill a mold-encrusted zombie, and then another one is generated by the MUD? Is it the same zombie? Maybe not, but what if I kill Heinrich the Mad, Terror of Twickham Tower and eventually he is regenerated by the MUD? Theoretically it's the same guy; we sweep under the table the exact mechanism by which he is now Terrorizing again even though you killed him last Tuesday.
So, if MUDL stored Heinrich as a character-typed variable it might find that even though Heinrich is still around, the variable for him was no longer attached to that Heinrich because the Heinrich it was attached to was slain. Instead, it stores his name. The next time Heinrich is needed, the programmer can use the various system functions that MUDL provides for this purpose to find the best match to Heinrich. It may happen that there is no good match, a contingency that the programmer must deal with.
I have found that I rarely miss either of the lack of a general loop, or the lack of a general ability to set variables. I have run into a few cases where I had to make some pretty awkward MUDL code to do what I wanted, but I think it was worth it to avoid the possibility of infinite loops.
MUDL has a hard life because it does not act in isolation, but runs inside of a dynamic MUD with other entities (players, that 30,000 lines of C code mentioned earlier, etc.) wandering around in it doing things. You might issue an innocuous MUDL statement like
cmd(%1,'north')causing the being indicated by the variable %1 to move one room north. But there was a pit trap there! Full of spikes! And %1 fell into it, and DIED! And not only that, but since he wasn't a player the memory his data was occupying has now been FREED and REUSED! All before your poor MUDL script even gets to execute the next statement...
MUDL makes extensive use of null values to handle this. All variable types can be null, and any system function called with one or more null arguments is short-circuited (it isn't even called at all) and evaluates to null. This means if someone or something is destroyed by unforeseen circumstances in the middle of execution of some MUDL code, the system will very often handle it gracefully without requiring any special code to check for it: all of the code depending on the dead person being there just won't execute, but the rest of the script will carry on as normal.
Novice programmers (and most of the MUDL programmers in the world, as of this writing, are novice programmers) can be confused by the syntactical similarity between definition (=, in C) and tests for equality (==, in C). MUDL only uses this symbol for tests for equality, which are written =, and performs definition with the set statement. This is similar to the way it was long ago in BASIC, but even BASIC has moved away from this. I think it was a good distinction, and so MUDL has it.
MUDL's set command declaws some complex functionality by presenting it in a simple way. A novice programmer might wonder, "if the function height(%c) tells me how tall %c is, and I want to make %c 200 cm tall, then why can't I just say height(%c) = 200?" In MUDL he can. The command
set(height(%c),200)does just that. Internally, this works because the compiler recognizes that the first argument of the set command is special and should not be treated like it would be if encountered roaming free in the wild. It's only necessary to extend this special treatment one level down the tree; the arguments of the first argument of set can be compiled just as usual.
Each MUDL script is compiled into an internal form which is higher-level than machine code. The internal form is a tree. This tree is then executed by an interpreter at the appropriate time.
Sloth MUD and MUDL are written in C. MUDL uses lex and yacc to generate its compiler. I am not a compiler guy; for example, my lex code has dangerous trailing context and I don't even know what that means.