Builtin Databases

Rapture comes with many features tailored specifically to making a MUD easier to program and create.  A large part of this functionality it provided in the Builtin Databases, which are listed here.  They are database that come predefined (you do not need to declare them) and have implied meaning to their use.  Many internal functions and subroutines rely on these meaning to provide the functionality that they do.  In all cases, a builtin database works just like a normal user database, but possibly has extra data fields which may or may not have a specific meaning.  You can use dbaliases on them (and we highly recommend it) and record creation and deletion works exactly the same.

Summary
Rapture comes with many features tailored specifically to making a MUD easier to program and create.
Each database listed here has the same default structure as any user database.
Database bytes and variables can be clamped.
The node database is what represents connected users in Rapture.
Ditch a user connected to this node, and free the data associated with it.
This is an arbitrary persona record attached to this node.
Each node has a status associated with it.
This string describes where the location from which user is connecting.
Use this to obtain the first through fourth bytes representing the user’s connecting IP address.
Use this to obtain the first byte of the user’s connecting IP address.
Use this to obtain the second byte of the user’s connecting IP address.
Use this to obtain the third byte of the user’s connecting IP address.
Use this to obtain the fourth byte of the user’s connecting IP address.
This will get or set the telnet local echo option of the user.
Use this field to turn on or off the user’s receiving of ANSI color codes.
When ANSI color is enabled for this node, this represents the foreground color of text being sent.
When ANSI color is enabled for this node, this represents the background color of text being sent.
Width specifies how many characters to send on a line before performing automatic word wrapping of output.
Disabling a node will prevent Rapture from assigning a new user to it.
The <ATCP> TELNET option has been enabled by the player’s client.
The <MXP> TELNET option has been enabled on this node.
Stores the terminal types the client supports.
256-color support has been enabled for this node.
This database is used to hold information regarding a character.
This is the persona (or character’s) name.
There is an internal resourced called the player database, however it is not really an actual database.
This database is intended to hold information about ‘objects’ in a game.
Access an individual name of an object.
Set how many names this object has.
The ‘primary’ name for an object.
The replica database is used to represent instantiated objects in the game.
What type of object this replica represents.
Where the replica currently resides.
Obtain a unique name of the object.
The game database is a catch-all.
This returns a string describing the current date and time in the format: “YYYY/MM/DD HH:MM:SS”.
This returns the current year.
This returns the current month (1-12).
This returns the current day of the month (1-31).
This returns the current day of the week (0-6, 0=Sunday).
This returns the current hour (0-23).
This returns the current minute (0-59).
This returns the current second (0-59).
Returns the current microsecond (0-999999).
This returns the time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
This returns the current tick value (a tick occurs every 1/100th of a second) since Rapture was started.
When verbs are declared, they are entered into a table.
This returns a string formatted to describe the current call stack and parameter values.
This is the value (in seconds) that Rapture will allow an executable to run before deciding the code is in an infinite loop and generating an error.
This is set to the input received from a user on a node (after calling input()).
This is the same as rawinput$, but preceding whitespace has been stripped.
When a task is called, this is set to the value provided when the task was scheduled.
This provides an informational string describing the internal state and statistics of the Rapture engine at runtime.
This provides an informational string indicating the database structure currently in use by the Rapture engine.
This provides an up-to-date listing of all currently scheduled tasks and when they are due to fire.
This is the name of a function taking a single string parameter which is designated as the procedure to call when an IPC message is received.
When an ATCP message is received from a node, this will be nonempty and contain the full body of the message.
When an MXP message is received from a node, this will be nonempty and will contain the full body of the message, with the MXP control codes stripped.

Common Structure

Field Types

Each database listed here has the same default structure as any user database.  That is, each has five field types (and you specify the size of each of those field types).  The field types can hold different types of data and are summarized in the following table.

dataA 32 bit signed integer value
variableA 16 bit unsigned integer value (can be clamped)
byteAn 8 bit unsigned integer value (can be clamped)
flagA single bit value (on or off)
string$A string value

Before using any of these fields, however, you must make sure the required space is allocated for each (just like user databases).  You use the db.<field>.maxrecord syntax to change the size/structure of a database record.  The following code snippet allocates several field type slots for the persona database and sets up a dbalias mapping to access the fields symbolically.

#define MAKE_AT_LEAST(val, min)   if( (val) < (min) )then (val) = (min)

MAKE_AT_LEAST(persona.data.maxrecord, 15);
MAKE_AT_LEAST(persona.byte.maxrecord, 30);
MAKE_AT_LEAST(persona.flag.maxrecord, 150);
MAKE_AT_LEAST(persona.string$.maxrecord, 6);

...

dbalias persona
{
Room => data[1], (* Where the person currently is. (vroom db record) *)
Height => byte[1], (* Height in inches *)
Weight => byte[2], (* Weight in pounds *)
Race => byte[3], (* Current race (race db record) *)
Class => byte[4], (* Current profession *)
...
Prefix$ => string$[1], (* A custom prefix e.g. "Baron" *)
Suffix$ => string$[2], (* A custom suffix e.g. "Harbinger of Doom" *)
...
IsFemale => flag[1], (* 1 = female, 0 = male *)
IsOnFire => flag[2], (* Poor guy! He's on fire! *)
...
};

The snippet is clipped in order to save space, but presents enough to get a picture of how to go about both allocating and accessing the data in a database.  Keep in mind that declaring a dbalias entry does not allocate space for it (and a runtime error will be generated if you do not do so).  Note the use of a macro to set the the maxrecord values.  This is necessary because simply assigning the value could possibly erase data from each record.  For example, you have a running game, and the persona database has 20 byte fields.  When you start the game up the persona.byte.maxrecord will reflect this.  Now, if you mistakenly assign a new value of, say, 15 to it, it will discard those extra 5 bytes per record.  If those 5 bytes held important information, it would all suddenly be lost.  Be wary of modifying a maxrecord value, it is an easy way to both allocate space and to free it.

Clamping

Database bytes and variables can be clamped.  A byte can have a value between 0 and 255, and a variable can have a value between 0 and 65535.  The value of a byte or a variable will loop if you attempt to set it to a value higher or lower than its upper or lower bounds.  Clamping allows you to avoid this potentially undesirable behavior by clamping a byte or a variable at its max value if any attempt is made to set it to a higher value than it can store.  Clamping can also be enabled on a global basis, and individual bytes/variables can be configured to be explicitly clamped, explicitly unclamped, or to always obey the global setting.  The following tables illustrate how global and individual settings interact.

field.clamped             result
------------- ------------------
0 obey game.clamping
1 always clamp
2 never clamp

field.clamped game.clamping clamped?
------------ ------------- --------
0 0 no
1 0 yes
2 0 no
0 1 yes
1 1 yes
2 1 no

The following snippets illustrate how clamping can be used in practice.

game.byte[1].clamped = 0;
game.byte[1] = 257; (* value rolls over to 1 *)
game.byte[1] = 0; game.byte[1] -= 1; (* value rolls over to 255 *)
game.byte[1].clamped = 1; (* enable clamping on a specific byte *)
game.byte[1] = 257; (* value is clamped at 255 *)
game.byte[1] = 0; byte.byte[1] -= 1; (* value is clamped at 0 *)
game.clamping = 1; (* enable global clamping *)
game.variable[1] = 65537; (* value is clamped at 65535 *)
game.variable[1].clamped = 2; (* explicitly unclamp this byte *)
game.variable[1] = 65537; (* value rolls over to 1 *)

Node Database

The node database is what represents connected users in Rapture.  Each time someone connects to a running Rapture server, they are placed on a free node, and that node’s “status” is updated to reflect the new connection state.  Also, arbitrary data may be stored with that node which will not be written to disk, but persist across reloads.  This is useful for any information that is pertinent only while a user is connected.  You cannot delete node records like you can normal database records, you must ditch them.  Ditching a node will disconnect the networking socket connection associated with the node and free the node up for use.  It must also be noted, that there is a limit on how high you can set the maxrecord for the node database.  When purchasing a Rapture license, you are authorized for only a limited number of active nodes, and any attempt to set it higher than that limit will be quietly ignored (and an informational message printed to the console).  In addition to the fields provided by normal databases, node records have the following properties.

node[].ditch()

Ditch a user connected to this node, and free the data associated with it.  It then becomes available for new connections in the server.

node[].persona

This is an arbitrary persona record attached to this node.  While the number is arbitrary is MUST point to a valid record in the persona database.  It is used for both the player ‘database’ (discussed later) and for resolving error values when errors are generated.

node[].status

Each node has a status associated with it.  Nodes which are not connected have a status of 0.  Those newly connected, but before entering a single command have a status of 1.  Those who are established connections (they have already had status 1, and have entered at least one command) have a status of 2.  Upon an error which causes the networking link to be severed (or the node is ditched), the status becomes 3.  After status 3, no more data can be written to or received from the node, it is essentially shut down.  After a brief time period with a status of 3, the status will be promoted to 4.  This status simply notifies the game logic that Rapture is freeing this node and making it available to be re-used.  Any change in status will show up normally in the main loop’s call to the input()internal function.

node[].host$

This string describes where the location from which user is connecting.  Reverse DNS lookups are performed asynchronously and this field is updated when that information is obtained.  If DNS resolution is not possible, then this will be simply a string representation of the user’s IP address.

node[].ip[]

Use this to obtain the first through fourth bytes representing the user’s connecting IP address.

node[].ip1

Use this to obtain the first byte of the user’s connecting IP address.

node[].ip2

Use this to obtain the second byte of the user’s connecting IP address.

node[].ip3

Use this to obtain the third byte of the user’s connecting IP address.

node[].ip4

Use this to obtain the fourth byte of the user’s connecting IP address.

node[].echo

This will get or set the telnet local echo option of the user.  Turn this off (set it to 0) to make the user’s client not echo what they type (when entering a password for instance).

node[].ansi

Use this field to turn on or off the user’s receiving of ANSI color codes.  If this is off, any ansi color codes will be stripped from the output before being sent to the user.

node[].forecolor

When ANSI color is enabled for this node, this represents the foreground color of text being sent.  Colors are listed in the reference for the function <ansicolor$>().  Set it to change the output text foreground color.

node[].backcolor

When ANSI color is enabled for this node, this represents the background color of text being sent.  Colors are listed in the reference for the <ansicolor$>() function.  Note, however, that only the colors 0 through 7 may be used for backgrounds.  Set it to change the output text background color.

node[].width

Width specifies how many characters to send on a line before performing automatic word wrapping of output.  By default, this is 80 and is normal for most client software.  Change this value only if you know some information about the client(s) being used to connect to your server.

node[].disabled

Disabling a node will prevent Rapture from assigning a new user to it.  You can then treat the node as a normal player (assign a persona to it, store data on it), however all output will be ignored, and it will never generate any input.  This is used (mostly in older code) to ‘fake’ connecting users.

node[].atcp

The <ATCP> TELNET option has been enabled by the player’s client.  This indicates that you are free to send messages using the <atcp_msg>() routine.

node[].mxp

The <MXP> TELNET option has been enabled on this node.  Rapture does not support auto-negotiation of the MXP option, so this has to be set to 1 for each node that will be using MXP.  When MXP is set on a node, all >, <, and & characters will be converted automatically to their HTML counterparts.  Also, auto-wrapping will be disabled on any text that comes between ASCII characters ETX and EOT.

node[].ttype@

Stores the terminal types the client supports.  This is automatically negotiated and populated when a new node has connected.  This may not be immediately available after a node has connected, as the negotiation is essentially occurring in the background.  It may be helpful to delay a second or two after a node has connected before trying to process anything that would be contained within it.

node[].xterm256

256-color support has been enabled for this node.  This setting is not automatically set.  Game code will be required to enable this, based either on the contents of node[].ttype@, or by some configuration option chosen by the user.

Persona Database

This database is used to hold information regarding a character.  It’s only special feature is that it has an additional ‘name’ associated with each record which can be used to access the record by name using the internal routines (and also to provide more information when errors are generated by users).

persona[].name$

This is the persona (or character’s) name.  It is case sensitive and used in various internal Rapture routines.  Names should be unique for all personas, but it is not required (however, not doing so will cause search_persona() to operate incorrectly).

Player “Database”

There is an internal resourced called the player database, however it is not really an actual database.  It is a shortcut for accessing the node and persona databases to provide backward compatibility with old Vortex source code.  It is exactly equivalent to the persona database, however, it takes a node record instead of a persona record.  It then indirectly looks up which persona record to use based on the internal node[].persona field value.  For example:

(* Access data[5] of the persona of the player on node 4.
The following two options are equivalent.
*)
player[4].data[5]
persona[node[4].persona].data[5]

Due to the single level of indirection here, using the player ‘database’ is slightly slower than accessing the persona database directly.  Also, programming errors can be introduced if the node[].persona field is not kept in sync with the game logic at hand.  Use of the player database in new code is discouraged for this reason.

Object Database

This database is intended to hold information about ‘objects’ in a game.  An object, in this sense of the word is something in the game of which, something is a type of... a cumbersome definition indeed.  If, for instance, you were to create swords in your game.  The object database allows you to create one “sword” object and then later, via the replica database create instances of that object.  Object records hold (in addition to the normal database fields) the following fields.  They can be used to store common properties for that given object type (such as the weight of any sword created from the sword object).

object[].name$[]

Access an individual name of an object.  Each object can have multiple names associated with it.  This is powerful for the following reason: You can implement simple type heirarchies using a common naming syntax.  By assigning an objects name in decreasing order of specificity, you can group objects usefully.  For example, you want to create a sword and give it various types.

object[obj].name$.maxrecord = 3;
object[obj].name$[1] = "longsword";
object[obj].name$[2] = "sword";
object[obj].name$[3] = "blade";

You can see that by decreasing in specificity, you can provide a heirarchy of sorts.  This allows searches for “sword” to return this object (and any other object with that name).  This can be done arbitrarily and at runtime, thereby allowing a dynamic restructuring of data types at runtime.  Note, though, that this does not actually enforce any typing scheme on the object data, it is a naming convention only.

object[].name$.maxrecord

Set how many names this object has.  You can use this to implement simple type heirarchies using naming conventions.

object[].pname$

The ‘primary’ name for an object.  This is equivalent to object[].name$[1] and is provided for convenience only.

Replica Database

The replica database is used to represent instantiated objects in the game.  If you’re wielding a sword you’re actually wielding a replica of a sword.  If you’re fighting a mob, you’re fighting a replica of that mob.  This is an important distinction (between an object and a replica of that object).  There is exactly one object for any given type of ‘thing’ in the game, for instance, a “longsword”, even though there may be many actual longswords in the game.  The important identifying features of a replica, are that it has a ‘mother’ which is an object type, and it has a ‘room’ which is where the replica is currently at.  These two are important because various internal routines exist to make using them much easier.  For instance, finding a “sword” in a room is easily obtained by using the routine search_replica_room().  Note, that for replica, a room is simply an integer, it does not have to actually map to a record in the room[] db.  The mother, however, MUST map to a valid record in the object[] database.

Replica will easily be your largest database.  Every item or monster will have a corresponding replica record.  Needless to say, that the data actually stored in this replica record should be kept small.  A mob generally uses much more data than an average item, but since records must be large enough to hold the largest amount of data, this can lead to much wasted space.  For example, if a mob takes an additional 300 bytes of data to represent it, and a sword only takes 10 bytes, you’re wasting 290 bytes of data PER REPLICA THAT IS NOT A MOB.  If you have 400,000 replica in the world (easily reachable for a large MUD) and only 10,000 mobs, that’s about 107 megabytes of memory wasted.  Be smart here, and use some indirection.

replica[].mother

What type of object this replica represents.  This field in crucial in facilitating the internal routines operate properly.  In order for the replica to show up as a “sword” it’s mother object must have “sword” in its object name list.  This value MUST map to a valid record in the object[] database.

replica[].room

Where the replica currently resides.  This is also important, but it can be very flexible.  It does not imply any meaning to the integer that it holds, but each integer must represent the same virtual space (but this does not have to map to the room[] db necessarily).  For example, you can have a virtual room mapping such as the following:

database vroom = "VRoom-Data";
dbalias vroom
{
Type => byte[1], (* Type of the room *)
Record => data[1], (* Pointer to actual record data *)
};

#define VROOM_PHYSICAL 1
#define VROOM_INVENTORY 2
#define VROOM_CONTAINER 3

In the above setup, you could have the replica[].room refer to a record in this ‘vroom’ (virtual-room) database.  Each record in the vroom database holds both a type and record pointer (an integer).  This allows you to represent different locations with different data, such as VROOM_PHYSICAL pointing to the room[] db, VROOM_INVENTORY pointing to the persona[] db, or VROOM_CONTAINER pointing to the replica[] db (as the replica could be inside another replica).  The system does not force any meaning on the value, except in assuming that room ‘1234’ is universal (everything with ‘1234’ for its room will be in the same location).

This member is used in search_replica_room() and other internal routines.

replica[].name$

Obtain a unique name of the object.  This is a convenience routine that will concatenate the object’s primary name and the replica record number.  So, replica 1234, who’s mother is an object with a .pname$ of “bag”, then the replica name would be “bag1234”.

Game Database

The game database is a catch-all.  It allows information to be stored that is global to the entire environment and still let it persist.  It has all the default fields of databases, however there is really only one record (and you can’t delete it).  For this reason, the record part is left out of all game database references.  (e.g. game.data[5] instead of game[1].data[5]).  There is also a lot of extra functionality provided by fields in the game database.  Some are keyed directly to the system and reflect the current state of the host, etc.

game. timestamp$

This returns a string describing the current date and time in the format: “YYYY/MM/DD HH:MM:SS”.

game.year

This returns the current year.

game. month

This returns the current month (1-12).

game.day

This returns the current day of the month (1-31).

game. weekday

This returns the current day of the week (0-6, 0=Sunday).

game.hour

This returns the current hour (0-23).

game. minute

This returns the current minute (0-59).

game. second

This returns the current second (0-59).

game. utick

Returns the current microsecond (0-999999).

game.time

This returns the time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.

game.tick

This returns the current tick value (a tick occurs every 1/100th of a second) since Rapture was started.

game. verb. maxrecord

When verbs are declared, they are entered into a table.  This is the size of that table (i.e. how many verbs are defined in the executable).

game. backtrace$

This returns a string formatted to describe the current call stack and parameter values.  The same is printed on each error to the error log.  Useful for tracking what procedure(s) are calling a another procedure.

game. timeout

This is the value (in seconds) that Rapture will allow an executable to run before deciding the code is in an infinite loop and generating an error.  It is essentially the time between calls to input() or wait_for_input().  A timeout value of 0 disables infinite loop detection.  Generally you want to keep this value relatively small (i.e. a few seconds) but for time intensive tasks, it’s okay to up it a bit.

game. rawinput$

This is set to the input received from a user on a node (after calling input()).

game. input$

This is the same as rawinput$, but preceding whitespace has been stripped.

game. taskobject

When a task is called, this is set to the value provided when the task was scheduled.  (The replica ID for replica_task, etc.)

game. stats$

This provides an informational string describing the internal state and statistics of the Rapture engine at runtime.

game. dbstats$

This provides an informational string indicating the database structure currently in use by the Rapture engine.

game. tasks$

This provides an up-to-date listing of all currently scheduled tasks and when they are due to fire.  Useful for identifying task contention or hotspots of task grouping.

game. mqhandler$

This is the name of a function taking a single string parameter which is designated as the procedure to call when an IPC message is received.  The subroutine returns the text that should be given back to the requesting process.  Set this to “” to ignore incoming IPC messages.  See <FeatIPC.txt> for more information.

game. atcpmsg$

When an ATCP message is received from a node, this will be nonempty and contain the full body of the message.  When this happens, game.input$ will be “”.  Note this will only ever happen for a node if it has successfully negotiated the ATCP TELNET option.

game. mxpmsg$

When an MXP message is received from a node, this will be nonempty and will contain the full body of the message, with the MXP control codes stripped.