.\" macros
.\" example start
.de EX
.sp
.ft B
.fl
.nf
.na
..
.\" example end
.de XE
.sp
.ft R
.fi
.ad
..
.\" end macros
.EH 'Ubermud Introduction
.OH '\(C)Copyright, Marcus J. Ranum, 1990
.SH
Ubermud - Introduction
.PP
Ubermud is a multi-user-dungeon server, with a programming language to
allow users and implementors as much latitude as possible in creating
objects in a virtual universe to interact with. Currently Ubermud is
implemented as a single program, incorporating a small compiler, a
machine emulator, 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.
.PP
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.
.PP
A Ubermud universe will consist of nothing but a quantity 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 modification
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 permissions 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 selectively 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 Ubermud. Ideally, Ubermud
will allow Wizards the joys and sorrows of customizing the rules of
their universes, without having to modify and recompile tons of C
code.
.PP
From the player's standpoint, a Ubermud universe could look very
much like a tinyMUD universe, with some variations. For trusted
users (or, at the Wizard's discretion, any users) access to the
U programming language will be permitted, allowing users to create
complex objects that may interact with eachother, other players,
and so on. The permissions 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.
.SH
Overview of the Implementation
.PP
Ubermud is a single program, incorporating a small compiler, 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.
.PP
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
universe that cannot be re-programmed without a source code change.
The design of Ubermud is such that multiple monitors is not
out of the question, or even hard to implement, should there be
divergence in the evolution of the monitor's parser.
.PP
The network interface acts at the glue holding the monitor,
compiler, and interpreter together. Its sole responsibility
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.
.SH
Data-Base Methods
.PP
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 counting. 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.
.SH
The U Programming Language
.PP
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 impossible to write Ucode that would loop endlessly.
Ucode has two types of variables: object elements, and local
variables. 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 is stored in main
memory all the time for performance reasons, 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 temporary system object elements
as the 'who is on' list and make copies of very frequently
used functions to speed their use.
.PP
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 illegal
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:
.EX
func #33.foo {
        if(badcall() == error) {
                echo("I'm afraid I can't do that\\n");
                return(error);
        }
        ...
}
.XE
The special value NULL is used similarly to compare against
nonexisting things. Additionally, NULL is used in assignments
to cause a thing to cease to exist. Certain types of errors
can return NULL, and users can define functions returning
NULL. Thus:
.EX
/* 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;
.XE
.PP
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 provided, as well as type checking functions such as \fIisstr\fP
and \fIisnum\fR. 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 programmer to attempt to call
any data as if it were a function. This is obviously an error, and
returns \rIerror\fR.
.PP
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 \fIany\fR 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:
.EX
/* 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");
}
.XE
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 \fIdo\fR 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.
.PP
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.
.PP
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.