02aug09 CmdrZin

Keying Items To Doors in DarkMud

How do you require the player to have a certain item to be able to get through a certain door?

This paper will look at the com.sun.sgs.darkmud.locked package. It supplies support for keys that can be used to unlock or lock doors that are lockable. The following are classes that were added to support keys and lockable doors.
    ManagedObject, Serializable->MudObject->GettableObject->
    ManagedObject, Serializable->MudObject->Door->

As shown above and explained in Exercise 3 of the JavaOne 7400darkstar Lab, everything in the game is based on the MudObject class that implement the ManagedObject interface and is Serializable. This allows all game objects to be managed by the PDS server database services to give the objects persistence.
NOTE: Game objects are passed around using their ManagedReference, not as ManagedObjects.

The MudObject class (1a) defines a minimal interface that allows all objects to have a description, maintain an inventory, support aliases of its name, and parse command the look command. For items that the Player can pick up, put down, and carry around, the GettableObject class (1b) is provided to support the take and drop commands.

The command parsing is where a lot of the game magic takes place and understanding it will be our holy grail.


As per the Exercise, commands are the primary means of interaction between objects. A key point is that the action an object takes is based on how the object interprets a command, not by the command issuer doing something to it. So the target of the command has to parse the command and respond to it.
Processing a command take two phases: Parse and Commit.

Parsing the Command

Text commands from the user are parsed to generate a command object. Each possible target (i.e. everthing in the room) is
given a change has to (2a) parse the command. If the target (2b) recognizes the command as one that it supports, then it (2c)
set the command's type. Once the command type is set, the rest of the objects do not have to parse the command for its type.
If an object (2d) recognizes that it is the target of the command, it (2e) sets itself to handle the command. If an object is not the
target, then it can still be involved by (2f) registering itself as being interested in the command.

Commiting the Command

This two phase process first allows any object that is registered to veto the command and second, if not vetoed, notifies all
interested objects that the command is being commited and the target executes the commnad.


This section will work though the flow of commands used to move around and interact with doors and items.

The Go Command

The GO command is the primary naviagtion command used in the game. It allows the player to move in basic compass directions like north and east. It supports the text entries go and walk and converts either of them into a GO command. As with most commands in the game, these are supported with help descriptions.

Text sent from the Client is processed by the MudUser class object that was generated when the player logged in (see UnderstandingDarkMud01 for more details).

Command Process Flow: GO

MudUser.receivedMessage() is sent the raw text data for parsing. It strips off front and back white spaces from the text string and sends the remaning text to parse(). The overloaded parse() method is part of the MudObject class since all object have to be able to respond to commands. The first type converts the text string into a MudCommand object that records who initiated the command and saves the command text. (It is trimmed again here which seems redundant.) The second type of parse() is sent the command object.

This parse() sends the command to the MudUser's overridden parseInnerCommand(). This method first sends the command to the MudCharacter's overridden parseInnerCommand() to see if it processes go commands. Since it doesn't the Command.type stays as null. The MudUser then checks to see it it processes go commands. It doesn't either., but it does register an interest in the command since a successful GO may send the player to a different location.

Since the player doesn't process GO commands, the command is sent to the container of the player, usually a room, to see if it does.
NOTE: The player was placed into the room (container) by being added to the room's inventory when the player logged on.

Assuming that the Player is in a basic Room class room, its parseInnerCommand() method checkes to see if someone else handled the command. None have, but it only processes look commands. So again, the command type stays as null. The command is now sent to all other objects in the room to see if any of them can process the go command.

One of the objects in the room's inventory is a Door class object. The command is passed to the Door.parseOuterCommand() method for processing. It first sends the command to its inherited classes to see if the command is generic, then its checks for command specific to the Door class. The method sees that the command has not been handled and that the type is still null. It trys to parse the command text by pattern matching for go or walk text. Since it find go, it sets the command type to CommandType.GO and places the word after go into dirString and replaces the current command text with it for later processing. It also sets up an output for use should the command fail.

The now passes the command to Direction.toDirection() and to checks if the text has a valid direction (north, south, etc.) or an abreviation of a valid direction (n, s, etc.) and if so, sets the command as handled, generates the proper text output, and declares that it is interested in the outcome of the command. All that's left now is to actually move the player to the destination on the other side of the door. This is done by the MudObject.parse() calling MudObject.notifyInterestList() as its last step.

The MudObject.notifyInterestList() method first setup a List of everyone that register interest during the parsing operation.
The object.notifyCommand() method is used to test it conditions have been met to successfully use the command. (see Rope.notifyCommand() as an example) It would seem that his logic was added after the Locked Door code was designed because it uses a different method of test.
NOTE: Fix to use this logic instead of catching it in parseOuterCommand(). Maybe later.

The last process is to commit the command by calling the commitCommand() method for each object on the list.
The Door.commitCommand() adds the player to the Room indicated by the Door's destination property. The call to addToInventory() also removes the player from the previous Room's inventory. It also sets up an ArrivalTask to complete the process of displaying the Room description and notifying other players about the move.

AND that is how movement is done through the GO command. So how does a Door get Locked and Unlocked?

Looking a, the parseOuterCommand() has been overridden to test for the Door's state. Throw an exception if the Door state = locked and a GO command was issued. Simple enough, but this should be handled with the notify/commit logic outlined above. Maybe later.


The last question is then "How do you use a Key to change the state of the Door?".
Looking at, it looks like the lock/unlock logic was done before the notify/commit logic was designed in. So the Key.parseOuterCommand() method does the actual Door state change. This is not the proscribed way of having the target of the action do the state change, but it works for now.

Using the notify/commit logic the flow could be:
    The Door parses the UNLOCK command and registers interest and the failure string "Can't unlock this door with that."
    The Key (or other key like item) modifies the command to put the name of the Door the Key works on as the string for Command.UNLOCK.
    During the commit phase, the Door.commit tests the command to see if the second word mathces the Door's name of aliases and changes its state as needed.

This notify/commit logic seems to be the more flexible way to get interaction in the game.