Skip to content

Commands

tl24 edited this page Mar 8, 2016 · 1 revision

Command Creation

Introduction

In most OO muds I've seen, player commands are implemented with classes. Seems like a lot of typing to me, specifying arguments, parsing them, etc. In Mirage, you can create commands by simply creating methods in a class. Much like the function pointers you'd see on a mud written in C, but better.

Details

Here's a sample command:

[Command(Description="Attempt to kill another player or mobile")]
public string kill([Actor] Player self, 
                   [Lookup("/Players")] Player target)
{
    return "You started a fight with " + target.Title + ".\r\n";
}

That's all it takes. Granted this command doesn't do a whole lot, but it does illustrate a few things. Let's examine this.

Command Attribute

The Command attribute lets the mud know that this is a player command. It can specify a few other things such as other command aliases, priority, minimum level required, security roles, etc. The mud will automatically include any method with a command attribute as an available command, no need to specify it in any config file.

Arguments

You specify arguments as you normally would do for a method. Normally you are working with strings, but you can also specify other primitive types. The Command Invoker will attempt to convert the player's arguments to that type before calling the method. You can also specify complex types such as a Player class. These arguments need special attributes to let the mud know how to convert the player's string argument into a Player object. Two examples are shown here.

The "Actor" attribute indicates that this argument should be the player that invoked the command. It can decorate any instance of the Living class, the base class for players and mobiles (Mobiles can execute commands too!). The Actor argument can be at any position in the list or not there at all if you don't need it. This argument is supplied by the mud and does not come from the player arguments.

The "Lookup" attribute looks up the argument within the mud using the QueryManager. In this case it tries to find a player by the name the invoker specified.

There is also another attribute, the "CustomParse" attribute. This indicates that the command will do its own argument parsing and the unparsed arguments should be passed to this parameter as a string. You can also have the interpreter parse some of the arguments for you. The "CustomParse" attribute should be on the last argument in the list. For example, the say command takes the remaining text on the input as the text to speak:

[Command(Aliases=new string[]{"'", "say"})]
public static Message say([Actor] Living actor, [CustomParse] string message)
{
   //speak to all others in the room
   ResourceMessage msgToOthers = new ResourceMessage(MessageType.Communication, Namespaces.Communication, "say.others");
   msgToOthers.Parameters["player"] = actor.Title;
   msgToOthers.Parameters["message"] = message;
   foreach (Living am in actor.Container.Contents(typeof(Living)))
   {
      if (am != actor)
      {
         am.Write(msgToOthers);
      }
   }

   //repeat message to yourself as confirmation
   ResourceMessage msgToSelf = new ResourceMessage(MessageType.Confirmation, Namespaces.Communication, "say.self");
   msgToSelf.Parameters["message"] = message;
   return msgToSelf;
}

Return Value

Any return value is sent to the invoker of the command. It can be a Message, string, or any other object. Anything that is not a Message object will have ToString called on it and then be converted to a StringMessage before sending to the player. You're also free to write to the actor using the Write method and return nothing as well.

Command Resolution and Overloading

You are free to use method overloading to specify different versions of the command with different arguments. You can also specify aliases within the Command attribute for the method. If you specify any aliases then the name of the method can no longer be used to invoke the command. You will need to include it in the alias list if you want this to happen. The Command interpreter allows the players to enter partial command names to invoke commands.

With partial aliases and method overloading, this means that any command invocation could have multiple commands that match. At this point the player arguments will be used to determine the correct command. First security and level is taken in to account. Any commands that the player is not privileged to invoke are removed. Second commands are weeded out based on number of arguments supplied vs. number of arguments expected. Then an attempt is made to convert arguments to compatible parameters for the remaining commands. Lastly if multiple commands are still available, priority (can be specified in the Command attribute) is used to sort the remaining commands. Then the first remaining command is invoked and output if any sent back to the player.

Command Class

Behind the scenes commands are still implemented with Classes. An instance of the [http://miragemud.googlecode.com/svn/MirageMUD/trunk/MirageMUD/Core/Command/ICommand.cs ICommand] interface stores all the necessary information to look up and invoke a command. Specifying the Command attribute on a method causes an instance of [http://miragemud.googlecode.com/svn/MirageMUD/trunk/MirageMUD/Core/Command/ReflectedCommand.cs ReflectedCommand] to be created.