[Tutorial] Sub-Commands in Different Classes

Discussion in 'Resources' started by AoH_Ruthless, Dec 7, 2013.

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

    AoH_Ruthless

    I haven't seen a tutorial on this topic, so I've decided to make one. Making sub-commands in different classes will improve readability of your code and increase efficiency, but because it is not of the most importance, there is no working tutorial on this topic.

    Step 1: Making an interface for your onCommand Method.
    We are going to make a public method that we can call later to execute commands.

    Show Spoiler
    Code:java
    1. //These are the two imports I used, but you can change the actual onCommand to whatever suits your needs.
    2. import org.bukkit.command.CommandSender;
    3. import org.bukkit.command.Command;
    4.  
    5. //IMPORTANT: This is an interface, not a class.
    6. public interface CommandInterface {
    7.  
    8. //Every time I make a command, I will use this same method.
    9. public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args);
    10.  
    11. }


    Step 2: Making your Command Handler.
    Now, I recommend making a separate class for this. So, you will want to make a class with the title CommandHandler.

    Show Spoiler
    Code:java
    1. //Imports we will need.
    2. import org.bukkit.ChatColor;
    3. import org.bukkit.command.Command;
    4. import org.bukkit.command.CommandExecutor;
    5. import org.bukkit.command.CommandSender;
    6. import org.bukkit.entity.Player;
    7.  
    8. import java.util.HashMap;
    9.  
    10. //The class will implement CommandExecutor.
    11. public class CommandHandler implements CommandExecutor
    12. {
    13.  
    14. //This is where we will store the commands
    15. private static HashMap<String, CommandInterface> commands = new HashMap<String, CommandInterface>();
    16.  
    17. //Register method. When we register commands in our onEnable() we will use this.
    18. public void register(String name, CommandInterface cmd) {
    19.  
    20. //When we register the command, this is what actually will put the command in the hashmap.
    21. commands.put(name, cmd);
    22. }
    23.  
    24. //This will be used to check if a string exists or not.
    25. public boolean exists(String name) {
    26.  
    27. //To actually check if the string exists, we will return the hashmap
    28. return commands.containsKey(name);
    29. }
    30.  
    31. //Getter method for the Executor.
    32. public CommandInterface getExecutor(String name) {
    33.  
    34. //Returns a command in the hashmap of the same name.
    35. return commands.get(name);
    36. }
    37.  
    38. //This will be a template. All commands will have this in common.
    39. @Override
    40. public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
    41.  
    42. //For example, in each command, it will check if the sender is a player and if not, send an error message.
    43. if(sender instanceof Player) {
    44.  
    45. //If there aren't any arguments, what is the command name going to be? For this example, we are going to call it /example.
    46. //This means that all commands will have the base of /example.
    47. if(args.length == 0) {
    48. getExecutor("example").onCommand(sender, cmd, commandLabel, args);
    49. return true;
    50. }
    51.  
    52. //What if there are arguments in the command? Such as /example args
    53. if(args.length > 0) {
    54.  
    55. //If that argument exists in our registration in the onEnable();
    56. if(exists(args[0])){
    57.  
    58. //Get The executor with the name of args[0]. With our example, the name of the executor will be args because in
    59. //the command /example args, args is our args[0].
    60. getExecutor(args[0]).onCommand(sender, cmd, commandLabel, args);
    61. return true;
    62. } else {
    63.  
    64. //We want to send a message to the sender if the command doesn't exist.
    65. sender.sendMessage("This command doesn't exist!");
    66. return true;
    67. }
    68. }
    69. } else {
    70. sender.sendMessage(ChatColor.RED + "You must be a player to use this command.");
    71. return true;
    72. }
    73. return false;
    74. }
    75.  
    76. }
    77.  


    Step 3: Making the class for the base command.
    Before we can make classes for each subcommand, we have to make the class containing the base command.

    Show Spoiler
    Code:java
    1. //Imports for the base command class.
    2. import org.bukkit.command.Command;
    3. import org.bukkit.command.CommandSender;
    4. import org.bukkit.entity.Player;
    5.  
    6. //This class implements the Command Interface.
    7. public class ExampleCmd implements CommandInterface
    8. {
    9.  
    10. //The command should be automatically created.
    11. @Override
    12. public boolean onCommand(CommandSender sender, Command cmd,
    13. String commandLabel, String[] args) {
    14.  
    15. //Because in our CommandHandler we are already checking if the sender's instance is a player, we don't have to do it here.
    16. Player p = (Player) sender;
    17. //For the purpose of this tutorial I am just sending the player a message.
    18. p.sendMessage("Example!");
    19. return false;
    20. }
    21.  
    22. }


    Step 4: Making the Sub-Command Classes.
    Now we can begin to make sub-commands.

    Show Spoiler
    Code:java
    1. //Imports Needed.
    2. import org.bukkit.command.Command;
    3. import org.bukkit.command.CommandSender;
    4. import org.bukkit.entity.Player;
    5.  
    6. //ArgsCmd also implements CommandInterface
    7. public class ArgsCmd implements CommandInterface
    8. {
    9.  
    10. @Override
    11. public boolean onCommand(CommandSender sender, Command cmd,
    12. String commandLabel, String[] args) {
    13. Player p = (Player) sender;
    14.  
    15. //We don't have to check if the args length is equal to one, but you will have to check if it is greater than 1.
    16. if(args.length > 1) return false;
    17.  
    18. p.sendMessage("example args!");
    19. return false;
    20. }
    21.  
    22. }
    23.  


    Step 5: Registering Commands in your onEnable().
    This is extremely important! If you do not register commands properly they WILL NOT work!

    In your main class:

    Show Spoiler
    Code:java
    1. package io.GitHub.AoHRuthless;
    2.  
    3. //We need to import the CommandHandler class
    4. import io.GitHub.AoHRuthless.CommandHandler;
    5.  
    6. import org.bukkit.plugin.java.JavaPlugin;
    7.  
    8. public class Main extends JavaPlugin {
    9.  
    10. //Because there may be LOTS of commands, we are going to make a separate method to keep
    11. //the onEnable() nice and clean.
    12. public void registerCommands() {
    13.  
    14. //For convenience' sake, we will initialize a variable.
    15. CommandHandler handler = new CommandHandler();
    16.  
    17. //Registers the command /example which has no arguments.
    18. handler.register("example", new ExampleCmd());
    19.  
    20. //Registers the command /example args based on args[0] (args)
    21. handler.register("args", new ArgsCmd());
    22. getCommand("example").setExecutor(handler);
    23. }
    24.  
    25. //Our onEnable() without taking into account any listeners you may have.
    26. public void onEnable() {
    27. this.registerCommands();
    28. }
    29.  
    30. }



    Make sure the base command (/example) is registered in your plugin.yml.
    I hope this helps any of you! Again, all this tutorial does is help improve your code readability so you don't have dozens of if/else statements in one class, as well as improves efficiency when adding / removing features for your plugins.
     
    iiHeroo likes this.
  2. Offline

    kamcio96

    You can rewrite args to remove subcommand arg, remove cmd and label in CommandInterface, and small sugestion: handler.registerPlayerCommand(...);
     
  3. Offline

    AoH_Ruthless

    kamcio96
    Yeah, among that I also came to the conclusion I can also trim the arguments from the Command handler. When I have some more free time on my hands I will edit the post :)

    But yeah I agree, I never really used those 3 things, I always just write my commands that way.
     
  4. Offline

    kamcio96

    And you can use CommandExecutor to subcommand, so you can change is config yours commands. Eg:
    /game start
    /game join
    ...
    When you change config you can register:
    /start
    /join
    ...
     
  5. Offline

    MaciekP42

    And a small question. Can i add arguments to subcommands? For example /example kill [player]?
     
  6. Offline

    AoH_Ruthless

    MaciekP42
    With this command structure, if you had the command /example kill player.

    You would have one class for the subcommand "kill". In that onCommand method, you would need to check if there is a given player argument.

    So basically:

    Code:java
    1. // going to assume you wrote the method parameters correctly
    2.  
    3. // Since you are looking for a player, you will need to define the player
    4. Player target = null;
    5.  
    6. // your [player] argument is still the second argument. So you would need to check if args.length == 2.
    7. if (args.length == 2) {
    8. target = Bukkit.getPlayer(args[1]);
    9. // Now you need to check if target is null again.
    10. if (target == null) {
    11. // Tell player the target doesn't exist
    12. } else {
    13. // Target exists, run your code
    14. }
    15. }
    16. return false;
    17. }


    When you register the command, you register the argument "kill". You don't need to register [player]

    However, if you had the command /kill <player>, this set command structure wouldn't work because you would need to register every single one, which is a pretty big hassle.
     
Thread Status:
Not open for further replies.

Share This Page