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