Solved Inventory reflections - java.lang.NoSuchMethodException: org.bukkit.craftbukkit.v1_15_R1.inventory.C

Discussion in 'Spigot Plugin Development' started by gronnmann, Mar 22, 2020.

  1. Hello!
    I am trying to make my plugin both support 1.8 and 1.14, but I can't get it to work with inventory names. There is no way to get the name which is available on both versions, so I have tried to use reflections. The problem is, Spigot tells me there is no Inventory getHolder method.

    This is my code:
    Code (Java):
    public static String getInventoryName(Inventory inv) {
            try {
                if (GeneralUtils.getMinecraftVersion() >= 14) {
                    Method getContainer = inv.getClass().getDeclaredMethod("getHolder");
                    Object container = getContainer.invoke(inv);
                   
                    Method getCustomName = container.getClass().getDeclaredMethod("getCustomName");
                    return (String) getCustomName.invoke(container);
                   
                   
                }else {
                    Method getName18 = inv.getClass().getDeclaredMethod("getName");
                   
                    return (String) getName18.invoke(inv);
                }
            }
            catch(Exception e) {
                Debug.print("Failed fetching inventory name for " + inv);
                Debug.print(e.getStackTrace().toString());
               
                e.printStackTrace();
               
                return "";
            }
        }
    And this is my error:
    Code (Text):
    [15:13:29] [Server thread/WARN]: java.lang.NoSuchMethodException: org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryCustom.getHolder()
    [15:13:29] [Server thread/WARN]:        at java.lang.Class.getDeclaredMethod(Unknown Source)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.utils.coinflipper.ReflectionUtils.getInventoryName(ReflectionUtils.java:14)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addPage(PagedInventory.java:76)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:146)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.materials.MaterialChooser.setup(MaterialChooser.java:59)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.FileEditSelector.setup(FileEditSelector.java:59)
    [15:13:29] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.Main.onEnable(Main.java:58)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:263)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:351)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:432)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugin(CraftServer.java:464)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugins(CraftServer.java:378)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.reload(CraftServer.java:835)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.Bukkit.reload(Bukkit.java:620)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:27)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:149)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchCommand(CraftServer.java:723)
    [15:13:29] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchServerCommand(CraftServer.java:708)
    [15:13:29] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.handleCommandQueue(DedicatedServer.java:443)
    [15:13:29] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.b(DedicatedServer.java:407)
    [15:13:29] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.a(MinecraftServer.java:984)
    [15:13:29] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.run(MinecraftServer.java:824)
    [15:13:29] [Server thread/WARN]:        at java.lang.Thread.run(Unknown Source)
    Any ideas?
     
  2. You're using getDeclaredMethod, which only finds method declared in the class - you probably want to use getMethod, which also searches in the superclasses. CraftInventoryCustom does not override getHolder, it is using the implementation defined in CraftInventory.
     
    • Winner Winner x 1
  3. Thanks, it fixed the issue for the Inventory class.
    I'm still having trouble getting the name, as spigot tries to find the method in my custom InventoryHolder class, instead of the Container class. Do you have any ideas how to get the Container class?
    The error I get now is
    Code (Java):
    [15:31:38] [Server thread/WARN]: java.lang.NoSuchMethodException: io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventoryHolder.getCustomName()
    [15:31:38] [Server thread/WARN]:        at java.lang.Class.getMethod(Unknown Source)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.utils.coinflipper.ReflectionUtils.getInventoryName(ReflectionUtils.java:17)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addPage(PagedInventory.java:74)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:144)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.materials.MaterialChooser.setup(MaterialChooser.java:59)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.FileEditSelector.setup(FileEditSelector.java:59)
    [15:31:38] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.CoinFlipper.onEnable(CoinFlipper.java:58)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:263)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:351)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:432)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugin(CraftServer.java:464)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugins(CraftServer.java:378)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.reload(CraftServer.java:835)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.Bukkit.reload(Bukkit.java:620)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:27)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:149)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchCommand(CraftServer.java:723)
    [15:31:38] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchServerCommand(CraftServer.java:708)
    [15:31:38] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.handleCommandQueue(DedicatedServer.java:443)
    [15:31:38] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.b(DedicatedServer.java:407)
    [15:31:38] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.a(MinecraftServer.java:984)
    [15:31:38] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.run(MinecraftServer.java:824)
    [15:31:38] [Server thread/WARN]:        at java.lang.Thread.run(Unknown Source)
    Since it refers to my inventoryholder class, its following:
    Code (Java):
    class PagedInventoryHolder implements InventoryHolder{

        @Override
        public Inventory getInventory() {
            return null;
        }
     
    }

    Edit: I have no tried getting the class directly, with no luck
    Code (Java):
    Method getContainer = inv.getClass().getMethod("getHolder");
                 
                 
                    Class<?> containerClass = Class.forName("org.bukkit.block.Container");
                 
                    Object container = getContainer.invoke(inv);
                 
                    Method getCustomName = containerClass.getClass().getMethod("getCustomName");
                    return (String) getCustomName.invoke(container);
    leads to
    Code (Text):
    [15:40:21] [Server thread/WARN]: java.lang.NoSuchMethodException: java.lang.Class.getCustomName()
    [15:40:21] [Server thread/WARN]:        at java.lang.Class.getMethod(Unknown Source)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.utils.coinflipper.ReflectionUtils.getInventoryName(ReflectionUtils.java:21)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addPage(PagedInventory.java:76)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:146)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.materials.MaterialChooser.setup(MaterialChooser.java:59)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.FileEditSelector.setup(FileEditSelector.java:59)
    [15:40:21] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.CoinFlipper.onEnable(CoinFlipper.java:58)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:263)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:351)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:432)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugin(CraftServer.java:464)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugins(CraftServer.java:378)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.reload(CraftServer.java:835)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.Bukkit.reload(Bukkit.java:620)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:27)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:149)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchCommand(CraftServer.java:723)
    [15:40:21] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchServerCommand(CraftServer.java:708)
    [15:40:21] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.handleCommandQueue(DedicatedServer.java:443)
    [15:40:21] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.b(DedicatedServer.java:407)
    [15:40:21] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.a(MinecraftServer.java:984)
    [15:40:21] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.run(MinecraftServer.java:824)
    [15:40:21] [Server thread/WARN]:        at java.lang.Thread.run(Unknown Source)
    I have now gotten a Nameable class. The problem is running the inventory holder through it. Right now it gives me a IllegalArgumentException
    The code:
    Code (Java):
    Method getContainer = inv.getClass().getMethod("getHolder");
                    Object holder = getContainer.invoke(inv);
                   
                    Class<?> containerClass = Class.forName("org.bukkit.Nameable");
                    Method getCustomName = containerClass.getMethod("getCustomName");
                   
                   
                    return (String) getCustomName.invoke(holder);
                   
                   
                }else {
                    Method getName18 = inv.getClass().getMethod("getName");
                   
                    return (String) getName18.invoke(inv);
                }
            }
    Code (Text):
    [15:45:43] [Server thread/WARN]: java.lang.IllegalArgumentException: object is not an instance of declaring class
    [15:45:43] [Server thread/WARN]:        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [15:45:43] [Server thread/WARN]:        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    [15:45:43] [Server thread/WARN]:        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    [15:45:43] [Server thread/WARN]:        at java.lang.reflect.Method.invoke(Unknown Source)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.coinflipper.ReflectionUtils.getInventoryName(ReflectionUtils.java:22)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addPage(PagedInventory.java:76)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:146)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.config.SoundChooser.setup(SoundChooser.java:55)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.FileEditSelector.setup(FileEditSelector.java:52)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.CoinFlipper.onEnable(CoinFlipper.java:58)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addPage(PagedInventory.java:76)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:146)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.materials.MaterialChooser.setup(MaterialChooser.java:59)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.utils.pagedinventory.coinflipper.PagedInventory.addItem(PagedInventory.java:146)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.materials.MaterialChooser.setup(MaterialChooser.java:59)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.gui.configurationeditor.FileEditSelector.setup(FileEditSelector.java:59)
    [15:45:43] [Server thread/WARN]:        at io.github.gronnmann.coinflipper.CoinFlipper.onEnable(CoinFlipper.java:58)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:263)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:351)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:432)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugin(CraftServer.java:464)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.enablePlugins(CraftServer.java:378)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.reload(CraftServer.java:835)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.Bukkit.reload(Bukkit.java:620)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:27)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:149)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchCommand(CraftServer.java:723)
    [15:45:43] [Server thread/WARN]:        at org.bukkit.craftbukkit.v1_15_R1.CraftServer.dispatchServerCommand(CraftServer.java:708)
    [15:45:43] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.handleCommandQueue(DedicatedServer.java:443)
    [15:45:43] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.DedicatedServer.b(DedicatedServer.java:407)
    [15:45:43] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.a(MinecraftServer.java:984)
    [15:45:43] [Server thread/WARN]:        at net.minecraft.server.v1_15_R1.MinecraftServer.run(MinecraftServer.java:824)
    [15:45:43] [Server thread/WARN]:        at java.lang.Thread.run(Unknown Source)
     
    #3 gronnmann, Mar 22, 2020
    Last edited: Mar 22, 2020
  4. Why don't you just use InventoryView#getTitle() ?
     
    • Winner Winner x 1
  5. Unfortunately I need an event to get the inventoryview, no method in inventory class to get it.
     
    • Funny Funny x 1
  6. In that case, obtain the MinecraftInventory that is wrapped by CraftInventoryCustom and use MinecraftInventory#getTitle(), or if you're dealing with inventories that are not created using Server#createInventory you can get the custom name from the containerblock/entity that owns the inventory.
    Interestingly, getTitle() used to be a method on Inventory, as can be seen from the old bukkit javadocs. I wonder why that got removed.
     
    #6 Jannyboy11, Mar 22, 2020
    Last edited: Mar 22, 2020
    • Like Like x 1
  7. Thank you. Do you have any ideas how I could get the class? The problem seems to be that there is no MinecraftInventory object, its generated on initialization of CraftInventoryCustom (https://github.com/squallblade/Spig...aftbukkit/inventory/CraftInventoryCustom.java), so I have no MinecraftInventory to get :/
     
  8. You can grab the viewer list from the inventory, which should in turn allow you to grab the view. It's a bit roundabout, but it should work.
     
    • Like Like x 1
  9. Thank you, did some trying and failing and did this. Ended up with getting the IInventory and using its getTitle funciton.
    For those with similar problems, this is my workaround:
    Code (Java):
    public static String getInventoryName(Inventory inv) {
           
            String title;
           
            try {
                if (GeneralUtils.getMinecraftVersion() >= 14) {
                   
                    String packageName = Bukkit.getServer().getClass().getPackage().getName();
                   
                   
                    Class<?> invClass = Class.forName(packageName + ".inventory.CraftInventory");
                   
                    Field iinventory = invClass.getDeclaredField("inventory");
                    iinventory.setAccessible(true);
                   
                   
                    Object iinv = iinventory.get(inv);
                   
                   
                   
                    Method getTitle = iinv.getClass().getMethod("getTitle");
                    getTitle.setAccessible(true);
                   
                    title = (String) getTitle.invoke(iinv);
                   
                   
                    getTitle.setAccessible(false);
                   
                    Debug.print("Reflection copying inventory (1.14+): " +  title);
                   
                    iinventory.setAccessible(false);
                   
                   
                   
                    //return (String)name;
                   
                    //return "";
                   
                }else {
                    Method getName18 = inv.getClass().getMethod("getName");

                    title = (String) getName18.invoke(inv);
                   
                    Debug.print("Reflection copying inventory (-1.14): " +  title);
                }
            }
            catch(Exception e) {
                Debug.print("Failed fetching inventory name for " + inv);
                Debug.print(e.getStackTrace().toString());
               
                e.printStackTrace();
               
                title = "";
            }
           
           
            return title;
        }
     
  10. Like you would get every class; using Class#forName(fullyQualifiedName)
    Qualified names of nested classes and inner classes use a dollar sign as a separator from the outer class, so it should be "org.bukkit.craftbukkit.<version>.inventory.CraftInventoryCustom$MinecraftInventory"
     
    • Winner Winner x 1