This is attributed to Stephen White (ghond), and is believed to be the original design document for MOO. ---------------- First off, let me admit publicly what most of you already know: I've been bitten by the MUD programming bug, a most heinous disease. Ever since I first logged in to TinyHELL, my life has been a tumult of conflicting desires: desires to investigate this bizarre domain, desire to see how this interesting program works, perhaps write one of my own, desire to hang out with some of the way-cool people on MUD's, and of course, desire to pass my courses (!). Of course, my twisted and perverse sense of priorities (or lack of any priorities at all?) deemed that I write a MUD. First off, I soon realized that TinyMUD, as a way of writing multi-user interactive fiction and creating a virtual reality, was quite limited. (I also realize this is not the sum total of what TinyMUD is, but this is the part that I was most interested in fiddling with). My first thought was, "well, you can't seem to make objects _do_ anything. You can pick them up, and drop them, and maybe they'll give off funny messages, but they don't do anything." What I wanted was: - greater flexibility in object manipulation, and more "realism" in terms of what objects do, specifically by allowing _actions_, attached to objects, rooms and players, which _did_ things (ie., caused other objects to appear, disappear, move around in other rooms, etc.) - a user interface that wasn't too ugly; perhaps no harder to use than TinyMUD - a program which used no more space on a per-object basis than TinyMUD So I grabbed the TinyMUD code, spent a few weeks figuring out the code and designing my extensions, and in one sleepless weekend wrote the first version of TinyMUCK. A neat hack, but not without its problems. First off, most things aren't exactly intuitive. The "linking" metaphor is not ideal: linking to objects summons them; linking to rooms takes you to that room; linking to players takes you to their location; linking to other links activates them. Not very orthogonal. People started saying things like, "this is like programming; perhaps we need a programming language." I started thinking about what TinyMUD is. Okay, so it's like a virtual-reality, whose neat attribute is that you can modify the VR from _inside the database itself_. No need to have separate compilation or external control; you're actually "in there", modifying the way things work. This seemed to be the antithesis of edit-compile-run programming, whose natural interface is "batch mode", rather than interactive. Also, since the server has to handle a whole bunch of other people, programs absolutely _cannot_ hog the server. So, no unbounded loops, no "goto", no recursion. Okay, so here's a dilemma: my experiments with TinyMUCK seemed to indicate that a programming language was needed, but this seems violate the nature of the VR. Hmm. Well, I talked with James Aspnes about it, and he seemed to be in favour of a programming language. After talking with him, he convinced me that it was the way to go. After all, TinyMUD is _slightly_ batch-mode, in that you have to do things in a certain order: dig a room, open an exit, link an exit, describe things, etc. So I went ahead. This time, I took about two months desgining, and gathered ideas from people I talked to, both here and on MUD's. As far as the coding, I opted out of the "one crazy weekend" approach, for the better of the code and my own mental health. I've been taking my time. It's still not quite finished; there are quite a few rough edges, and a lot of features I'd like to add. I'm not saying it's the MUD for everyone; what I'm into may not be what you're into, but as far as puzzle-creation, virtual reality, and a lot of other applications, I think you might like it. This is going to be a basic summary of what MOO does, and some of how it does it. I'll also mention what it doesn't do, and what I'd like it to do in future. (Note: This file is much longer than I intended it to be. Sigh.) Some Thanks ----------- First, I must give great thanks to James Aspnes for developing the original TinyMUD. If it weren't for him, I wouldn't have spent the last few months of my life in such delightful chaos. He also gave me some great ideas for how to go about things, also the use of the TinyMUD server code, since I'm definitely socket-stupid. Some Warnings ------------- I haven't played extensively with LP-Mud or Aber-MUD, so I don't know how these systems do programmability, or much else. Also, I may have made the same mistakes as some of these programs did in early version, or whatever. Also note that MOO has a really pretentious name--it's only partly object-oriented, in that the way you create classes and things is object-oriented, but the actual programming language is not. An Overview ----------- In creating MOO, I had a number of design goals: 1) Programmability. I wanted to allow connecting users to add to the way things work in a number of ways: 1. Creating objects. This is similar to what TinyMUD allows you to do. You can create an object (by instantiating a class), and set properties of that object (strings and numbers). In MOO, there is no distinction between players, rooms, or things. There is no 'exit'-type object; this is done with verbs (see "Verbs"). 2. Programming objects. To add new things you can do to an object, you 'program' new verbs on it. Alternately, you can create a new class of object (a container, for instance) to which you attach the new programs for manipulating it. 3. Creating new classes. This is the aspect of MOO which is object-oriented. When you create an object, you specify a class of thing you want to create. The new object "inherits" all the properties (variables) and verbs (programs) attached to the class, and all classes above that class in the hierarchy. 2) Speed. I wanted my program to run with a fair number of users on a machine somewhat less powerful than a Cray. To this end, there are a number of things in the code which are hard-coded, and provide some restrictions in what you can do. Nonetheless, with a bit of planning and care, a player can quite easily add new things to the game. 3) Ease-of-use. I wanted the game to be usable by CS-majors and non-CS-people alike. Therefore, I tried to make it fairly easy to make simple things, with a natural extension to more complicated things. The Commands ------------ I think the best way to get going with this thing is to dive right into it, by giving a list of commands, and what they do. The following is a list of commands in MOO as of today (2 May 90). (Note that this was achieved by logging on to MOO and typing "help".) say - say something : - do a pose page - page a player help - give help .newclass - create a new class .create - instantiate a class (create an object) .show - look at the internals of an object (like examine) .string - add a string property to an object or class .verb - add a verb to an object or class .rmverb - remove a verb .number - add a numeric property .move - move an object (like @teleport) .find - find an object (like @find) .findclass - show the class hierarchy .list - list a verb's program .set - set a property .default - set the default value for a property .program - program a verb .end - stop programming .dobject - set the direct object for a verb .iobject - set the indirect object for a verb .preposition - set the preposition for a verb .chown - change owners of an object .dump - dump the database .shutdown - shut down the server .wall - tell all players a message Commands in the first set are player commands, the second set builder commands, and the third set wizard commands. Objects ------- Objects in MOO have a number of pre-defined attributes: ID: Each object has a unique identifier, or number, just as in TinyMUD. Name: This is the string which is displayed for an object's name. Handles: These are the words with which you can refer to something. For example, an object with the name "BMW 2000" might have the handles "BMW 2000 car automobile". To refer to an object within a command, you give a set of handles which uniquely identifies the object in your environment (see "Matcher"). You can also refer to an object with the #n reference, like TinyMUD. Class: The class of an object tells you what properties and verbs it inherits from the parent class. Location: This is where the object is located. All objects are inside something, except for #0, which isn't inside anything (this is how the minimal database starts up). Contents: This is what the object contains. Objects a player is carrying are stored here, as well as the people and objects in a room. Owner: This is who owns the object. Flags: There are eight built-in flags, at this point: player, builder, wizard, dark, public, matchable, accessible, and forward (see "Flags"). Lookfunc: This is the name of the verb to be executed when a player enters an object (usually a room). The usual lookfunc is "look". Verb definitions: This is a list of the verbs which are attached directly to the object. The object can also be manipulated using any verbs it inherits from its class (see "Verbs"). Property definitions: This defines the numeric and string properties which are attached directly to this object. For example, a lightbulb object might have a numeric property which indicated whether it was on or not. A player object might have a string property which represented their description, and was triggered by a "look" verb. A property definition (or "propdef") contains the name of the property, the type (string or numeric), and the default value (shown in square brackets in .show). Properties: This is where the actual values of an object's properties are located. Both inherited properties and directly attached properties are listed: their names, and the current value. Classes ------- Each class has a number of built-in attributes: ID: This is a unique class identifer (a number), similar to the object ID. References to class by ID are of the format &n, where n is the class ID. Name: The name of the class. You can also use this to refer to a class. Owner: Classes are either "***PUBLIC CLASS***", which means anyone can access them, or owned by the player who created them. The .publish command (not implemented yet) will allow Wizards to make private classes public. Parent: The parent of a class is the class from which it is descended, in the class hierarchy. Verb definitions: As with objects, this is a list of what commands are defined on the class. Any class verbs can be over-ridden with verbs attached locally to an object. Property definitions: As with objects, classes can defined properties. So a weighted object might have a numeric property, "weight", and a weighted container might be a subclass, with the additional numeric property "capacity". Matcher ------- In order to make things easier to manipulate and program, MOO has a limited parser (which I call the "matcher", to distinguish it from the program parser) which tries to match the user's input to something meaningful for the environment. The basic format for a command typed by the user is: verb [direct object] [preposition [indirect object]] eg., north eat food put rubber ducky in brown bag hit ball with bat The first word is always the verb. The rest of the input is parsed, looking for a preposition or a prepositional phrase (these are pre-defined). The preposition is used to separate the direct object from the indirect object. If no preposition is found, the words after the verb are taken to be the direct object in it's entirety. The direct object (and indirect object) are then compared with the handles on _every object in the player's environment_. This includes objects contained in other objects (one of those ungraceful hacks I was talking about). This is done because otherwise, there would be no way to remove objects from containers. If the words specified for the direct and indirect object match more than one object in the area, the direct object is set to "ambiguous". The matcher itself does _not_ give an error at this point (see "Verbs"). If the dobj or iobj cannot be matched to any objects, it is set to "fail" (a failed match). If no dobj or iobj is specified, it is set to "none". Once the matcher has finished matching the dobj, iobj and prep, it "sends" the verb as a "message" to the room (to use o-o talk). What this really means is that the matcher searches the room for a verb which matches the given verb. (Either directly attached or inherited verbs will work; see "Verbs"). If this fails, the verb is sent to the dobj, if any, then the iobj, if any. This means that, to manipulate an object, you will almost always have to specify the object on the command line (another ungraceful hack). Verbs ----- Each verb has a number of attributes associated with it: The dobj (direct object) and iobj (indirect object), which specifies what direct object and indirect objects are allowed for the verb. There is also a preposition which indicates which preposition (or prepositional phrase) must be used when using this verb. These attributes can be specifed when using the .verb command, or can be changed (before programming the verb) using the .dobj, iobj, and .prep commands. In order for a verb to match, the dobj, iobj and prep specified on the command line must match those for the verb. If they do not match, another attempt is made to match the verb. The valid options for dobj and iobj are: "none" - the verb cannot have a dobj or iobj (an exit is usually like this). "any" - this will allow any class of object to match the verb (this actually sets the field to "0", since every object has the root class.) class - If a class is specified (using the class name or & notation), only objects with that class will work in that position. "fail" - this matches if the dobj specified could not be matched to any objects. "ambiguous" - this matches if the dobj or iobj specified could be matched to more than one object. These last two options give you the ability to give customized messages for a paticular verb (for example, "put blah in box" where box exists but blah doesn't might give the message: "I don't see what you want me to put in the box." if a dummy "put" verb is attached with "fail" as the dobj. This isn't as tricky as I've made it sound--it's easier to explain by doing it.) The ability to specify a class of objects is required for programming verbs which are dependent upon a particular property of a particular class (see "Programming"). Flags ----- All flags are set or unset by using the .set command. .set is also how you set the value of a propertyA The format is: ".set thing.flag" to set, and ".set thing.flag 0" to unset. The flags are defined as follows: player - This must be set for the program to recognize an object as a player builder - allows a player to use builder commands wizard - allows a player to use wizard commands, and gives exceptions to just about every law. dark - This has similar meaning to TinyMUD (ie., dark objects are invisble, dark rooms or players give no arrival or leaving messages). public - This is like the LINK_OK flag in TinyMUD. Objects which are LINK_OK can be move()'ed to by any player (see "Programming"). matchable accessible - these flags currently do nothing, but will eventually be implemented to restrict matching of objects. My idea was that only objects set "matchable" would have their contents matchable by the matcher. Also, only containers which were set "accessible" would allow you to access verbs on the contained objects. This would prevent someone from "get"ting an object from a transparent container, for instance. The ideas was, if a verb was sent to an object which was contained in a "matchable" but not "accessible" container (most players would probably be set this way), the verb would be sent to the container instead. Then the container could choose to send the verb to the contained object, or not. These flags won't be particularly useful without the ability for verbs to call each other, which has not been implemented yet. forward - this is probably the neatest addition. This flag allows messages such as "say", poses (":"), and other messages (see "otell", under "Programming") to be sent to a containing room (or "meta-room".) The "meta-room" would then send the messages back down to the contained rooms. This allows you to have different areas of a room which are "common", in a sense. You could also have a quiet corner of a room which did _not_ have the forward flag set, but was still contained in the meta-room. That way, you could hear what was going on in the room, and carry on a private conversation with 3 or more people at the same time. There are a number of different applications of the forward flag. For instance, in the test dbase I've been fiddling with, I created a window which was openable and closable, and a room outside the window. If the window is open, you can hear people talking outside. If it's closed, you can't. This is done by actually moving the outside room in and out of the meta-room. Programming ----------- To make a verb actually do something, you have to "program" it. This is done with the .program command. This is somewhat analogous to @link'ing an exit on TinyMUD. Once you are programming, the server starts recording each of the lines you input. There are exceptions: ".end" stops programming, and "say", ":" and "page" still work (ie., they don't get remembered). Once you are finished (by typing ".end"), the server then sends your program off to be compiled by the program parser. Note that _the source code is scrapped after parsing_. If you want to modify your verbs later, it's a good idea to hang on to your source code. There are also no editing facilities, so it's a good idea to edit locally. I have a modified version of tinytalk with the /upload command, which is quite useful. As far as the programming language, it is vaguely c-like, vaguely c-shell-like, and very primitive. The only two types currently supported are numbers and strings (the property types). Pseudo-objects -------------- There are a number of pseudo-objects which you can refer to in programs: $player - the player who initiated the action. $this - the object to which the verb is attached $dobj - the direct object specified by the player $iobj - the indirect object specified by the player $item - the current item in a for() loop; see "Statements", below Pseudo-properties ----------------- There are a number of pseudo-properties which you can refer to in your programs (and with .set): .location (numeric) .class (numeric) .owner (numeric) .name (string) .handles (string) .contents (numeric) - this is boolean .lookverb (string) Plus all of the flags (player, builder, etc.) These psuedo-properties are read-only, within programs. Eventually, some of them should be read/write. Currently, .contents merely tells you if an object has any non-dark contents. I'm going to modify this to give you the number of non- dark objects, eventually. Perhaps if I get list or array types implemented, this would return a list. Statements ---------- These are the available statements: move(thing, dest) - moves an object around. To make an exit which leads to room #32, for example, you would: move ($player, #32) set(object.property, value) - sets the value of a property You must give a numeric expression (expr) for a numeric property, or a string expression (strexpr) for a string property. tell(strexpr) - tells the $player a message. otell(strexpr) - tells everyone else a message dtell(strexpr) - tells the direct object a message itell(strexpr) - tells the indirect object a message For otell, dtell and itell, the message is automatically prefixed with $player.name, to avoid confusion and spoofing. if (expr) then statements endif if (expr) then statements else statements endif Yer basic conditional, c-like. for (thing) statements endfor This is the only looping construct. It loops for the non-dark contents of thing, setting $this to each one. $this always refers to the inner-most for(), so you can do nested for()'s. Expressions ----------- Most of the operators are c-like: ! - not, negate || boolean or && boolean and == != equality, inequality < <= > >= the usual + - ditto * / % mult, div, modulus You can also refer to objects (by name, if they are nearby, or by # notation otherwise.) and their numeric properties (and pseudo-properties). Any digits are interpreted as numeric constants. String expressions ------------------ Any string quoted in double-quotes (") is a string constant. Any string property is interpreted as a string expression. Numeric properties (and object numbers) can be converted to strings by use of the str() function. String expressions can be concatenated simply by placing them one after another. For example, if object #23 had the string property "foo" and the numeric property "bar", you could do: tell("Foo is " #23.foo " and bar is " str(#23.bar) ".") to display their values. Permissions ----------- You can always move $player and $this around, and always move to $player, $this, $player.location and $this.location. You can only move $dobj and $iobj around if they are owned by $player or $this.owner. (Eventually I might implement something like the TinyMUD drop-to, if I can figure a not-too-inelegant way to do it). Wrap-up ------- Well, this file is getting pretty big. I'm sure I've missed lots of stuff, and I don't have time to do the "What's next" section yet, but I'd like to get back to bug-fixing, so if you'll excuse me, I'm outta here. I hope to get a permanent beta-test site by the end of this week, if possible. Please feel free to give me feedback via email on these doc's or the ideas behind the program. -- Stephen White ("Ghondahrl") sfwhite@watcgl.waterloo.edu