[Tutorial] Registering commands at runtime!

Discussion in 'Resources' started by ELCHILEN0, Jul 7, 2013.

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

    ELCHILEN0

    Hey everyone!

    Recently I have been working on a more generic command manager that allows for dynamic categories and subcommands. In the process of development I realized that a huge pitfall of the Bukkit command system is the inability in the native API for direct accessibility to register commands at runtime. So without further ado I decided to make a simple tutorial on how to achieve such functionality!

    So first I will be posting the code then explaining each segment.
    Code:java
    1. private Plugin plugin;
    2.  
    3. public void registerCommand(String... aliases) {
    4. PluginCommand command = getCommand(aliases[0], plugin);
    5.  
    6. command.setAliases(Arrays.asList(aliases));
    7. getCommandMap().register(plugin.getDescription().getName(), command);
    8. }
    9.  
    10. private static PluginCommand getCommand(String name, Plugin plugin) {
    11. PluginCommand command = null;
    12.  
    13. try {
    14. Constructor<PluginCommand> c = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
    15. c.setAccessible(true);
    16.  
    17. command = c.newInstance(name, plugin);
    18. } catch (SecurityException e) {
    19. e.printStackTrace();
    20. e.printStackTrace();
    21. } catch (IllegalAccessException e) {
    22. e.printStackTrace();
    23. } catch (InstantiationException e) {
    24. e.printStackTrace();
    25. e.printStackTrace();
    26. } catch (NoSuchMethodException e) {
    27. e.printStackTrace();
    28. }
    29.  
    30. return command;
    31. }
    32.  
    33. private static CommandMap getCommandMap() {
    34. CommandMap commandMap = null;
    35.  
    36. try {
    37. if (Bukkit.getPluginManager() instanceof SimplePluginManager) {
    38. Field f = SimplePluginManager.class.getDeclaredField("commandMap");
    39. f.setAccessible(true);
    40.  
    41. commandMap = (CommandMap) f.get(Bukkit.getPluginManager());
    42. }
    43. } catch (NoSuchFieldException e) {
    44. e.printStackTrace();
    45. } catch (SecurityException e) {
    46. e.printStackTrace();
    47. e.printStackTrace();
    48. } catch (IllegalAccessException e) {
    49. e.printStackTrace();
    50. }
    51.  
    52. return commandMap;
    53. }


    First what we must do is create a PluginCommand. Since by default the PluginCommand constructor is protected we cannot instantiate it with new PluginCommand(String name, Plugin plugin). Instead we create it by accessing it using reflection in the initCommand() method.

    Next is where the actual attributes go. These do not require reflection as they are public by default. All that I require in my plugins are to set the aliases; however, all other fields that can be set in the plugin.yml can also be set here as well.

    Finally we add the command to the server command map. The command map is the same for every plugin and is found in the SimplePluginManager and we access it through reflection in the getCommandMap() method. The command is then registered for your plugin!

    Remember that you still have to set the command executor. This can be set when you set the attributes with command.setExecutor(); or can be set later with getCommand("command").setExecutor();

    I certainly hope this helps some of you! :)
     
    Ultimate_n00b likes this.
  2. Offline

    RainoBoy97

    Thanks! I needed this :)

    I modified to work with my custom command executor! Works awesome :D
     
  3. Offline

    MylesIsCool

    But what if SimplePluginManager isn't SimplePluginManager! What about custom plugin managers :(, I prefer Bukkit.getPluginManager().getClass() instead.
     
  4. Offline

    ELCHILEN0

    Glad you like it!

    Yes that would make it more compatible with any modifications. I will update the tutorial.
     
  5. Offline

    SoThatsIt

    um, wheres the initcommand method? :p

    Edit: wait my bad, its acutally called getCommand in the code
     
  6. Offline

    Vislo

    I think its better use PlayerCommandPreprocessEvent its easy don't use reflection less line of code and can make it more interact with user and prevent any other command overrides yours.
    Its pretty simple.
     
  7. Offline

    ELCHILEN0

    That is always an alternative; however, some plugins do use the command map to get a list of registered commands. By running commands in the PlayerCommandPreprocessEvent you loose that functionality. It is all a matter on what you would prefer, simplicity vs compatibility.

    Oh woops, thanks for noticing! I must have overlooked that when moving the snippets from Eclipse and into here. Fixed!
     
  8. Offline

    nightpool

    Short and sweet! I like it!
     
Thread Status:
Not open for further replies.

Share This Page