[TUT] Using sk89q's command framework

Discussion in 'Resources' started by molenzwiebel, Oct 20, 2013.

  1. When looking through GitHub some time ago, I stumbled upon @sk89q's command framework. I have used it in a lot of my plugins now and I can say that it is a awesome system. Here is how to use it!

    Step 1: Installing the framework
    Option 1: Depending on WorldEdit. This is probably the easiest way to include the framework, but the downside is that the server owner needs WorldEdit installed.
    Option 2: Copy all the files from OvercastNetwork/sk89q-command-framework to your project. A way to do it, but not recommended (by me).
    Option 3: Add it as a maven dependency and shade it:
    pom.xml (open)

    Code (Text):
    1.  
    2. //Add the oc.tc (Overcast Network) repo
    3.  
    4. <repository>
    5.     <id>repo.oc.tc</id>
    6.     <url>http://repo.oc.tc/content/repositories/releases/</url>
    7. </repository>
    8.  
    9.  
    10. //Add the dependency
    11.  
    12. <dependency>
    13.     <groupId>com.sk89q</groupId>
    14.     <artifactId>sk89q-command-framework</artifactId>
    15.     <version>0.5</version>
    16. </dependency>
    17.  
    18.  


    Step 2: Adding the onCommand handler
    Start by defining a variable in your main class:
    Code:java
    1. private CommandsManager<CommandSender> commands;

    After that, add this method to initiate your manager
    Code:java
    1. private void setupCommands() {
    2. this.commands = new CommandsManager<CommandSender>() {
    3. @Override
    4. public boolean hasPermission(CommandSender sender, String perm) {
    5. return sender instanceof ConsoleCommandSender || sender.hasPermission(perm);
    6. }
    7. };
    8. CommandsManagerRegistration cmdRegister = new CommandsManagerRegistration(this, this.commands);
    9. //Register your commands here
    10. }

    Almost done! Now you need one more thing. The onCommand:
    Code:java
    1. @Override
    2. public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
    3. try {
    4. this.commands.execute(cmd.getName(), args, sender, sender);
    5. } catch (CommandPermissionsException e) {
    6. sender.sendMessage(ChatColor.RED + "You don't have permission.");
    7. } catch (MissingNestedCommandException e) {
    8. sender.sendMessage(ChatColor.RED + e.getUsage());
    9. } catch (CommandUsageException e) {
    10. sender.sendMessage(ChatColor.RED + e.getMessage());
    11. sender.sendMessage(ChatColor.RED + e.getUsage());
    12. } catch (WrappedCommandException e) {
    13. if (e.getCause() instanceof NumberFormatException) {
    14. sender.sendMessage(ChatColor.RED + "Number expected, string received instead.");
    15. } else {
    16. sender.sendMessage(ChatColor.RED + "An error has occurred. See console.");
    17. e.printStackTrace();
    18. }
    19. } catch (CommandException e) {
    20. sender.sendMessage(ChatColor.RED + e.getMessage());
    21. }
    22.  
    23. return true;
    24. }

    That's all the code you'll need in your main class, lets go on and make some commands!

    Step 3: The Commands
    First, create a new class. You can name it whatever you want.
    The Command Structure
    Every command method has a @Command annotation, which defines certain aspects of the command. Have a look at this example:
    Code (Text):
    1. @Command(aliases = { "hello", "hey" }, desc = "Says hello", usage = "[player] - The player to say hello to", min = 1, max = 1)
    This will create a command that can be called with either hello or hey, has the description of "Says hello" in the help list, has a min of 1 arguments and a max of 1 arguments (set to -1 for infinite) and prints the usage ("Usage: /hello [player] - The player to say hello to") on incorrect usage.

    After the @Command, you'll need a method like this
    Code:java
    1. public static void hello(final CommandContext args, CommandSender sender) throws Exception {

    Note that the method can be called anything, as long as it is after a @Command.

    Here is an example of the hello command:
    Code:java
    1. @Command(aliases = { "hello", "hey" }, desc = "Says hello", usage = "[player] - The player to say hello to", min = 1, max = 1)
    2. public static void hello(final CommandContext args, CommandSender sender) throws CommandException {
    3. Player target = Bukkit.getPlayer(args.getString(0)); //0 is the index
    4. if (target == null) throw new CommandException("Player "+args.getString(0)+" not found!");
    5. target.sendMessage("Hello!");
    6. }


    Step 4: Registering your commands
    This is probably the easiest step. Remember the setupCommands method in your main class? After all the code that's already in there, call
    Code (Text):
    1. cmdRegister.register(MYCOMMANDCLASS.class);

    Step 3b: Command tricks
    The CommandContext
    The CommandContext is an advanced version of String[] args. It allows for some really cool usages. Here are the methods you are most likely using most:
    args.getString(index) Gets the string at a certain index
    args.getInt(index) Gets the int at a certain index, throws an error message (see the onCommand) if the index is not a string
    args.getJoinedStrings(index) Joins all the strings after index. For example, if the player did /hello <player> I am awesome and you are not, args.getJoinedStrings(1) will return I am....
    For every get<TYPE> command, you can also supply a default value. For example, if you do args.getInt(0, 10) it'll be 10 if the player did not pass an int at index 0.
    For a full documentation on CommandContext, have a look here

    Flags!
    @Command also allows you to specify flags for the player to pass in! Just add flags="a" in your @Command to allow the player to do /hello -a <player>. You can also require a flag to have a value by adding a : in front of it. For example, flags=":ab" will allow you to pass in -b and -a <value>.
    The CommandContext also has the method hasFlag('b') and getFlag('a') for getting the flags.

    Nested commands
    You can allow for nested commands (/myplugin <command>) by adding a @NestedCommand. An example of this is here.

    Conclusion
    I feel that the real power of sk89q's command framework lies in the NestedCommands, but overall it is a really powerful system for handling commands. I hope you try it out :)
     
  2. I alway's wanted a better command framework I could actually understand, the other ones always confused me somewheres along the road, and never saw a tutorial on how to use this yet. Thank you.
     
  3. This is a really good tutorial, great job molenz.
     
  4. I hate to bump, but the Maven setup in the OP is no longer working.
     
  5. Incomprehendable likes this.
  6. RainoBoy97
    I do believe that has fixed the issue, but the Overcast Network is currently down. Is there some other maven repo with the standalone framework?
     
  7. Incomprehendable
    I dont think there is. I'm also waiting for Overcast's repository to go live again, can't build my plugin without it :p
     
    Incomprehendable likes this.
  8. RainoBoy97
    Back to depending on WorldEdit, then. Let's hope it's back up soon enough. Thanks for your help!
    EDIT: I asked the owner of the network on Twitter. He doesn't know of any alternatives and has no ETA.
     
  9. For all future visitors, repo.oc.tc is up again.
     
  10. While the Overcast Network's Maven repository is back up, the shading part of the OP is now horribly incorrect. I'm pretty sure that the new repository link is https://repo.oc.tc/content/groups/public/, and I'm still unaware of the actual artifact ID. But I'm sure that the version is now 0.5-SNAPSHOT. Any help on this would be appreciated.
     
  11. Din't know sk89q had a framework like this, looking into using this for some of my bigger projects. :D
     
  12. Incomprehendable
    This is what I use, I'm not sure if this is the "correct" one to use.
    Code (Text):
    1.  
    2. <dependency>
    3. <groupId>com.sk89q</groupId>
    4. <artifactId>sk89q-command-framework</artifactId>
    5. <version>0.4-SNAPSHOT</version>
    6. </dependency>
    7.  
     
  13. RainoBoy97
    That doesn't seem to be working. When you go to repo.oc.tc, click "Repositories", and then click "Releases", there's no "com" directory. There's one in the "Public" repository though, but in that one, there is no "0.4-SNAPSHOT" version, only a "0.5-SNAPSHOT" one. The artifacts in the public repository are:

    • command-framework-bukkit
    • command-framework-bungee
    • command-framework-core
    • command-framework-parent
    Sorry if I'm making any nooby mistakes, I'm a bit new to Maven.

    EDIT: Fixed.
    I managed to solve this on my own.

    For all of those who may have had this issue, here are the details you want to put in your pom.xml:
    REPO ID: repo.oc.tc
    REPO URL: https://repo.oc.tc/content/groups/public/
    DEPENDENCY GROUP ID: com.sk89q
    DEPENDENCY ARTIFACT ID: command-framework-bukkit
    DEPENDENCY VERSION: 0.5-SNAPSHOT
     
  14. I hate to double post, but I did resolve this issue. Look in my post above. ^
     
  15. For future reference here's a POM.xml for my PVPMoney plugin and it works absolutely fine:
    Code (Text):
    1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    3.     <modelVersion>4.0.0</modelVersion>
    4.     <groupId>com.frostcore</groupId>
    5.     <artifactId>PVPMoney</artifactId>
    6.     <version>0.7</version>
    7.     <build>
    8.         <defaultGoal>clean install</defaultGoal>
    9.         <plugins>
    10.             <plugin>
    11.                 <groupId>org.apache.maven.plugins</groupId>
    12.                 <artifactId>maven-compiler-plugin</artifactId>
    13.                 <version>3.1</version>
    14.                 <configuration>
    15.                     <source>1.6</source>
    16.                     <target>1.6</target>
    17.                 </configuration>
    18.             </plugin>
    19.             <plugin>
    20.                 <groupId>org.apache.maven.plugins</groupId>
    21.                 <artifactId>maven-shade-plugin</artifactId>
    22.                 <version>2.3</version>
    23.                 <executions>
    24.                     <execution>
    25.                         <phase>package</phase>
    26.                         <goals>
    27.                             <goal>shade</goal>
    28.                         </goals>
    29.                         <configuration>
    30.                             <finalName>PVPMoney</finalName>
    31.                             <useBaseVersion>true</useBaseVersion>
    32.                             <shadedClassifierName/>
    33.                             <outputDirectory>${project.basedir}</outputDirectory>
    34.                             <artifactSet>
    35.                                 <includes>
    36.                                     <include>net.gravitydevelopment.updater:updater</include>
    37.                                     <include>com.sk89q:command-framework-bukkit</include>
    38.                                     <include>org.mcstats.bukkit:metrics</include>
    39.                                 </includes>
    40.                             </artifactSet>
    41.                             <relocations>
    42.                                 <relocation>
    43.                                     <pattern>net.gravitydevelopment.updater</pattern>
    44.                                     <shadedPattern>com.frostcore.pvpmoney.updater</shadedPattern>
    45.                                 </relocation>
    46.                                 <relocation>
    47.                                     <pattern>com.sk89q:command-framework-bukkit</pattern>
    48.                                     <shadedPattern>com.frostcore.pvpmoney.commands</shadedPattern>
    49.                                 </relocation>
    50.                                 <relocation>
    51.                                     <pattern>org.mcstats.bukkit:metrics</pattern>
    52.                                     <shadedPattern>com.frostcore.pvpmoney.metrics</shadedPattern>
    53.                                 </relocation>
    54.                             </relocations>
    55.                         </configuration>
    56.                     </execution>
    57.                 </executions>
    58.             </plugin>
    59.         </plugins>
    60.     </build>
    61.     <repositories>
    62.         <repository>
    63.             <id>bukkit-repo</id>
    64.             <url>http://repo.bukkit.org/content/groups/public/</url>
    65.         </repository>
    66.         <repository>
    67.             <id>vault-repo</id>
    68.             <url>http://nexus.theyeticave.net/content/repositories/pub_releases/</url>
    69.         </repository>
    70.         <repository>
    71.             <id>Plugin Metrics</id>
    72.             <url>http://repo.mcstats.org/content/repositories/public</url>
    73.         </repository>
    74.         <repository>
    75.             <id>repo.oc.tc</id>
    76.             <url>http://repo.oc.tc/content/repositories/public/</url>
    77.         </repository>
    78.         <repository>
    79.             <id>gravity-repo</id>
    80.             <url>http://repo.gravitydevelopment.net</url>
    81.         </repository>
    82.     </repositories>
    83.     <dependencies>
    84.         <dependency>
    85.             <groupId>org.bukkit</groupId>
    86.             <artifactId>bukkit</artifactId>
    87.             <version>LATEST</version>
    88.             <scope>provided</scope>
    89.         </dependency>
    90.         <dependency>
    91.             <groupId>org.mcstats.bukkit</groupId>
    92.             <artifactId>metrics</artifactId>
    93.             <version>R7</version>
    94.         </dependency>
    95.         <dependency>
    96.             <groupId>net.milkbowl.vault</groupId>
    97.             <artifactId>VaultAPI</artifactId>
    98.             <version>1.4</version>
    99.             <scope>provided</scope>
    100.         </dependency>
    101.         <dependency>
    102.             <groupId>com.sk89q</groupId>
    103.             <artifactId>command-framework-bukkit</artifactId>
    104.             <version>0.5-SNAPSHOT</version>
    105.         </dependency>
    106.         <dependency>
    107.             <groupId>net.gravitydevelopment.updater</groupId>
    108.             <artifactId>updater</artifactId>
    109.             <version>2.1</version>
    110.         </dependency>
    111.     </dependencies>
    112. </project>
     
  16. Incomprehendable
    You don't need to wait for repos to come online to build, you can always build it yourself. Fetch the code from github and do a clean install with maven. You will end up with perfectly usable build in your local maven repo. It's even written in readme.
     
  17. raGan.
    "Complaining > Competence"
    In seriousness, I wasn't aware of that, thanks for letting me know!
     
  18. Is there any fancy way to differ player vs. Console command?
     
  19. No, I don't think so. I usually do something like:
    Code (Text):
    1.  
    2. if (!(sender instanceof Player))
    3. throw new CommandException("You must be a player to use this command.");
    4.  
    And if you don't put a @ Console annotation before your command method, I don't think it will be called if the command was issued by the console. I'm not sure if the method will be called by a command block or not though.
     

Share This Page