/
teeny/db/
teeny/dbm/
teeny/doc/
teeny/includes/
	My intention in this document is to roughly sketch the internals of
TeenyMUD, particularly the database management code. If you don't plan to hack
at the server at all, or if you don't know C, don't bother reading this.

Database Implementation
-----------------------

	The database is implemented as an index, a cache and a chunks file
providing backing store for the cache. The index is implemented as a large
array (main_index) of pointers to  'descriptor' structs (things of type struct
dsc). An object number is used as an index into this array to locate the unique
descriptor associated with the object. A NULL pointer in the array indicates a
non-existant object (typically one that has been recycled). The descriptor
contains the flags, a pointer to the name, and the NEXT field (used in
contents/exits lists) of the object it describes, as well as a pointer to where
the remainder of the object data can be found. If the object is in cache, this
is a memory pointer to an 'obj_data' struct, otherwise this is an offset into
the chunks file. The flags field contains a flag (the IN_MEMORY flag) which
allows the system to differentiate between these cases. Quite incidentally, the
same descriptor structs are used to describe free chunks in the chunkfile, and
a list of these is used to maintain data about the free chunks.

	Descriptors are permanently resident in memory, thus the name, flags
and NEXT field of any object are rapidly accessible. This may or may not have
been a good design decision, but is the way things are. All the rest of the
data associated with an object is subject to being cached and paged in and out.
When an object is read in from disk, the information about where it is on disk
(the disk chunk it lives in) is copied into the obj_data struct, and the
descriptor no longer contains this data. When the object is flushed from cache,
if it is unchanged nothing happens, otherwise this disk chunk is freed, and
then a new chunk is requested (since the object may have changed size while in
memory) into which to write the updated object data. The descriptor is then
filled back in with data about where in the chunks file the object data can be
found. At any given moment, the descriptor conatins the actual size the object
takes (or will take when written back) on disk. It is this number that is used
for cache usage estimates (thus, cache usage is really only approximate, since
in memory size is somewhat different). This size data is also used by
disk_freeze() when requesting a disk chunk.

	 The code responsible for reading and writing objects to the chunks file
is in db/disk.c, and consists primarily of two routines, disk_thaw() and
disk_freeze(), each of which accepts a pointer to a descriptor. The former
reads the object data off disk into an obj_data struct, and returns a pointer
to this, the latter writes an obj_data struct to disk, and frees all the
associated memory.

	The code responsible for managing the cache -- inserting things into
it, deleteing things from it, trimming it back to the current cache size, and
purging it for dumps, lives in (surprise!) db/cache.c. Since it seemed like a
good idea at the time, the code which keeps track of where free chunks
('holes') in the chunks file also lives in here. Routines for making a disk
chunk free, and for acquiring a disk chunk to write data in to exist, and are
used by disk.c. These routines implement a worst fit algorithm, and do
amalgamate adjacent free chunks, so fragmentation should remain fairly well
controlled. See my remarks in doc/teeny.doc for how to de-frag a database.

	For performance reasons, and since I didn't want to constantly maintain
the index on disk, the cache is NOT write-through. Thus, in the event of a
server crash, the chunks file is out of date, and the information about where
things are in it is completely lost. The option is to write objects back to
disk immediately when they are changed, and to update the disk copy of the
index at the same time. I deemed this an unacceptable performance hit.

	Finally, the code for actually implementing the TeenyMUD database lives
in db/db.c. It includes a suite of routines for accessing elements of an object
completely transparently: set_str_elt(), set_int_elt(), set_lock_elt(),
get_str_elt(), get_int_elt(), get_lock_elt(). These accept an element code, and
return or set the appropriate element. They take care of allocating and freeing
memory for everything, with the exception of set_lock_elt(), which expects to
be supplied with a lock written into memory it can use (this is an anachronism,
for which I apologise). Note that get_str_elt() and get_lock_elt() return
pointers to the literal in-memory data. If subsequent database accesses cause
this data to be purged and written back to disk, these pointers will be
garbage. For this reason, the command handler calls cache_lock() to prevent
*any* data from being purged from the cache at the beginning of each command,
and calls cache_unlock() at the end. (and calls cache_trim() at that time to
trim the cache back if required). Several utility routines are also provided,
create_obj(), destroy_obj(), exists_object() and so on.

	The main index is read in at server startup time by read_descriptors(),
and is written back during dumps by write_descriptors(). These functions read
and write all the descriptors and descriptor data in to the index file,
followed by a list of data about the free chunks in the chunk file. To bring
the db into synch, one needs merely to flush the cache, and then call
write_descriptors(). Note that doing this in the wrong order would be
disasterous, since many of the descriptors written will contain data on where
*in memory* the object data can be found, with no mention of the location in
the chunk file.

Game Database Layout
--------------------

	Each thing in the database has a number of elements associated with it.
The usual strings (name/suc/osuc/fail/ofail/desc) and the usual integers
(pennies/owner/location/home/flags) are present, and do the usual things. Here,
usual means 'usual in the sense of TinyMUD.'  Each object also has a contents
element, and an exits element. These both point to the first thing in the exits
or contents list of the thing, lists are constructed by chaining items together
with the 'next' element. This closely resembles the TinyMUD database, with the
difference that everything has an exits list. In particular, when a player
picks up an exit, it goes into the player's *exits* list, not into their
contents list. It is fundamentally this that makes the TinyMUD dump format
incompatible with the TeenyMUD text dump.

	Besides this, there is nothing terribly exciting about the database
layout. The astute reader will notice, however, the distinct lack of a password
field on things. This is because most things don't have passwords, so I chose
to code the passwords as the second word of the player name, and then be
careful about things. The is responsible for much ookey code in the server, and
I sometimes regret this descision. Such is life.

	TeenyMUD 1.1 also adds (optionally -- define TIMESTAMPS in
includes/teeny.h) a timestamps field to each object. This is an integer,
intended to be interpreted as the time, in *minutes* after 00:00 GMT Jan. 1,
1970, when the object was last 'used'. The database manager (described in
doc/dbm.doc) can use these fields. Version 1.1 does not, however, fully
implement timestamping. I added this stuff here to avoid obseleting databases
in future, if I ever get around to properly doing timestamping. Actually, it's
pretty easy to implement. I did write a stamp(<object number>) function, which
lives in mud/utils.c, to write a timestamp (of the current time) on an object.
Placing suitable calls to this function willy-nilly in the code will implement
timestamping. I didn't do this because it's a) tedious, and b) not so easy to
figure out where the 'right' places to call stamp() are.


Network interface 
-----------------

	The network interface is implemented in mud/tcpio.c and mud/misc.c (the
latter is so named because I couldn't think of anything better). These
implement a fairly simple server interface with minimal protection for the
server. tcpio.c is a hacked version of the UberMud network interface, by the
way. Many thanks are due Marcus Ranum for writing this beautiful code, which is
much less beautiful since I've gotten ahold of it. The code in mud/misc.c
implements command quotas on a per time slice basis, and is responsible for
slicing input from the network up into commands and dispatching them
appropriately. It is in mud/misc.c that WHO and QUIT are handled, as well as
connect and create (create player). The function match_who(), for matching
against the WHO list also lives in mud/misc.c, since it needs to fiddle around
with data structures private to this module.


Other stuff
-----------

	mud/match.c contains the bulk of the routines for resolving strings into
database references. The code herein is somewhat ookey. Feel free to re-write
it. See the comments for how to use it, it's fairly straighforward with a few
icky bits that may or may not be well described in the code.

	The TeenyMUD command set is implemented in mud/cmds.c, mud/buildcmds.c,
mud/speech.c and mud/wiz.c. Utilities used by these commands for doing all
sorts of things are contained in mud/cmdutils.c. The command parser (such as it
is) is in mud/command.c, as well as the code implementing the 'gripe' command.
Look at this last, you'll like it.

	mud/boolexp.c contains, basically, three routines, one for parsing
Boolean expresions into my nifty internal format, one for writing an infix
(text) version of an internal-format Boolean expression into a buffer, and one
for evaluating a internal-format Boolean expression. The internal format
consists of an array of integers, the first of which is how many integers
follow. The remaining integers constitute a simple RPN 'program' for the
evaluator. Positive numbers are interpreted as db references, and evaluate to
TRUE if the player is, or is carrying the numbered object. Negative numbers are
operations, AND, OR, NOT and STOP. A small stack machine in islocked() executes
the program, and returns the negation of the result. An artifact of this design
(and of my laziness) is that when an object is recycled, any lock it appears in
retains the reference to that object number. If and when the object number is
re-used, the lock will now reference that new object. The alternative is to
scan the entire db (sucking most of it off disk) and fiddle wildly with locks
on everything. Ick.

	mud/teeny.c is the mainline, and also contains a few other high-level
take-this-goo-and-make-it-a-MUD type routines, like dump_db(), the console
handler and so on.

	mud/notify.c contains some routines for telling players and groups of
players things.


Hacking the server
------------------

	Feel free to do so. You can do anything the copyright says you can, and
that's most everything. To do some of the basic things people are *bound* to
want, follow the instrctions below.

+ pronoun substitution

	All suc/osucc/fail/ofail strings are handled, in the bitter end, by
do_strings() in cmdutils.c. Add a couple bits to the player flags in teeny.h
(there's lots of room for expansion), add a couple more lines to do_set() in
mud/cmds.c to allow the player to mess with these bits, and add a few more
lines to do_strings() to substitute pronouns. Piece of cake, take you maybe an
hour or two.

+ new strings (drop/odrop-like)

	This is trickier. You will need to extend the obj_data struct in
includes/db.h, and modify disk_freeze() and disk_thaw() to cope with these
extra elements. Then you need to define a couple more element codes in
includes/teeny.h, and teach get_str_elt() and set_str_elt() what they mean.
Finally you have to modify the appropriate player commands (do_drop(),
do_kill() etc.. ) in the appropriate modules to echo these strings suitably
whacked up at the appropriate times.

+ new data elements of other types

	Integers are easy. Just like strings, really, but teach get_int_elt()
and set_int_elt() instead. Other data types, like p-lists, are probably a very
bad idea. Don't even think about it.

+ modifying the command set

	Muck 1.0-like extensions would be cute, and not very hard to do, I
think. Peer at the code in mud/cmds.c _et_al_ until you understand it, and then
just do it. It is an unfortunate feature of my database interface that
programming player commands is even more grotesquely tedious than you can
imagine.

	Extensions like @find are not easy to do, since they require,
basically, thrashing the entire database, most of which will typically be on
disk. If you think a wiz-@find is bad on a TinyMUD, just have a shot at
implementing it in Teeny. This is one reason that NO contents are displayed in
a DARK room -- if objects controlled by the player were shown, then every time
any player entered Limbo, every single object there would have to be unfrozen,
to see if the player controlled it. Not good. Therefore not implemented.
This sort of functionality is, however, provided in the standalone db manager.
See doc/dbm.doc.

	Extending @rec to rooms is similarly sticky. The code for doing so
exists almost entirely, except for the bit about unlinking all the database
elements referencing the room (exits leading in, objects homed to the room).
This, again, requires thrashing the entire DB checking dests and home. In this
case, a check of flags will elminate the need to unfreeze the rooms, at least.
Again, this is handled by the database manager.

	Have fun with it, folks...

		Andrew/bob