Go to the previous, next chapter.

Server Commands and Database Assumptions

This chapter describes all of the commands that are built into the server and every property and verb in the database specifically accessed by the server. Aside from what is listed here, no assumptions are made by the server concerning the contents of the database.

Built-in Commands

As was mentioned in the chapter on command parsing, there are three commands whose interpretation is fixed by the server: PREFIX, SUFFIX, and .program. The first two of these are intended for use by programs that connect to the MOO, so-called `client' programs. The .program command is used by programmers to associate a MOO program with a particular verb.

The server also performs special processing on command lines that begin with certain punctuation characters.

This section discusses these built-in pieces of the command-interpretation process.

Command-Output Delimiters

Every MOO network connection has associated with it two strings, the output prefix and the output suffix. Just before executing a command typed on that connection, the server prints the output prefix, if any, to the player. Similarly, just after finishing the command, the output suffix, if any, is printed to the player. Initially, these strings are not defined, so no extra printing takes place.

The PREFIX and SUFFIX commands are used to set and clear these strings. They have the following simple syntax:

PREFIX  output-prefix
SUFFIX  output-suffix

That is, all text after the command name and any following spaces is used as the new value of the appropriate string. If there is no non-blank text after the command string, then the corresponding string is cleared.

These commands are intended for use by programs connected to the MOO, so that they can issue MOO commands and reliably determine the beginning and end of the resulting output. For example, one editor-based client program sends this sequence of commands on occasion:

PREFIX >>MOO-Prefix<<
SUFFIX >>MOO-Suffix<<
@list object:verb without numbers
PREFIX
SUFFIX

The effect of which, in a LambdaCore-derived database, is to print out the code for the named verb preceded by a line containing only >>MOO-Prefix<< and followed by a line containing only >>MOO-Suffix<<. This enables the editor to reliably extract the program text from the MOO output and show it to the user in a separate editor window. There are many other possible uses.

The built-in function output_delimiters() can be used by MOO code to find out the output prefix and suffix currently in effect on a particular network connection.

Programming

The .program command is a common way for programmers to associate a particular MOO-code program with a particular verb. It has the following syntax:

.program object:verb
...several lines of MOO code...
.

That is, after typing the .program command, then all lines of input from the player are considered to be a part of the MOO program being defined. This ends as soon as the player types a line containing only a dot (.). When that line is received, the accumulated MOO program is checked for proper MOO syntax and, if correct, associated with the named verb.

In the .program command, object may have one of three forms:

Initial Punctuation in Commands

The server interprets command lines that begin with any of the following characters specially:

"        :        ;

Before processing the command, the initial punctuation character is replaced by the corresponding word below, followed by a space:

say      emote    eval

For example, the command line

"Hello, there.

is transformed into

say Hello, there.

before parsing.

Server Assumptions About the Database

There are a small number of circumstances under which the server directly and specifically accesses a particular verb or property in the database. This section gives a complete list of such circumstances.

The Very First Task

Whenever the server is booted, it checks for the existence of the verb #0:server_started(). If there is such a verb, then the very first task run is a server task invoking that verb with no arguments and with player equal to #-1. This task runs even before the server gets the value of #0.dump_interval to schedule the first checkpoint. (See below for more information on checkpoint scheduling.)

Checkpointing the Database

The server maintains the entire MOO database in main memory, not on disk. It is therefore necessary for it to dump the database to disk if it is to persist beyond the lifetime of any particular server execution. The server is careful to dump the database just before shutting down, of course, but it is also prudent for it to do so at regular intervals, just in case something untoward happens.

To determine how often to make these checkpoints of the database, the server consults the value of #0.dump_interval. If it exists and its value is a number greater than or equal to 60, then it is taken as the number of seconds to wait between checkpoints; otherwise, the server makes a new checkpoint every 3600 seconds (one hour).

The decision about how long to wait between checkpoints is made again immediately after each one begins. Thus, changes to #0.dump_interval will take effect after the next checkpoint happens.

Whenever the server begins to make a checkpoint, it makes the following verb call:

#0:checkpoint_started()

When the checkpointing process is complete, the server makes the following verb call:

#0:checkpoint_finished(success)

where success is true if and only if the checkpoint was successfully written on the disk. Checkpointing can fail for a number of reasons, usually due to exhaustion of various operating system resources such as virtual memory or disk space.

Associating Network Connections with Players

When a network connection is first made to the MOO, it is identified by a unique, negative object number. Such a connection is said to be un-logged-in and is not yet associated with any MOO player object.

Each line of input on an un-logged-in connection is first parsed into words in the usual way (see the chapter on command parsing for details) and then these words are passed as the arguments in a call to the verb #0:do_login_command(). For example, the input line

connect Munchkin frebblebit

would result in the following call being made:

#0:do_login_command("connect", "Munchkin", "frebblebit")

In that call, the variable player will have as its value the negative object number associated with the appropriate network connection. The functions notify() and boot_player() can be used with such object numbers to send output to and disconnect un-logged-in connections. Also, the variable argstr will have as its value the unparsed command line as received on the network connection.

If #0:do_login_command returns a valid player object, then the connection is considered to have logged into that player. The server then makes one of the following verbs calls, depending on the player object that was returned:

#0:user_created(player)
#0:user_connected(player)
#0:user_reconnected(player)

The first of these is used if the returned object number is greater than the value returned by the max_object() function before #0:do_login_command was invoked, that is, it is called if the returned object appears to have been freshly created. If this is not the case, then one of the other two verb calls is used. The #0:user_connected call is used if there was no existing active connection for the returned player object. Otherwise, the #0:user_reconnected call is used instead.

When the network connection is terminated, either by the client side or though the use of the boot_player() function, then one of the following two verb calls is made:

#0:user_disconnected(player)
#0:user_client_disconnected(player)

The first is used if the disconnection is due to a use of the boot_player() function and the second if the disconnection was initiated by the client side.

It is not an error if any of these five verbs do not exist; the corresponding call is simply skipped.

Note: Only one network connection can be controlling a given player object at a given time; should a second connection attempt to log in as that player, the first connection is unceremoniously closed (and #0:user_reconnected called, as described above). This makes it easy to recover from various kinds of network problems that leave connections open but inaccessible.

When the network connection is first established, the null command is automatically entered by the server, resulting in an initial call to #0:do_login_command with no arguments. This signal can be used by the verb to print out a welcome message, for example.

Warning: If there is no #0:do_login_command verb defined, then the line of input is simply discarded. Thus, it is necessary for any database to include a suitable definition for this verb.

Out-of-Band Commands

It is possible to compile the server with an option defining an out-of-band prefix for commands. This is a string that the server will check for at the beginning of every line of input from players, regardless of whether or not those players are logged in and regardless of whether or not reading tasks are waiting for input from those players. If a given line of input begins with the defined out-of-band prefix (leading spaces, if any, are not stripped before testing), then it is not treated as a normal command or as input to any reading task. Instead, the line is parsed into a list of words in the usual way and those words are given as the arguments in a call to #0:do_out_of_band_command. For example, if the out-of-band prefix were defined to be #$#, then the line of input

#$# client-type fancy

would result in the following call being made in a new server task:

#0:do_out_of_band_command("#$#", "client-type", "fancy")

During the call to #0:do_out_of_band_command, the variable player is set to the object number representing the player associated with the connection from which the input line came. Of course, if that connection has not yet logged in, the object number will be negative. Also, the variable argstr will have as its value the unparsed input line as received on the network connection.

Out-of-band commands are intended for use by fancy client programs that may generate asynchronous events of which the server must be notified. Since the client cannot, in general, know the state of the player's connection (logged-in or not, reading task or not), out-of-band commands provide the only reliable client-to-server communications channel.

Matching in Command Parsing

In the process of matching the direct and indirect object strings in a command to actual objects, the server uses the value of the aliases property, if any, on each object in the contents of the player and the player's location. For complete details, see the chapter on command parsing.

Creating and Recycling Objects

Whenever the create() function is used to create a new object, that object's initialize verb, if any, is called with no arguments. The call is simply skipped if no such verb is defined on the object.

Symmetrically, just before the recycle() function actually destroys an object, the object's recycle verb, if any, is called with no arguments. Again, the call is simply skipped if no such verb is defined on the object.

Both create() and recycle() check for the existence of an ownership_quota property on the owner of the newly-created or -destroyed object. If such a property exists and its value is a number, then it is treated as a quota on object ownership. Otherwise, the following two paragraphs do not apply.

The create() function checks whether or not the quota is positive; if so, it is reduced by one and stored back into the ownership_quota property on the owner. If the quota is zero or negative, the quota is considered to be exhausted and create() returns E_QUOTA.

The recycle() function increases the quota by one and stores it back into the ownership_quota property on the owner.

Object Movement

During evaluation of a call to the move() function, the server can make calls on the accept and enterfunc verbs defined on the destination of the move and on the exitfunc verb defined on the source. The rules and circumstances are somewhat complicated and are given in detail in the description of the move() function.