Reflections and NMS

Discussion in 'Spigot Plugin Development' started by Nico_DreamzZ, Jun 10, 2017.

  1. Hey there. I have a problem. I need to run this line:

    Code (Text):
    ((CraftServer) plugin.getServer()).getCommandMap().register(plugin.getName(), command);
    Therefore i need the import:

    Code (Text):
    import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
    Is it possible to make this working with multiple spigot/bukkit versions? I don't know anything about NMS and reflections. Thanks for help!

    Greetings Nico_DreamzZ
     
  2. So ur tryna register a command using reflection / nms o_O
     
  3. Yes, it is part of a modular system where i cannot use CommandExecutors.
     
  4. I tried some things but they all dont work :( My last test was:

    Code (Text):
    String version = plugin.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
            String name = "org.bukkit.craftbukkit."+version+".CraftServer";
            try {
                Class<?> craftserverclass = Class.forName(name);
                ((craftserverclass) plugin.getServer()).getCommandMap().register(plugin.getName(), command);

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    But this doesnt work :(
     
  5. You could use interface
     
  6. @coco_gig What do you mean?
     
  7. This isn't an option because i don't want do add different versions to my dependencys. A friend helped my out. The working result is:
    Code (Text):
    protected void registerCommand(BukkitCommand command) {
            String version = plugin.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
            String name = "org.bukkit.craftbukkit."+version+".CraftServer";
            Class<?> craftserver = null;
            try {
                craftserver = Class.forName(name);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            assert craftserver != null;
            try {
                SimpleCommandMap scm = (SimpleCommandMap) craftserver.cast(plugin.getServer()).getClass().getMethod("getCommandMap")
                        .invoke(plugin.getServer());
                scm.register(plugin.getName(), command);
            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
     
  8. Fine you should use a reflection util class
    For example the class from protocollib or tinyprotocol
     
  9. There's an alternate to this where instead of attempting to get the CraftServer method, you directly access the field. It's cleaner that the above approach as you don't have to check the bukkit version and depend on craftbukkit.

    An example may be found here, https://github.com/Pante/Karus-Comm...slabs/commons/commands/ProxiedCommandMap.java
    Essentially you pass in the server into the constructor and it attempts to get the commandMap field, "commandMap", then bypass the private modifier and finally get the commandMap from the server instance.
     
    #11 PanteLegacy, Jun 11, 2017
    Last edited: Jun 11, 2017
    • Creative Creative x 1
  10. The assert statement should not be used in production. Use a if statement and broadcast an error message to the console instead.
     
  11. Never should you broadcast an error message yourself either, throw an exception instead.
     
  12. I agree to some extent but every statement that you can handle yourself needs no exception.

    You can use exceptions in API’s to allow the developers to react to some problem.