Iron Realms Entertainment
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.
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.
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)
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.
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.
The following snippets illustrate how clamping can be used in practice.
game.byte.clamped = 0;
game.byte.clamped = 1; (* enable clamping on a specific byte *)
game.clamping = 1; (* enable global clamping *)
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.
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.
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.
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.
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.
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.
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.
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).
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 of the persona of the player on node 4.
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.
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).
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;
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.
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.
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.
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";
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.
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 instead of game.data). 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.
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.
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.