[LIB/TUT] MondoCommand - Handle sub-commands with ease!

Discussion in 'Resources' started by Crast, Feb 18, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    Crast

    One of the most irking things about developing bukkit plugins is handling commands, especially when you want to do the more common approach these days of having a single outer command with multiple sub-actions nested inside it (and don't get me started on sub-sub-actions).

    You've probably tried it, and very soon entire function blocks start to look like a huge if-elseif-else scenario. Well worry not, there's a new library in town, and it's going to make the headache about dealing with sub-commands go away.

    MondoCommand:
    • Lets you register many sub-commands and let you separate the code how you like (multiple methods, classes, whatever) with minimal headache.
    • Enforces permissions on individual sub-commands
    • Generates a colorful help screen automatically
    • Handles gracefully player-only commands vs commands for consoles
    • Lets you do pretty formatted color output without having to bang your head against the ChatColor class.
    Resources
    Brief Intro Tutorial
    Note There is now an even better tutorial as part of the official documentation
    Code:java
    1. // Basic setup and registration
    2. MondoCommand base = new MondoCommand();
    3. base.autoRegisterFrom(this);
    4. getCommand("housebuilder").setExecutor(base);
    Yep, that's all there is to register the base command. MondoCommand is a CommandExecutor, nothing terribly fancy.

    Now you're going to write your first command:
    Code:java
    1. @Sub(description="Build a House", minArgs=2, usage="<owner> <name>")
    2. public void build(CallInfo call) {
    3. String owner = call.getArg(0);
    4. String name = call.getArg(1);
    5. if (houseMap.containsKey(name)) {
    6. call.reply("House with name %s already exists", name);
    7. } else {
    8. // TODO add code to actually make a house
    9. call.reply("House %s made!", name);
    10. }
    11. }

    Whoa whoa, what just happened there? Well, MondoCommand lets you register commands automatically from any class's attributes by decorating methods with @Sub. The method must take an argument of CallInfo (more on this later) and return void.

    Let's show another example command:
    Code:java
    1. @Sub(permission="housebuilder.destroy", description="Destroy a House",
    2. minArgs=1, usage="<name>", allowConsole=false)
    3. public void destroy(CallInfo Call) {
    4. // We don't need to check number of args, becaus we registered the
    5. // command with minArgs = 1.
    6. String name = call.getArg(0);
    7. if (houseMap.containsKey(name)) {
    8. houseMap.remove(name);
    9. call.reply("{GREEN}House {GOLD}%s{GREEN} removed", name);
    10. } else {
    11. call.reply("{RED}House %s not found", name);
    12. }
    13. }
    This shows off a few more of MondoCommand's features, like the ability to control whether individual commands can be used at the console or not, and to gate individual sub-commands with permissions. Also, automatic color interpolation, which we'll explain more soon.

    Besides handling the sub-command registration, you also get auto-generated help screens that look like this:
    [​IMG]

    On top of all that, MondoCommand wraps much of the ugliness of command handling into much easier convenience accessors:
    • call.getPlayer() gets a Player object (no more casting from CommandSender) and commands can be registered as allowing console or player-only.
    • call.reply(template, [...]) - This is the gem of MondoCommand, it will send a message back to the user that interprets color codes embedded in the string, and lets you also interpolate variables into the string without having to do string concatenation. Ever write something like:
      Code:java
      1. player.sendMessage(ChatColor.BLUE.toString() + "Added user "
      2. + ChatColor.RED.toString() + targetPlayer.getName()
      3. + ChatColor.BLUE.toString() + "with role"
      4. + ChatColor.GREEN.toString() + role);
      Well with MondoCommand, that looks like:
      Code:java
      1. call.reply("{BLUE}Added user {RED}%s {BLUE}with role {GREEN}%s",
      2. targetPlayer.getName(), role);
      You can put any of the Bukkit color codes in braces and also take advantage of string formatting as provided by String.format to let you smartly interpolate variables.
    • call.getArg(index) - To get a single argument, where the 0th index is the first index which comes after the sub command name (no more argument math!) and furthermore, you don't need to check the length of the args if you registered the subcommand with setMinArgs(), it will show the player a usage message and stop them from running your command.
    • call.getIntArg(index) - Convenient way to get an argument coerced into an integer.
    • call.getJoinedArgsAfter(index) - If you need to get a bunch of arguments after a certain index (like say you're accepting a text entry or chat message) this convenience method does that for you.
    • call.numArgs() - Show number of arguments.

    Do it with static registration (advanced mode)

    If you don't want to use dynamic command registration with @Sub, don't worry, there's an API for you which uses no reflection and is completely predictable and type-safe. Actually, dynamic @Sub registration is implemented by building a bunch of anonymous handler classes for the API, so it works out roughly the same.

    This tutorial section has now moved into the official documentation
     
    bobacadodl, evilmidget38 and ohtwo like this.
  2. Offline

    ohtwo

    Wowwww this is really cool.
     
  3. Offline

    Crast

    Glad you dig it. Have you had a chance to use it? any comments?

    By the way, I just released version 0.2, and the API has stabilized somewhat, I don't anticipate breaking it for the moment (but if you follow a -SNAPSHOT release, you never know :p)
     
  4. Offline

    ohtwo

    No, not yet! Haven't had the chance/time to build a plugin with this. I'll let you know when I do!
     
  5. Offline

    evilmidget38

    This is a very sexy command system. I foresee myself using it.
     
    ohtwo likes this.
  6. Offline

    Crast

    I have released MondoCommand 0.3, and with it comes even cooler dynamic registration via annotations (optional, you can still register sub-commands manually too). It's also now on DevBukkit, though my recommendation is to use maven so you can shade the code easily.
     
  7. Offline

    surtic

    For a small Plugin i use ure Command Lib and it works well.

    Thanks for your work.
     
  8. Offline

    DJ8X

    I had a problem... I can't find the getCommand() method that you used in the example... Where do I get that?
     
  9. Offline

    xTrollxDudex

    DJ8X
    You register your command in the main class... Otherwise, use plugin.getCommand(...)...
     
  10. Offline

    chrisman0091

    You know how I make subcommands? I use a crap ton of nested if statements. But you know what? This is so much nicer. Thanks OP, bookmarking this.
     
  11. Offline

    Crast

    bobacadodl likes this.
Thread Status:
Not open for further replies.

Share This Page