/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/

			     TeenyMUD II - Database


	DESCRIPTION

	    TeenyMUD version 2.0 features a cached, disk oriented, 
	database scheme.  The database system currently allows four
	specific (although internally alike) object types: the room,
	the generic thing, the player, and the exit.  In addition to
	any data stored within the database itself, each object may
	also have files associated with it.  Each object may contain
	any number of attributes, both those used internally by the
	server, and those used by users.

	    Each attribute generally consists of two strings, the
	attribute name, the attribute data, and an integer - the
	attribute flags.  Name and data should be obvious; the flags
	integer is used for per-attribute flags governing whether or
	not a particular attribute may be inherited normally, is
	visible to everyone, etc.  This is described in more depth
	below.

	    Within the database itself, each object may have a single
	parent, which, when set, will cause the object to inherit the
	attributes of it's parent.  Parents may be nested; if an
	attribute is not found on the source object, the parent will
	be searched, then the parent of that, and so on, until a
	matching attribute is found, or the database runs out of
	parents to search.  The routines used to lookup attribute
	data are described in further depth below.

	    The TeenyMUD cache has a fixed size, and is locked
	(meaning modified data will not be removed from the cache)
	during many operations of the server, including database file
	backup.  When an object is referenced, the cache is first
	searched for the appropriate data, failing which the object
	is loaded off of disk via whichever database library the
	server is linked with.  The cache is internally implemented
	as a hash table, producing exceptional lookup speed.

	    In addition to the cached attributes, there are also
	several integers associated with each object, reflecting such
	things as the the objects location, contents, exits, quota,
	flags, owner, home, rooms, and the next object within a
	linked list (the linked lists are contents, exits, and rooms).
	Most of these integers are subject to being paged in out of
	the cache along with the object's attributes, but some special
	ones are kept in memory at all times: flags, owner,
	home/destination, next, and parent.  This allows for quick
	referencing of those elements.

	    There are also integer arrays stored within the database,
	which usually contain exit destinations.  The first element of
	this array is the total number of elements that follow, or
	typically -1 if the array is empty.

	    The final element of each object are the non-attribute
	strings. There is currently only one of these, the object name.


	ATTRIBUTES

	    Attributes are managed within the database as a hashed list.
	When the server requests an attribute, the object (if necessary)
	is loaded off of disk, and placed in the cache.  Then, the
	hashed list is traversed, searching for the proper attribute.
	This scheme may be slower than some others, but uses very little
	memory, since there is no record of what attributes are
	associated with each object, except for the list itself.  All
	internal attribute name matching is case-insensitive.

	    There are at least two distinct types of attributes:
	regular strings, and locks (b-trees).  Each is managed
	differently within the attribute layer, though they are kept in
	the same list.

	    The server restricts the length of attribute names to
	around 60 characters, although the database itself can handle
	any length.  The size of the data associated with each (string)
	attribute is restricted to 4k bytes, for the sake of sanity.
	Attribute flags are handled by the server, and thus not
	discussed here.

	The following routines are defined for attribute manipulation:

	#include "teeny.h"
	#include "externs.h"

	int attr_set(int obj, char *name, int flags)
		Resets the flags element of the attribute to the value
		specified.  Checking of the value for sanity must be 
		performed by the caller.  Returns -1 upon database
		error (no such object, or disk error), -2 if no
		matching attribute could be found, or 0 on success.

	int attr_addlock(int obj, char *name, struct boolexp *data,
		         int flags)
		Adds the specified lock attribute to the object,
		replacing any matching attribute that may already
		exist.  Sanity checking of the data and flags must be
		performed by the caller.  Returns -1 upon database
		error, or 0 upon success.

	int attr_add(int obj, char *name, char *data, int flags)
		Adds the specified attribute to the object, replacing
		any matching attribute that may already exist.  Data
		must be a string, and the sanity of the flags element
		must be checked by the caller.  Returns -1 upon
		database error, or 0 upon success.

	int attr_delete(int obj, char *name)
		Deletes the specified attribute from the object.
		Returns -1 upon database error, or 0 upon success
		(if the attribute didn't exist, 0 is returned).

	int attr_source(int obj, char *name)
		Returns the object number of the object on which the
		named attribute can actually be found, using
		inheritance.  Returns -1 upon database error or if no
		matching attribute could be found.

	int attr_getlock_parent(int obj, char *name,
			        struct boolexp **ret, int *flags,
				int *source)
		Retrieves the specified lock attribute, using
		inheritance.  ret and flags will be filled in with
		the data, and source will contain the object number
		of the object actually containing the attribute.
		Returns -1 upon database error, or 0 upon success.
		If no matching attribute was found, 0 is returned, ret
		will be set to NULL, and the flags and source
		variables will both be set to -1.

	int attr_get_parent(int obj, char *name, char **ret, int *flags,
			    int *source)
		Retrieves the specified attribute, using inheritance.
		ret and flags will be filled in with the data, and
		source will contain the object number of the object
		actually containing the attribute.  Returns -1 upon
		database error, or 0 upon success.  If no matching
		attribute was found, 0 is returned, ret will be set
		to NULL, and the flags and source variables will both
		be set to -1.

	int attr_getlock(int obj, char *name, struct boolexp **ret,
			 int *flags)
		Retrieves the specified lock attribute from the
		specified object.  ret and flags will be filled in with
		the data.  Returns -1 upon database error, or 0 upon
		success.  If no matching attribute was found, 0 is
		returned, ret will be set to NULL, and the flags
		variable will contain -1.

	int attr_get(int obj, char *name, char **ret, int *flags)
		Retrieves the specified attribute from the specified
		object.  ret and flags will be filled in with the data.
		Returns -1 upon database error, or 0 upon success.
		If no matching attribute was found, 0 is returned, ret
		will be set to NULL, and the flags variable will be set
		to -1.

	    The following routines implement attribute iteration.  Note
	that the order of attributes has no particular meaning, it is
	simply based upon the hash value of their names.  The following
	routines will visit each attribute exactly once, though if
	attributes are added or deleted during iteration the unexpected
	will result.

	#include "attrs.h"

	struct attr *attr_first(int obj, struct asearch *search)
		Returns the ``first'' attribute on the specified object,
		or NULL if there are none or in the case of database
		error.  search will be filled in with internal data used
		to implement attribute iteration.

	struct attr *attr_next(int obj, struct asearch *search)
		Returns the ``next'' attribute on the specified object,
		or NULL if there are none left.  search should be a
		pointer as previously setup by attr_first().

	    Attributes used internally by the server (such as Description,
	Success, Fail, etc.), have their names stored within the global
	Atable.  When referencing them with the above routines, the
	programmer should use the #defines in teeny.h, such as DESC,
	SUC, FAIL, etc.  If more of this type of attribute must be added,
	be sure that the #defines in teeny.h properly reflect the
	position within the Atable.  Note that new attributes should be
	added to the end of this table.


	OBJECTS

	    Each object has certain elements by default.  These range
	from integer, through integer array, and regular strings.  Use
	only the #defines in teeny.h with the following routines.

	    Data returned by these routines should not be directly
	modified, unless the programmer really wants to destroy his
	database, since the cache is not write-through.

	#include "teeny.h"
	#include "externs.h"

	int get_str_elt(int obj, int elt, char **ret)
		Sets ret to the value of the specified string element.
		NAME is the only legal string element.  Returns -1 upon
		database error, or 0 upon success.

	int get_int_elt(int obj, int elt, int *ret)
		Sets ret to the value of the specified integer element.
		Returns -1 upon database error, or 0 upon success.

	int get_array_elt(int obj, int elt, int **ret)
		Sets ret to the value of the specified array pointer.
		DESTS is the only legal array element.  Returns -1 upon
		database error, or 0 upon success.

	int set_str_elt(int obj, int elt, char *value)
		Sets the value of the specified string element.
		Allocation of storage for the new value is handled
		internally.  NAME is the only legal string element.
		Returns -1 upon database error, or 0 upon success.

	int set_lock_elt(int obj, int elt, int *value)
		Sets the value of the array pointer.  Allocation of
		storage for the new value is handled internally.  DESTS
		is the only legal array element.  Returns -1 upon
		database error, or 0 upon success.

	void destroy_obj(int obj)
		Destroys the specified object, freeing it's number for
		later use.  This frees all resources associated with
		the object.  There are higher level routines which are
		usually better to use than this one!

	int create_obj(int type)
		Creates a new object, of the specified type.
		Returns the object number.

	int exists_object(int num)
		Returns true if the specified object exits, false
		otherwise.  Garbage objects do not exist.

	int Typeof(int obj)
		Returns the type of the object.
	
	    There are also isFLAG() macros defined in teeny.h, where
	FLAG is the name of an object flag (in all caps).

	    If the programmer wishes to add more standard elements, they
	must add them to either the dsc (descriptor) struct in teenydb.h,
	or the obj (object) struct.  dsc (descriptor) data is kept in
	memory, obj (object) data is paged in and out of the cache as
	needed.  Since the goal of TeenyMUD is to be disk based, data
	should not be added to the descriptor without careful
	consideration.  After adding the struct elements, #defines for
	the new elements must be added to teeny.h, and the proper
	get_*_elt() and set_*_elt() routines must be taught how to
	access them, and all other routines in db.c that reference object
	data must be modified.  Lastly, the database library interfaces,
	if appropriate, must be modified to save and load the element
	from disk.  These types of elements should only be added by
	experienced TeenyMUD coders; use generic attributes, that's what
	they're there for.


	DBM

	    TeenyMUD II requires either the GNU Database Manager library,
	or the Berkeley DB library in order to function.  GDBM is
	preferred.  The GDBM interface is in the file gdbm.c, and the
	Berkeley interface is in bsddbm.c; both interfaces are very
	similar.  GDBM is an advanced database manager, allowing flexible
	record management of unlimited size.  It can generally be thought
	of by the programmer as a black box: the suite of routines in the
	file gdbm.c do nothing except reformat the internal structures of
	the database into the format GDBM uses - a simple, sized, series
	of bytes.  (The GDBM record may be thought of as a string, but
	it's not a C string.  It may freely contain zero terminations
	anywhere in the field.)  Please see the GDBM documentation for
	more information.


	CACHE

	    As stated above, the TeenyMUD cache has a fixed size, and is
	not write-through.  Internally, it is organized as a hash table
	of object structures, with the most recently used object at the
	head of it's list.  The size limitation of the cache is based
	upon the size of the object struct, plus the space used by the
	attributes.

	    When an object is loaded from disk, it is immediately placed
	at the head of the appropriate list, and the IN_MEMORY flag is
	set on it.  This allows the database to quickly check if an
	object needs to be loaded, or if it should traverse the cache
	list.

	    The other cache related descriptor flag, DIRTY, is set on an
	object when it's data has been modified.  When this flag is set,
	the cache will pass the object data on to the dbm layer whenever
	it comes time to purge the object from the cache.  Objects are
	purged from the cache at two times: the oldest objects are
	purged when the cache has reached it's size limit, and must load
	another object.  The only other time an object is saved is
	during a cache flush, when it has every DIRTY object in the cache
	written to disk, and frees everything else, resetting the cache.
	If a write fails for the object during the first of those two
	occurances, it will be kept in the cache, instead of purged.

	    Whenever the cache is reset, it checks to make sure that the
	hashing scheme is running at optimal performance, and will
	automatically grow in width if needed.


	NAMES

	    As mentioned above, object names are the only non-attribute
	strings.  In older versions, these were kept in memory at all
	times, but in this version they are paged in and out of the cache
	as needed.  This reduces the amount of memory used by the server,
	and is an all around good thing, with the only drawback being
	slowness of routines that search through the names of every
	object in the database.

	    One special case are the names of players.  At boot time,
	these are all loaded in from disk, and placed in a hash table
	within the database layer.  Invisibly to the rest of the server,
	these are kept in memory at all times, since they are referenced
	so much more often than any other object names.  Since players
	names are restricted in length to 16 characters or less, the
	overall memory hit is rather minimal when compared to the
	performance hit of not keeping them in memory.


	CREDITS

	    Much of the general layout and design of the database system
	is based upon (in an evolutionary way) that which was developed
	for TeenyMUD 1.x by Andrew Molitor.

	This document is Copyright(C) 1993, 1995 by Jason Downs.
	All rights reserved.  This material may be freely distributed in
	any means, either electronic or printed, so long as this statement
	of rights remains intact.  This material may be freely translated
	into other languages or otherwise modified and distributed, so
	long as no additional distribution limitations are imposed, and
	the translation of this statement of rights has been pre-approved
	by Jason Downs.