The purpose of this document is to provide a high-level explanation of the command handling process. This document is not intended to be a tutorial on adding action support to your objects. If that's what you're looking for, please see the Actions manual.
Users obviously have to be able to type commands in order to communicate their wishes to the MUD world. The handling of those commands is, therefore, an integral aspect of player interaction with the game world.
Commands can be divided up into different categories. There are some basic ever-present commands that are made available to all players, such as look, say, and who. There are specialized ever-present commands that are made available only to select players, such as guild commands made available to members of that guild. And then there are commands that are made available by individual objects in the world, such as a read command from a book. These latter commands are quite transient and are generally only available to the player while the object is in the player's inventory or environment. It's also possible for the player to possess multiple objects that make available commands with the same name, such as a book with a read command and a scroll with a read command. Finally, it will sometimes be necessary for an object, such as an editor, to have full, exclusive control over the player input. The command-handling process must be flexible enough to account for all of these scenarios, and simple enough for coders to easily add command support to their objects that warrant it.
There are different ways of command handling currently employed by various MUDs. Some have a central repository and implementation of each command, which is referenced by objects that want to provide that command. This single-implementation approach has the advantages of eliminating code duplication, maintaining consistency in which commands are associated with which actions, and enforcing the same syntax for a particular command across objects. It also allows the MUD administrators to keep track of which commands are currently available in the MUD. But this latter point is also a big disadvantage of this approach. Anytime a coder wants their object to support a command that is not yet available, they must convince the MUD administrators to design the command, work out the syntax, implement it, and add it to the repository.
An alternative is a system that lets objects add commands to players at will. In this system, each player has a command (or "action") manager that keeps track of what commands are currently available to the player. Objects can register new commands with a player's action manager when appropriate, and they can remove access to those commands later on. In this system, commands are usually implemented by the objects making those commands available, rather than in a central repository. The advantage to this system is that it allows for more freedom for the coders, but can also support the central-repository approach, if desired (along with some development guidelines). It's this latter "ad hoc" approach that was chosen for the Bluemud command handling system.
Command handling actually begins in the Driver, which is responsible for pulling the input from the network socket and creating an instance of an InputEvent which is put onto the event queue. When it's executed by the event engine, it will pass the player input to the mudlib.mind.Mind instance associated with that player, invoking the Mind's processInput method.
The Mind class is, as its name implies, the center of thought for an interactive entity: output from the world is written to the Mind, and input from the entity originates from it. In the case of NPCs, this is where their artificial intelligence would be implemented. In the case of players, it acts as a bridge between the world and the person sitting at the terminal. The command handling process is only concerned with input arriving from the player. The processInput method in the Mind manages the first round of command processing delegation. Player commands can be generally handled in one of three ways: by special "input processing methods," by bulk "command handlers," or by "action handlers" from objects in the player's inventory or environment.
Input processing methods are usually used for special-purpose processing in which it's necessary to have full, exlusive control over handling of player input. The primary examples are editors, although some commands, such as search, use processing methods to intercept player input to simulate the player being busy and therefore unable to execute commands. Processing methods are pushed onto a player's processor stack via the pushInputProcessor method of the Mind. If any processors exist on the processor stack, all input from that player will be routed to the most recently pushed processor (and nowhere else). When a processing method is no longer needed, it should be popped from the processor stack via the popInputProcessor method.
Bulk command handlers (often just referred to as command handlers) are classes setup specifically to handle a wide range of commands. Support for all of the basic player commands, such as look, say, and who, are bundled into a single command handling class. Likewise, all of the basic coder commands are bundled into a separate, single command handling class. To enable a command handler for a player, its register method must be invoked and passed an instance of the player's associated ActionManager. The register method, in turn, will invoke the ActionManager's addAction method for each of the commands made available by the command handler (more about the addAction method later).
Action handlers are individual methods written specifically to handle commands provided by objects in a player's inventory or environment. Action handlers are usually written as part of the object making the command available, and are made active for a player by invoking the addAction method on the player's ActionManager. When the command is to become unavailable, the ActionManager's removeAction method should be used.
The mudlib.living.ActionManager class is responsible for managing the commands currently available to a player, whether they come from a command handler or from an object. In both cases, commands are added through the addAction method and removed through the removeAction method.
When the processInput method of the player's Mind is invoked, it first checks to see if there are any processing methods on the player's input processor stack. If there are, then control is passed exclusively to the processor most recently pushed. If there are no processing methods, the Mind's processCommand method is invoked, which is responsible for breaking up the input into separate commands, which can be separated by semicolons, and then passing each command to the processAction method of the player's ActionManager instance (with a short delay between each invocation in the case of multiple commands in order to keep things at least a little fair).
The processAction method will first lookup the command in the player's alias map and, if it finds a match, will defer to the processCommandAlias method to replace the command with whatever string the alias maps to. It only does this once per command in order to prevent against the possibility of infinite aliasing loops. It then passes control to the processCommandAction method, which will run through the mapping of registered actions and determine if any match the command. It does this by comparing the first word in the input to the command name that was provided to the ActionManager when the command was added. Commands are compared in the opposite order that they were added (so most recently added first). If there is a match, then the method associated with the matching action is invoked and passed the player input. If that method returns a false value, then it indicates its failure to handle the command (perhaps because of bad syntax), and the processCommandAction method will continue looking for any additional matches. This continues until a matching action method returns true, indicating successful handling of the command, or until no more matches are found. In this latter case, a failure message is displayed to the player. The failure message will be the last one set by a failing action method via the notifyFail method, or a default "What?" message if no specific failure messages were set.
Control is then returned back to the Driver, and the InputEvent terminates.
The intent of the implementation is to provide a very flexible command handling system that is easy to interact with. Objects need only call a single addAction method in a player's ActionManager instance to make available a new command to the player, and call the removeAction method to make it unavailable. The register method of the bulk command handlers does exactly that: it invokes addAction for each of the commands it has an implementation for, just the same way that an object adds its action. The only difference is that bulk command handlers don't ever remove their commands, since they're intended to be of the basic ever-present variety. Finally, the input processor mechanism provides a way for objects to gain complete control over player input when needed.
For more details and specifics, the documentation for the mudlib.mind.Mind and mudlib.living.ActionManager classes should be consulted. For an example of a bulk command handler, the mudlib.commands.BaseCommandHandler class can be perused. For an example of a command using an input processor, please see the search method in the mudlib.commands.BaseCommandHandler class. For an example of an object that makes a command available to players, see the tavern.items.SmellyFish class.