Resource Since everone is posting their CommandAPI -> Here is mine

Discussion in 'Spigot Plugin Development' started by Syranda, Apr 30, 2017.

  1. BetterCommands - A simple command API

    I've been programming this command API for a while now so I thought I'd share it with you
    I don't want to talk long so here is an example command:

    Code (Text):

    package de.syranda.bettercommands.examples;

    import org.bukkit.Bukkit;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;

    import de.syranda.bettercommands.customclasses.SubCommand;
    import de.syranda.bettercommands.customclasses.adc.Array;
    import de.syranda.bettercommands.customclasses.adc.Command;
    import de.syranda.bettercommands.customclasses.adc.Conversation;
    import de.syranda.bettercommands.customclasses.adc.Help;
    import de.syranda.bettercommands.customclasses.adc.Optional;

    /*
     * Here's a short feature list of what this API does:
     *
     *  # Syntax check (length)
     *  # CommandSender check (either player or console)
     *  # Argument validation
     *    # Default validators:
     *      -> Boolean  (Wrapper class | not native 'boolean')
     *  -> Double  (Wrapper class | not native 'double')
     *  -> Integer  (Wrapper class | not native 'int')
     *  -> Material
     *  -> OfflinePlayer
     *  -> Player
     *  -> Plugin
     *  -> World
     *  # You can add your own validated arguments
     *  # Automatic and dynamic 'help' command
     *    # You can exclude sub commands from being shown in the 'help' command (Default: false)
     *    # You can exclude sub commands from being shown in the 'help' command if the command sender hasn't got the permission to execute the command (Default: false)
     *    # You can decide if you want the command's permission to be shown
     *  # Conversation commands (Default: false)
     *  # Does reask your argument if that what you typed argument in is not valid (e.g. the player is not online or the material doesn't exist)
     *  # Permission check (Default: true)
     *    # <plugin>.<root permission>.<sub command permission> (e.g. bettercommands.example.help)
     *    # All commands have by default a permission set
     *  # Annotation driven and object driven
     *  # Tab completer (for static AND dynamic arguments)
     *  # e.g static '/example he' completes to '/example help'
     *  # e.g dynamic (Syntax: /example path <material>) '/example path STO' completes to '/example path STONE'
     *  # Tab completer is diabled if the CommandSender doesn't have the required permission to execute the command (Looks fancy)
     *  # The following default parameters have tab completers:
     *  -> Material  (List with all materials)
     *  -> OfflinePlayer (List with all player)
     *  -> Player  (List with all online players)
     *  -> World  (List with all worlds)
     *  # Explicit permissions
     *  # You can prevent users with wildcard permissions (e.g. 'bettercommands.example.*') to execute commands like '/example help' if
     *  they don't have the command's explicit permission (in this case: 'bettercommands.example.help')
     *  # Different translators for error and normal messages
     *    # You can disable appending the command's prefix on error messages (Looks cleaner; IMO)
     *  # The API scans your plugin for commands (You cannot 'forget' to register your commands)
     *  # Inline commands (e.g. /pex <user> add <permission> | <user> = inline argument)
     *    # Path syntax: path.%.path (% = inline argument)
     *  
     */  

    /*
     * value  = command name
     * aliases  = aliases (self explaining)
     * registerHelp  = whether a help command should be registered  (Default: false)
     * helpPermission = whether the help command should have a permission set  (Default: true)
     * prefix  = the prefix appended in front of messages
     *
     */
    @Command(value = "example", aliases = {"exa"}, registerHelp = true, helpPermission = false, prefix = ":sn[:hExample:sn] ")
    public class ExampleCommand {
       
       /*
        * The default help command is always registered under /<command> help (e.g. '/example help')     *
        * The help command Shows then all sub commands in this pattern:
        *
        * <syntax> - <help message> (<permission>)
        *
        *  e.g. /example help - Help command
        *  
        *  If no help message is available you'll get a default message
        *  
        *  Notes: 1. The help message will always be shown when the API cannot associate a syntax to a sub command
        *  2. If the help command is not registered and more than one sub command is registered and the API cannot associate a syntax to a sub command nothing will happen (Doesn't look good)
        *
        */
       
       /*
        * To tell the API it should scan your plugin just call the 'BetterCommand#scan(Plugin plugin)' method in your 'onEnable' method
        * To register CommandParameters call 'ParameterRegistry#getRegistry()#put(Class<?> clazz, CommandParameter<?> commandParameter)
        *      -> e.g. ParameterRegistry.getRegistry().put(Plugin.class, PluginParameter.class);
        */
       
       /**
        * The value of the 'Help' annotation tells the API what help message should be shown when the user types in '/example help'
        *
        * @param player  With the 1st argument being a 'Player' the API does an atomatic check, if the CommandSender is actually a player
        * @param recipient  'recipient' is the 1st actual argument of the command (see syntax). The API does automaticly check if the player is online
        * @param message  The 'Array' Annotation tells the API that this argument is not a single word but all typen arguments starting with the (in this case)
        *  3rd are taken into 'message'
        */
       @Help("Command to send messages") // This is the help message shown in the help command
       @Command(path = "path.nextpath", appendPrefixOnError = false /* We don't want the prefix on error messages */, showPermission = false /* Way too long in this case */) // -> Command: /example anotherpath <recipient> <message...> | Permission: bettercommands.example.anotherpath
       public void send(/* 1. */ Player player , /* 2. */ Player recipient, /* 3. */ @Array String message) {
         
         /*
          *  /example path nextpath Syranda You are a cool guy
          *  -> Valid command line
          *  recipient = Syranda (My IGN)
          *  message  = You are a cool guy
          *  
          *  If the command sender is not a player (in this case) you'll get an error message as console
          *  
          *  Too many (Not possible for this example) or too few arguments will result in a default error message
          *  
          *  e.g. /command path nextpath Syranda
          *  -> Not valid command line. Too few arguments.
          *  -> Results in message: (Red)Usage: /example path nextpath <recipient> <message...>
          *  
          *  If the player is not online
          *  
          */
         
         SubCommand subCommand = SubCommand.getSubCommand(getClass()); // Get the SubCommand instance of this sub command
         
         /*
          * The 'SubCommand' class has a special #sendMessage method
          * This makes color changes way easier
          *
          * This method translated the following colors:
          *
          * :n  -> normal  (Default: Grey)
          * :sn -> sub normal  (Default: Grey)
          * :h  -> highlight  (Default: Gold)
          * :sh -> sub highlight  (Default: Gold)
          * :s  -> success  (Default: Green)
          * :f  -> failed  (Default: Red)
          * :m  -> minor  (Default: Dark Grey)
          *
          * You can change the default colors with a new BetterCommandsConfig (Look at 'ExampleConfig')
          * All registered commands will be automaticly associated with your plugin and all messages / colours will adapt to your config
          *
          * Componets:
          *
          * {text;Hover text|SUGGEST_COMMAND\\:/command} -> Component with: Text = 'text' | Hover text: 'Hover text' | Action: 'SUGGEST_COMMAND' (value='/command')
          * {text|SUGGEST_COMMAND\\:/command}  -> Component with: Text = 'text' | Hover text: none | Action: 'SUGGEST_COMMAND' (value='/command')
          * {text\\:Hover text}  -> Component with: Text = 'text' | Hover text: 'Hover text' | Action: none
          *
          * Example:
          *
          * :nThis is a cool {:htext;:hWhy do you hover on me?|RUN_COMMAND\\:/stop}
          * -> (Grey)This is a cool (Gold)text
          * -> Hover on 'text': (Gold)Why do you hover on me?
          * -> Click on 'text': Executes command '/stop'
          *
          */
         
         if(player == recipient) { // You cannot send yourself messages...
           subCommand.sendErrorMessage(player, ":fYou cannot send yourself a message");
           return;
         }
         
         subCommand.sendMessage(recipient, ":nYou got a message from :h" + player.getName() + ":n:");
         subCommand.sendMessage(recipient, ":nMessage: :sn" + message);
         subCommand.sendMessage(recipient, ":nClick {:hhere;:hClick me to reply|SUGGEST_COMMAND\\:/example path nextpath " + player.getName() + " } :nto reply");
         subCommand.sendMessage(player, ":sMessage sent.");    
         
       }
       
       /**
        * @param sender In this case console can execute this command too
        */
       @Command(path = "hidden", showInHelp = false, permission = "custom.permission" /* You can change the permission */)
       public void hiddenCommand(CommandSender sender /* 1. */) {
         
         SubCommand subCommand = SubCommand.getSubCommand(getClass()); // Get the SubCommand instance of this sub command
         
         subCommand.sendMessage(sender, ":hWow, this was super hidden");
         
       }
       
       
       /**
        * The 'Conversation' Annotation turns this command into a conversation.
        * This means that the command will ask separately for each argument (except for inline arguments)
        *
        * 'showOnPermission = true'  results in the command only be shown if the CommandSender has the required permission
        * 'requiresExplicitPermission = true'  states that the CommandSender requires the exact permission (Wildcardpermissions like 'bettercommands.*' won't be accepted)
        *
        * Syntax: /example <recipient> admin
        *
        * @param sender  Only shows in help when the console sender has the reuqired permission
        * @param recipient  Requires the explicit permission ('bettercommands.example.%.admin'); Doesn't grant access if the command sender has a wildcard permission like 'bettercommands.*'
        * @param subject  The 'Array' annotation is not required here since this is a conversation command
        * @param message  The 'Array' annotation is not required here since this is a conversation command
        */
       @Conversation
       @Help("Command to send admin emails")
       @Command(path = "%.admin", showOnPermission = true, requiresExplicitPermission = true)
       public void adminCommand(CommandSender sender, Player recipient, String subject, String message) {
               
         SubCommand subCommand = SubCommand.getSubCommand(getClass()); // Get the SubCommand instance of this sub command
         
         if(sender == recipient) {
           subCommand.sendErrorMessage(sender, ":fYou cannot send yourself an email");
           return;
         }
         
         subCommand.sendMessage(recipient, ":nYou got an :hadmin email:n:");
         subCommand.sendMessage(recipient, ":nSubject: :h" + subject);
         subCommand.sendMessage(sender, ":sAdmin email sent.");
         subCommand.sendMessage(recipient, ":nMessage: :h" + message);
         
       }
       
       @Command("telleverybody") // You can also register completly other commands | If you don't pass a path the sub command will be executed on /<command> (eg. /telleverybody <message...>)
       public void anotherCommand(CommandSender sender, @Array String message) {
         Bukkit.broadcastMessage(sender.getName() + " has a message for you: " + message);
       }
       
       @Command(path = "optional")
       public void optional(CommandSender sender, String name, @Optional Integer age /* Optional arguments are also a thing */) {
         if(age == null)
           sender.sendMessage(name + " is cool");
         else
           sender.sendMessage(name + " is cool and " + age + " years old");
       }
       
       @Command(path = "optionaltwo")
       public void optionalTwo(CommandSender sender, String name, @Optional("3.14") Double pi /* With default values */) {
         sender.sendMessage(name + " is cool and pi = " + pi);
       }
       
    }

     
    But why should you use it?

    BetterCommands offers many unique aspects such as the way the color setup works,conversation commands or a automatic help command. Also this API is highly customizable: every message can be edited via your own config class.

    Suggestions are very appreciated.
    Link to Spigot resource page
    GitLab
     
    #1 Syranda, Apr 30, 2017
    Last edited: May 2, 2017
    • Like Like x 3
  2. There is an useful tag for Javadocs called <param> to describe a parameter. Use it!
    About the API, it's nice well done!
     
  3. You're right. I'll overwork it when I get to it :)
     
  4. Updated example command and post