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.
As was mentioned in the chapter on command parsing, there are five commands
whose interpretation is fixed by the server: PREFIX
,
OUTPUTPREFIX
, SUFFIX
, OUTPUTSUFFIX
, and .program
.
The first four 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 can,
in addition, recognize a sixth special command on any or all connections, the
flush command.
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.
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. For
compatibility with some general MUD client programs, the server also recognizes
OUTPUTPREFIX
as a synonym for PREFIX
and OUTPUTSUFFIX
as a
synonym for SUFFIX
.
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.
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.
If, at the time the line containing only a dot is processed, (a) the player is
not a programmer, (b) the player does not have write permission on the named
verb, or (c) the property $server_options.protect_set_verb_code
exists
and has a true value and the player is not a wizard, then an error message is
printed and the named verb's program is not changed.
In the .program
command, object may have one of three forms:
#number
.
#0
), in the form
$name
. In this case, the current value of #0.name
must be a valid object.
It sometimes happens that a user changes their mind about having typed one or more lines of input and would like to `untype' them before the server actually gets around to processing them. If they react quickly enough, they can type their connection's defined flush command; when the server first reads that command from the network, it immediately and completely flushes any as-yet unprocessed input from that user, printing a message to the user describing just which lines of input were discarded, if any.
Fine point: The flush command is handled very early in the server's processing of a line of input, before the line is entered into the task queue for the connection and well before it is parsed into words like other commands. For this reason, it must be typed exactly as it was defined, alone on the line, without quotation marks, and without any spaces before or after it.
When a connection is first accepted by the server, it is given an initial flush
command setting taken from the current default. This initial setting can be
changed later using the set_connection_option()
command.
By default, each connection is initially given `.flush' as its flush
command. If the property $server_options.default_flush_command
exists,
then its value overrides this default. If
$server_options.default_flush_command
is a non-empty string, then that
string is the flush command for all new connections; otherwise, new connections
are initially given no flush command at all.
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.
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.
Many optional behaviors of the server can be controlled from within the
database by creating the property #0.server_options
(also known as
$server_options
), assigning as its value a valid object number, and then
defining various properties on that object. At a number of times, the server
checks for whether the property $server_options
exists and has an object
number as its value. If so, then the server looks for a variety of other
properties on that $server_options
object and, if they exist, uses their
values to control how the server operates.
The specific properties searched for are each described in the appropriate section below, but here is a brief list of all of the relevant properties for ease of reference:
bg_seconds
bg_ticks
connect_timeout
default_flush_command
fg_seconds
fg_ticks
max_stack_depth
name_lookup_timeout
outbound_connect_timeout
protect_property
protect_function
support_numeric_verbname_strings
There are a number of circumstances under which the server itself generates
messages on network connections. Most of these can be customized or even
eliminated from within the database. In each such case, a property on
$server_options
is checked at the time the message would be printed. If
the property does not exist, a default message is printed. If the property
exists and its value is not a string or a list containing strings, then no
message is printed at all. Otherwise, the string(s) are printed in place of
the default message, one string per line. None of these messages are ever
printed on an outbound network connection created by the function
open_network_connection()
.
The following list covers all of the customizable messages, showing for each
the name of the relevant property on $server_options
, the default
message, and the circumstances under which the message is printed:
boot_msg = "*** Disconnected ***"
boot_player()
was called on this connection.
connect_msg = "*** Connected ***"
$do_login_command()
was called.
create_msg = "*** Created ***"
$do_login_command()
was called.
recycle_msg = "*** Recycled ***"
redirect_from_msg = "*** Redirecting connection to new port ***"
redirect_to_msg = "*** Redirecting old connection to this port ***"
server_full_msg
*** Sorry, but the server cannot accept any more connections right now. *** Please try again later.This connection arrived when the server really couldn't accept any more connections, due to running out of a critical operating system resource.
timeout_msg = "*** Timed-out waiting for login. ***"
CONNECT_TIMEOUT
seconds (as defined in the file `options.h' when
the server was compiled).
Fine point: If the network connection in question was received at a listening point (established by the `listen()' function) handled by an object obj other than
#0
, then system messages for that connection are looked for onobj.server_options
; if that property does not exist, then$server_options
is used instead.
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 an integer 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). If the value of
#0.dump_interval
implies that the next checkpoint should be scheduled at
a time after 3:14:07 a.m. on Tuesday, January 19, 2038, then the server instead
uses the default value of 3600 seconds in the future.
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:
$checkpoint_started()
When the checkpointing process is complete, the server makes the following verb call:
$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. It is not an error if either of these verbs does not exist; the corresponding call is simply skipped.
When the server first accepts a new, incoming network connection, it is given
the low-level network address of computer on the other end. It immediately
attempts to convert this address into the human-readable host name that will be
entered in the server log and returned by the connection_name()
function. This conversion can, for the TCP/IP networking configurations,
involve a certain amount of communication with remote name servers, which can
take quite a long time and/or fail entirely. While the server is doing this
conversion, it is not doing anything else at all; in particular, it it not
responding to user commands or executing MOO tasks.
By default, the server will wait no more than 5 seconds for such a name lookup
to succeed; after that, it behaves as if the conversion had failed, using
instead a printable representation of the low-level address. If the property
name_lookup_timeout
exists on $server_options
and has an integer
as its value, that integer is used instead as the timeout interval.
When the open_network_connection()
function is used, the server must
again do a conversion, this time from the host name given as an argument into
the low-level address necessary for actually opening the connection. This
conversion is subject to the same timeout as in the in-bound case; if the
conversion does not succeed before the timeout expires, the connection attempt
is aborted and open_network_connection()
raises E_QUOTA
.
After a successful conversion, though, the server must still wait for the
actual connection to be accepted by the remote computer. As before, this can
take a long time during which the server is again doing nothing else. Also as
before, the server will by default wait no more than 5 seconds for the
connection attempt to succeed; if the timeout expires,
open_network_connection()
again raises E_QUOTA
. This default
timeout interval can also be overridden from within the database, by defining
the property outbound_connect_timeout
on $server_options
with an
integer as its value.
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
$do_login_command()
. For example, the input line
connect Munchkin frebblebit
would result in the following call being made:
$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 $do_login_command()
returns a valid player object and the connection
is still open, 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:
$user_created(player) $user_connected(player) $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
$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 $user_connected()
call is used
if there was no existing active connection for the returned player object.
Otherwise, the $user_reconnected()
call is used instead.
Fine point: If a user reconnects and the user's old and new connections are on two different listening points being handled by different objects (see the description of the
listen()
function for more details), thenuser_client_disconnected
is called for the old connection anduser_connected
for the new one.
If an in-bound network connection does not successfully log in within a certain
period of time, the server will automatically shut down the connection, thereby
freeing up the resources associated with maintaining it. Let L be the
object handling the listening point on which the connection was received (or
#0
if the connection came in on the initial listening point). To
discover the timeout period, the server checks on
L.server_options
or, if it doesn't exist, on
$server_options
for a connect_timeout
property. If one is found
and its value is a positive integer, then that's the number of seconds the
server will use for the timeout period. If the connect_timeout
property
exists but its value isn't a positive integer, then there is no timeout at
all. If the property doesn't exist, then the default timeout is 300 seconds.
When any network connection (even an un-logged-in or outbound one) is terminated, by either the server or the client, then one of the following two verb calls is made:
$user_disconnected(player) $user_client_disconnected(player)
The first is used if the disconnection is due to actions taken by the server
(e.g., a use of the boot_player()
function or the un-logged-in timeout
described above) 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
$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
$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
$do_login_command()
verb defined, then lines of input from un-logged-in connections are simply discarded. Thus, it is necessary for any database to include a suitable definition for this verb.
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
$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:
$do_out_of_band_command("#$#", "client-type", "fancy")
During the call to $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.
Whenever the server is booted, there are a few tasks it runs right at the
beginning, before accepting connections or getting the value of
#0.dump_interval
to schedule the first checkpoint (see below for more
information on checkpoint scheduling).
First, the server calls $user_disconnected()
once for each user who
was connected at the time the database file was written; this allows for any
cleaning up that's usually done when users disconnect (e.g., moving their
player objects back to some `home' location, etc.).
Next, it checks for the existence of the verb $server_started()
. If
there is such a verb, then the server runs a task invoking that verb with no
arguments and with player
equal to #-1
. This is useful for
carefully scheduling checkpoints and for re-initializing any state that is not
properly represented in the database file (e.g., re-opening certain outbound
network connections, clearing out certain tables, etc.).
As described earlier, in the section describing MOO tasks, the server places
limits on the number of seconds for which any task may run continuously and the
number of "ticks," or low-level operations, any task may execute in one
unbroken period. By default, foreground tasks may use 30,000 ticks and five
seconds, and background tasks may use 15,000 ticks and three seconds. These
defaults can be overridden from within the database by defining any or all of
the following properties on $server_options
and giving them integer
values:
bg_seconds
bg_ticks
fg_seconds
fg_ticks
The server ignores the values of fg_ticks
and bg_ticks
if they
are less than 100 and similarly ignores fg_seconds
and bg_seconds
if their values are less than 1. This may help prevent utter disaster should
you accidentally give them uselessly-small values.
Recall that command tasks and server tasks are deemed foreground tasks, while forked, suspended, and reading tasks are defined as background tasks. The settings of these variables take effect only at the beginning of execution or upon resumption of execution after suspending or reading.
The server also places a limit on the number of levels of nested verb calls,
raising E_MAXREC
from a verb-call expression if the limit is exceeded.
The limit is 50 levels by default, but this can be increased from within the
database by defining the max_stack_depth
property on
$server_options
and giving it an integer value greater than 50. The
maximum stack depth for any task is set at the time that task is created and
cannot be changed thereafter. This implies that suspended tasks, even after
being saved in and restored from the DB, are not affected by later changes to
$server_options.max_stack_depth.
Finally, the server can place a limit on the number of forked or suspended
tasks any player can have queued at a given time. Each time a fork
statement or a call to suspend()
is executed in some verb, the server
checks for a property named queued_task_limit
on the programmer. If
that property exists and its value is a non-negative integer, then that integer
is the limit. Otherwise, if $server_options.queued_task_limit
exists
and its value is a non-negative integer, then that's the limit. Otherwise,
there is no limit. If the programmer already has a number of queued tasks that
is greater than or equal to the limit, E_QUOTA
is raised instead of
either forking or suspending. Reading tasks are affected by the queued-task
limit.
The server will abort the execution of tasks for either of two reasons:
In each case, after aborting the task, the server attempts to call a particular handler verb within the database to allow code there to handle this mishap in some appropriate way. If this verb call suspends or returns a true value, then it is considered to have handled the situation completely and no further processing will be done by the server. On the other hand, if the handler verb does not exist, or if the call either returns a false value without suspending or itself is aborted, the server takes matters into its own hands.
First, an error message and a MOO verb-call stack traceback are printed to the player who typed the command that created the original aborted task, explaining why the task was aborted and where in the task the problem occurred. Then, if the call to the handler verb was itself aborted, a second error message and traceback are printed, describing that problem as well. Note that if the handler-verb call itself is aborted, no further `nested' handler calls are made; this policy prevents what might otherwise be quite a vicious little cycle.
The specific handler verb, and the set of arguments it is passed, differs for the two causes of aborted tasks.
If an error is raised and not caught, then the verb-call
$handle_uncaught_error(code, msg, value, traceback, formatted)
is made, where code, msg, value, and traceback are the
values that would have been passed to a handler in a try
-except
statement and formatted is a list of strings being the lines of error and
traceback output that will be printed to the player if
$handle_uncaught_error
returns false without suspending.
If a task runs out of ticks or seconds, then the verb-call
$handle_task_timeout(resource, traceback, formatted)
is made, where resource is the appropriate one of the strings
"ticks"
or "seconds"
, and traceback and formatted are
as above.
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.
Whenever verb code attempts to read the value of a built-in property prop
on any object, the server checks to see if the property
$server_options.protect_prop
exists and has a true value. If so,
then E_PERM
is raised if the programmer is not a wizard.
Whenever verb code calls a built-in function func()
and the caller
is not the object #0
, the server checks to see if the property
$server_options.protect_func
exists and has a true value. If so,
then the server next checks to see if the verb $bf_func()
exists;
if that verb exists, then the server calls it instead of the built-in
function, returning or raising whatever that verb returns or raises. If the
$bf_func()
does not exist and the programmer is not a wizard, then
the server immediately raises E_PERM
, without actually calling
the function. Otherwise (if the caller is #0
, if
$server_options.protect_func
either doesn't exist or has a false
value, or if $bf_func()
exists but the programmer is a wizard),
then the built-in function is called normally.
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 an integer, 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()
raises E_QUOTA
.
The recycle()
function increases the quota by one and stores it back
into the ownership_quota
property on the owner.
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.
If the property $server_options.support_numeric_verbname_strings
exists
and has a true value, then the server supports a obsolete mechanism for less
ambiguously referring to specific verbs in various built-in functions. For
more details, see the discussion given just following the description of the
verbs()
function.
Go to the first, previous, next, last section, table of contents.