Skip to content

Use #0:do_match to resolve nouns in commands#16

Open
ojacobson wants to merge 6 commits intowrog:mainfrom
ojacobson:db-match-callback
Open

Use #0:do_match to resolve nouns in commands#16
ojacobson wants to merge 6 commits intowrog:mainfrom
ojacobson:db-match-callback

Conversation

@ojacobson
Copy link
Copy Markdown

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 invoking huh 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.

Owen Jacobson added 6 commits September 5, 2012 08:52
The plan is to have the command line parsing retain the existing taskid when
calling in-DB functions for processing the command string. This should avoid
breaking things that rely on command-related verb calls all coming in with the
same taskid (paging in LambdaCore, for example).
The intent is to add a branch to match_object that tries matching via
an in-server callback, then falls back to server-side matching if in-server
matching fails outside of prescribed ways.
Following the same general rules as #0:do_command, this allows DBs to replace
the server's name-to-object matching, supporting a few use cases:

* Adding uniform support for new kinds of names (such as LambdaMOO's
  `*mailing-list` convention), rather than hacking it into commands on the
  player or room classes.
* Supporting smarter disambiguation protocols.
* Allowing in-DB matching to behave IDENTICALLY to command matching, rather
  than requiring a parallel implementation within the DB.
* Restricting players from using "privileged" names - object numbers and
  dollar names - in game-like servers.

The server still parses out pronouns and slots strings into dobjstr/iobjstr,
but delegates to #0:do_match to match strings to objects. Server-side matching
can return negative object numbers to indicate failed or ambiguous matches.
Returning a non-object value, raising an uncaught exception, suspending, or
waiting for input in $do_match() will cause the server to continue with the
built-in matching protocol.
Through the entire do_match, do_command, actual verb, do_huh sequence, the
input task maintains a consistent task_id.

I un-stitched run_server_command_setting_id since it was unusable outside of
tasks.c anyways (types used in its declaration are only in tasks.c anyways) in
the process. We now have run_server_command (which starts a new taskid) and
run_server_command_in_current_id (which uses the current taskid).
Players connected via listen()-registered handlers now get do_match from their
listener's handler, not from #0, to be consistent with do_command and
do_login_command.
@wrog wrog added the Built-in Parser proposed change to built-in command parser (feel free to discuss, but deferred to 1.9.1+) label Oct 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Built-in Parser proposed change to built-in command parser (feel free to discuss, but deferred to 1.9.1+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants