lambdamoo
lambdamoo copied to clipboard
Use #0:do_match to resolve nouns in commands
This change permits in-DB code to override the server's noun-to-object matching by providing a hook (do_match
) on the player's connection handler. As with other in-server command parsing hooks (do_command
, do_login_command
), the handler is controlled using the listen
built-in.
do_match
hooks are called with one argument (the string to be matched to a noun), and can either return non-object values to instruct the server to continue with built-in matching or return any object number to indicate matching success. To indicate a failed match, return #-1
($nothing
in LambdaMOO), #-2
($ambiguous_match
), or #-3
($failed_match
) as appropriate, not 0
. Raising an exception, suspending, or stopping to read input will also cause built-in noun matching to proceed.
The do_match
hooks are invoked before do_command
, and share a taskid with the rest of the input task pipeline (do_command
, command verbs, and huh
calls).
This was motivated by a few scenarios:
- In "game"-like rather than social MOOs, the built-in noun matching may be inappropriate: it permits references to bare object numbers, requiring every command verb to check that its direct or indirect objects are actually reachable. Putting this logic into
$do_match
makes this kind of checking more consistent and streamlines error handling (returning$nothing
rather than invokinghuh
directly, for example). - The built-in matching is inextensible without server patches. For example, LambdaMOO's
*name
convention for mailing lists is handled by special-case matching in mailing list commands, which have to reside on players or in the player's feature chain rather than on the mailing list object itself. Matching*name
to mailing lists early in command parsing means mailing list verbs can live on the mailing lists themselves, and that other commands can interact with mailing lists consistently. - Compound noun matching requires an in-DB copy of the server's noun matching for consistency. For example, in
@verb Bob:look_self tnt rxd
,Bob:look_self
is a "compound" noun containing both the location of the verb (Bob
) and the name of the verb. Matching the location to an object has to be done in-DB, as there's no way to invoke the server's matcher. Having the server also use an in-DB matcher means it's easier to get this kind of object matching to work consistently with command verbs than it would be by reimplementing the stock matcher in MOO. (If you think this is easy, then, without looking, do you know if$name
strings are supported? Do you know if room contents are matched before or after player inventories?)
This change also restructures the run_server_task…
family of functions a bit: the _setting_id
function is gone, replaced with a start_new_task()
function in tasks.c
, and a new _in_current_id
function permits hook calls to reuse the ongoing input task ID. run_server_task
itself still starts a new taskid before calling any verbs.
An appropriate sample implementation for LambdaMOO might be
@verb #0:do_match tnt rxd
@program #0:do_match
{name} = args;
return $string_utils:match_object(name, player.location, player);
.
It is possible to render your database unusable with this hook. The do_match
hook is also invoked for the .program
builtin and for the emergency wizard mode recovery commands. If your do_match
hook renders its location unresolvable by anyone, the following eval
command may be able to help you recover:
;; info = verb_info(#0, "do_match"); info[3] = "do_match_disabled"; set_verb_info(#0, "do_match", info);
It is possible to make command-handling very slow with this hook. The hook is invoked for each of the direct and indirect objects with a full execution quota each time. This means that under the default settings, commands that need to resolve both objects can add up to ten seconds to command handling. This is unlikely, but it's a thing to be aware of when implementing this hook. The do_match
hook is also evaluated before do_command
if both are present.