Skip to content
Ranzdo edited this page Dec 6, 2012 · 14 revisions

This is a library that makes it easier and more structured to write bukkit commands.

Why?

  • It also reduces the amount of argument validation code you have to write, so you only need to write the code that the command actually does.
  • It implements an easy to use flag system.
  • It makes it easier to code sub commands (/root sub)
  • It makes it easy to code help messages.

Sounds cool, but how do I use it?

Using the CommandHandler class we can access the command register method. Then we register a simple command and with the help of annotations.

private CommandHandler handler;
public void onEnable() {
    handler = new CommandHandler(this);
    handler.registerCommands(this);
}

@Command(
identifier="test", //The identifier is the way the command will be accessed ingame, this command will be accessed with /test
description="This is a test command", //Writing the description of the command here makes it easier to generate help messages.
onlyPlayers = true, //If the command can be executed by a player or not.
permission = {"test.test", "test.admin"} //What permission nodes the player must have to execute this command
)
public void sendSelf(
    Player sender, //The first parameter is the CommandSender, but since we know that this command can only be executed by players we can set this a Player instead.
    @Arg(name="one_string") String test //The first command argument that we want defined by the player.
) {
    sender.sendMessage(test);
}
  • Usage for this command will be: /test [one_string]
  • NOTE: The identifier needs to be defined it the plugin.yml along with its aliases.

This simple command is ready to use, however, this could be easily be done with the normal command system too, so we move on to the goodies.


You can also use primitive variables and some bukkit objects (that can be defined with a string) instead of a normal string.

@Command(identifier="send", description="Sends a message to a player")
public void sendMessageToPlayerCommand(
    Player sender,
    @Arg(name="send_to") Player player,
    @Wildcard @Arg(name="message") String message
){
    player.sendMessage(message);
}
  • Usage for this command will be: /send [send_to] [message]
  • The player argument will try to parse a player out of the supplied string with the help of the Bukkit.getPlayer() method. If the player is not online, it will send a user friendly error message to the command sender and never execute the method.
  • The @Wildcard annotation on the "message" parameter will catch every single argument beyond that argument into one string.

This command simply sends a message to another player, but with the standard bukkit command system you would be needed to do alot more in the method body (verify that there was enough arguments, checking if the player exists, join the rest of the arguments etc).


You can also use verifiers for static input validations.

@Command(identifier="spawn", description="Spawns an entity")
public void spawnEntityCommand(
   Player sender, 
   @Arg(name="entity") EntityType type,
   @Arg(name="amount", verifiers="min[1]|max[10]") int amount //We add some verifiers here.
) {
   Location location = sender.getTargetBlock(null, 100).getLocation();
   for(int i= 0;i < amount;i++) {
      location.getWorld().spawnCreature(location, type);
   }
}
  • Usage for this command will be: /spawn [entity] [amount]
  • The EntityType parameter will be transformed with the help of the EntityType.fromName() or EntityType.fromId() method. If it falis it will send a user friendly error message to the command sender and never execute the method.
  • The amount parameter will be parsed with the help of the Integer.parseInt() method. If it is not possible (aka it is not a number) or it will send a user friendly error message to the command sender and never execute the method.
  • After the parse of the amount parameter it will go though the verifiers. "max" and "min" is two verifier methods that is available if you use an integer. If the defined integer is less than 1 or greater than 10 it will return a user friendly error message to the command sender and never execute the method.

Flags

Flags can make commands do optional things. For example, a command that kills a player can look like this.

@Command(identifier="kill", description="Kills the target player")
public void killPlayer(
   Player sender, 
   @Arg(name="target") Player target
) {
target.setHealth(0);
}

However, we would want to make the option to have a delay or spawning an zombie when the player get killed. We can implement this easy with the help of flags.

@Command(identifier="kill", description="Kills the target player")
@Flags(identifier = {"d", "z"}, description = {"Adds a delay", "Spawns a zombie upon death"})
public void killPlayer(
   Player sender, 
   @Arg(name="target") final Player target,
   @FlagArg("d") @Arg(name="time", def="0") int delay,
   @FlagArg("z") final boolean zombie
) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
   public void run() {
       target.setHealth(0);
       if(zombie) {
           target.getWorld().spawnEntity(target.getLocation(), EntityType.ZOMBIE);
       }   
   }
}, delay*20);
}
  • Usage for this command will be: /kill [target] (-d [delay]) (-z)
  • If the player executes this command and only supplying the target parameter, the target will be killed immediately since the default value for the flag -d is 0 and since -z does not exist it will be false. However if we would add "-d 4 -z" to the command it would be a 4 sec delay and zombie would be spawned.

Default argument

You can define default arguments so if the command executor does not define that argument it will use the default one. We can go back to the command we did to explain how verifiers worked and add if a amount is not defined, it will default to 1.

@Command(identifier="spawn", description="Spawns an entity")
public void spawnEntityCommand(
   Player sender, 
   @Arg(name="entity") EntityType type,
   @Arg(name="amount", verifiers="min[1]|max[10]" def="1") int amount //The default will be 1 here
) {
   Location location = sender.getTargetBlock(null, 100).getLocation();
   for(int i= 0;i < amount;i++) {
      location.getWorld().spawnCreature(location, type);
   }
}

NOTE: The default argument will go though the parsing like every other argument, so it has to be a valid one that can work with your verifies and argument parse.

But how do we do a default variable that can be dynamic? For example, I maybe want my player argument to default to the sender if the sender does not supply that argument. Then you can use ArgumentVariables. We can go back to the command we used to kill a player and add so if the sender does not supply a player it will kill himself.

@Command(identifier="kill", description="Kills the target player")
public void killPlayer(
   Player sender, 
   @Arg(name="target" def="?sender") Player target //We add ?sender to say that we want the sender of the argument to be default.
) {
target.setHealth(0);
}

In this command the sender would be equal to the player argument if a player name was not defined by the command sender.

All ArgumentVaribles is prefixed with ? and if you want for some reason the default to be ?sender natural you can escape a ArgumentVariable with a backslash "" so it will be for example def="\?sender" (adding that extra backslash to escape the backslash in java).

The ?sender also works with the World argument handler, where it will take the command senders current world as argument if not defined.

Default arguments for flags works the same, but since the flag arguments is required when the command sender decides to use one the default values are only used when the flag is not used. An example is the command used to describe the flag system above, where the [time] argument will be 0 if the flag is not defined.

Subcommands

To define a subcommand just add it to the identifier="root sub" and then the command can be accessed through /root sub . You can have unlimited levels of subcommands so identifier="root sub1 sub2 sub3" works too.

Be careful though, since if the root command has arguments that can potentially collide with a subcommand since it will priority the subcommand, making the user unable to execute the root command with the desired colliding argument.

TO BE CONTINUED