Ubermud - Introduction Ubermud is a multi-user-dungeon server, with a program- ming language to allow users and implementors as much lati- tude as possible in creating objects in a virtual universe to interact with. Currently Ubermud is implemented as a sin- gle program, incorporating a small compiler, a machine emu- lator, a network interface, and a simple user interface. The software is implemented in C, designed to run under Berkeley UNIX, or versions of System V that support Berkeley networking protocols. Ubermud's design goals are threefold: 1) To provide an environment in which complex and fun-to-play-with objects can be reasonably easily created and modified. 2) To provide an environment that can be tailored at a variety of levels - allowing modification of the underlying rules of a universe or simply modification of individual objects and how they behave in their universe. 3) To provide a decent path for future evolution - to expand well as hardware gets faster, and databases get large, and to provide a framework to build upon. To meet these goals, Ubermud is a based on a low-level programming language that allows basic object manipulation, creation, simple math, random number generation, basic I/O, and list manipulation. Objects are stored in a reasonably efficient database format, intended to strike a balance between performance in the short term, and scalability in the long term. It is expected that the performance of a Ubermud universe will stay roughly the same, no matter how large it grows. A Ubermud universe will consist of nothing but a quan- tity of objects in limbo. The relationships, types, and interactions between the objects is determined by the system functions, or rules of the universe. These system functions are written in the internal programming language of Ubermud, 'U', which allows a great deal of leeway as far as modifica- tion of the rules of the universe. A permissions system is built into the basic object manipulation functions of the U programming language, and still more complex types of per- missions interactions can be synthesized from these building blocks. There are special optimizations in the code to ensure that the system functions of a universe are as fast as possible, to allow universes to be complicated without becoming slow. A universe's Wizard will have a great deal of leeway in breaking the rules of the universe (hereafter breaking the rules of a universe will be referred to as 'magic') or creating objects that allow others to selec- tively break the rules. In this manner, it is hoped that universes may wind up behaving quite differently, depending on the goals of the Wizard and players, yet still be based on the same software system, allowing at least a degree of portability and flexibility as new things are added to Uber- mud. Ideally, Ubermud will allow Wizards the joys and June 22, 1990 Ubermud Introduction sorrows of customizing the rules of their universes, without having to modify and recompile tons of C code. From the player's standpoint, a Ubermud universe could look very much like a tinyMUD universe, with some varia- tions. For trusted users (or, at the Wizard's discretion, any users) access to the U programming language will be per- mitted, allowing users to create complex objects that may interact with eachother, other players, and so on. The per- missions system is designed to make it somewhat difficult to destroy another player's work, though the potential for creating complicated and puzzling objects exists. Users with programming access would write the source code for their objects (source code written in the U programming language will be referred to as Ucode, or just U.) and submit it to the server for automatic compilation and execution. This design is intended to permit users to have access to their favorite text editors, to offload processing from the server, and, possibly most importantly, to encourage users to maintain their own libraries of Ucode for complicated objects. Databases may come and go, and it is vital to encourage users to take responsibility for archiving their own valuable material, so that in the event of server crashes, or database rollbacks, work is not lost. Overview of the Implementation Ubermud is a single program, incorporating a small com- piler, a machine emulator, a network interface, and a user interface. The compiler reads Ucode through the network interface, and compiles it into operations for a virtual machine. The compiled Ucode is either executed immediately, if it is a simple command, or is stored in the database, if it represents the data of an object, or a function. When a function or command is executed, it is run by the machine emulator, and interpreted. Errors are handled to as great a degree possible, but certain types of errors will cause the interpreter to cease running the offending command. The interpreted commands are the part of Ubermud that does all the work, manipulating data elements, echoing text to the users, etc. A user typically will not interact directly with the compiler, since it is unreasonable to force players to 'talk' in Ucode. The user interface (hereafter sometimes referred to as the 'monitor') is responsible for reading plain-text input, and trying to match words in the input with functions in the database and universe's rules. Thus, when a player types: 'take cat' the monitor is responsible for analyzing the input and generating a call to the take() function, with appropriate parameters ('cat' in this case). The monitor's function is to do as little work as possible, before invoking a compiled Ucode function. The monitor is, unfortunately, static, and is one of the only parts of the June 22, 1990 Copyright, Marcus J. Ranum, 1990 universe that cannot be re-programmed without a source code change. The design of Ubermud is such that multiple moni- tors is not out of the question, or even hard to implement, should there be divergence in the evolution of the monitor's parser. The network interface acts at the glue holding the mon- itor, compiler, and interpreter together. Its sole responsi- bility is moving bits between these functional members in a reasonably efficient manner. The network interface is as separate from the functional elements as possible, to avoid making Ubermud totally UNIX-specific. Data-Base Methods The database of Ubermud is stored in several files, which are keyed to unique object numbers. These object numbers are logical only, and are mapped to real storage locations through a b+tree index. The details of the disk storage layout are not germane here, but permit multiple references to the same basic object, with reference count- ing. This permits a fair amount of data compaction to be used, and the U syntax takes advantage of this in several ways, allowing 'pointers' to be set to basic objects as well as permitting direct changes to the contents of a multiply referenced basic object. A large cache is maintained, at both the level of the basic objects and the b+tree index. It is unavoidable that there will be disk I/O aplenty, but from the author's observations and testing of tinyMUD, it is expected that cacheing will greatly reduce the amount of I/O. To prevent databases from becoming inconsistent, the disk is kept up to date with the cache at all times, a major performance hit, but worthwhile in view of the alternative. The U Programming Language The U language resembles C, in its syntax, but takes advantage of its interpreted nature to save the user from having to do type-checking and declaration. Additional differences between U and C are an absence of arrays, pointers, and all flow control constructs that can be used to implement an unbreakable loop. U has a list iterator for-loop, which can be used to emulate limited loops, or traverse elements in a list, but it is intended to be impos- sible to write Ucode that would loop endlessly. Ucode has two types of variables: object elements, and local vari- ables. An object element is a value that is stored in the permanent database, whereas a local variable is fast, but does not get preserved. Local variables are useful as list iterators, temporaries, and values that are deliberately intended to vanish at the end of a function call. Object elements are the basic building blocks of a universe, and are frozen and thawed to and from disk as needed. A special class of object element is the system object element, which June 22, 1990 Ubermud Introduction is stored in main memory all the time for performance rea- sons, as well as to ease modification of a universe's rules. When a Ubermud server is booted, it is typically bootstrapped with a file of Ucode that will set such tem- porary system object elements as the 'who is on' list and make copies of very frequently used functions to speed their use. U has several data types: integers, strings, functions, object numbers, errors, and NULL. Variables are typeless, to the extent that they 'adapt' to whatever they are assigned to. In some cases, variables may be operated on in an ille- gal manner (such as dividing two strings) which results in a conversion of the value to an error. Error values in U are primary data types, and can be manipulated by a user (though there is little reason to). Typically, error values are used to test conditions: func #33.foo { if(badcall() == error) { echo("I'm afraid I can't do that\n"); return(error); } ... } The special value NULL is used similarly to compare against nonexisting things. Additionally, NULL is used in assign- ments to cause a thing to cease to exist. Certain types of errors can return NULL, and users can define functions returning NULL. Thus: /* correctly handle a nonexisting value */ if(#33.desc == NULL) echo("You see nothing special.\n"); else echo(#33.desc); /* destroy a value */ #33.name = NULL; Integer data permits the usual types of simple integer math, and string data can be manipulated with BASIC and C- like string builtin functions. Unlike some languages (such as awk) strings and numbers cannot be coerced transparently into eachother. Type conversion builtin functions are pro- vided, as well as type checking functions such as isstr and isnum. Functions are a primary data type, but are unique in that there are no operations that can be performed on them, other than calling them. The syntax of U permits a program- mer to attempt to call any data as if it were a function. This is obviously an error, and returns7 Ierror. June 22, 1990 Copyright, Marcus J. Ranum, 1990 Another data type that is crucial to Ubermud is the list. A list can contain a set of object numbers (making lists capable of containing any data type may be a future enhancement). A list iterator syntax is built into the language, which permits all the elements of a list to be accessed in turn: /* a sample WHO function */ func WHO { $users = 0; foreach $tmp in (system.WHOlist) { echo($tmp.name,"\n"); $users = $users + 1; } echo("total users: ",$users,"\n"); } Lists will play a large role in the creation of universe rules. Typically, something like a 'room' will be modeled as an object with a string description, a list of contents, a list of players, and a list of exits. There are major advantages to this approach, since adding new elements to a database is easy (though you have to program the logic of what to do with them) and, more importantly, it allows a universe builder to optimize the rules to support frequently used operations. To make conversation more efficient, for example, all the players in a room can be maintained on a separate list from the 'dead' objects. In this manner, the logic of the 'say' function is simplified, since it can ignore the 'dead' objects and only look at what matters. This will tend to improve performance, since things that are not used often will seldom be thawed from disk, but rooms where active conversations are being held will typically have their player-lists in active cache. The design of Ubermud's stack machine and interpreter is such that adding a new data type is not very difficult. Clearly, introducing new data types or syntax is not to be untaken lightly, but to allow the software to survive over time, every effort has been made to make it easy to add new functionality. For a more detailed description of the U Programming Language, there is documentation describing the syntax and built-in functions, which contains more examples and the reasoning behind some aspects of the language's design. June 22, 1990