Solved Get returned value from method with Reflection

Discussion in 'Plugin Development' started by BrunoRM, Jun 24, 2021.

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

    BrunoRM

    Hello, I'm trying to get the IChatBaseComponent return value from the "a" method from ChatSerializer.

    This is what I want to do:
    Code:
    IChatBaseComponent message = ChatSerializer.a("{\"text\":\"" + text + "\"}");
    But I want to do it with reflection, so I don't have to import the IChatBaseComponent and ChatSerializer for every net.minecraft.server

    I tried doing this:
    Code:
    String nms = "net.minecraft.server." + version;
    Class<?> chatSerializer = Class.forName(nms + ".IChatBaseComponent$ChatSerializer");
    Method method = chatSerializer.getMethod("a");
    Object object = method.invoke(chatSerializer, "{\"text\":\"" + "hello" + "\"}");
    
    But I get this error:
    Code:
    java.lang.NoSuchMethodException: java.lang.Class.a()
    This is my first post on the Bukkit forums, and its also the first time I try doing things using Reflection
    I appreciate the help!
     
  2. Offline

    KarimAKL

    @BrunoRM
    1. You might want to use #getDeclaredMethod instead of #getMethod.
    2. I have never had to use reflection to get an inner class, so I do not know whether it would be . or $, but you can try both.
     
  3. Offline

    BrunoRM

    I fixed the "NoSuchMethodException" error, but now I have another error:
    Code:
    java.lang.IllegalArgumentException: object is not an instance of declaring class
    This is my current code:
    Code:
                        Class<?> chatSerializer = Class.forName(nms + ".IChatBaseComponent$ChatSerializer");
                        Method a = chatSerializer.getMethod("a", String.class);
                        Object chatTitle = a.invoke(chatSerializer, "{\"text\":\"" + "hola" + "\"}");
                        //IChatBaseComponent chatTitle = ChatSerializer.a("{\"text\":\"" + "hola" + "\"}");
                       
                        Class <?> iChatBaseComponent = Class.forName(nms + ".IChatBaseComponent");
                        Class <?> packetPlayOutTitle = Class.forName(nms + ".PacketPlayOutTitle");
                        Object enumTitleActionTitle = packetPlayOutTitle.getDeclaredClasses()[0].getEnumConstants()[0];
                        Constructor<?> packetPlayOutTitleConstructor = packetPlayOutTitle.getConstructor(packetPlayOutTitle.getDeclaredClasses()[0], iChatBaseComponent);
                        Object titlePacket = packetPlayOutTitleConstructor.newInstance(enumTitleActionTitle, chatTitle);
                        //PacketPlayOutTitle titlePacket = new PacketPlayOutTitle(EnumTitleAction.TITLE, chatTitle);
    
                        Class <?> craftPlayerClass = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
                        Method getHandle = craftPlayerClass.getMethod("getHandle");
                        // CraftPlayer.getHandle();
                       
                        Object craftPlayerHandle = getHandle.invoke(player);
                        // (CraftPlayer) player.getHandle();
                       
                        Class <?> playerConnection = craftPlayerHandle.getClass().getField("playerConnection").getType();
                        // (CraftPlayer) player.getHandle().playerConnection;
                       
                        Class<?> packetClass = Class.forName(nms + ".Packet");
                        Method sendPacket = playerConnection.getMethod("sendPacket", packetClass);
                       
                        sendPacket.invoke(playerConnection, titlePacket);
    
    The error is on the last line.
    The method sendPacket() takes an instance of the class Packet
    The problem is, I'm giving it an instance of the class PacketPlayOutTitle
    Is there any way I can convert it to Packet?
     
  4. Offline

    Shqep

    @BrunoRM
    You're getting a NoSuchMethodException because you did not specify the signature of the method.
    Code:Java
    1. public class Yay {
    2. public void a() {}
    3. public void b(String string) {}
    4. }
    5.  
    6. Class.forName("Yay").getDeclaredMethod("b"); // NoSuchMethod
    7. Class.forName("Yay").getDeclaredMethod("b", String.class); // Returns the b(String string) method.


    Reflection is rough and ugly.
    Code:Java
    1. // IChatBaseComponent message = ChatSerializer.a("{\"text\":\"" + text + "\"}")
    2.  
    3. // First, you have to grab the class ChatSerializer.
    4. final Class<?> chatSerializer = Class.forName(nms + ".ChatSerializer") // For version 1.8 - 1.8.3
    5. final Class<?> chatSerializer = Class.forName(nms + ".IChatBaseComponent$ChatSerializer") // For version 1.8.4 - 1.16.4
    6. final Class<?> chatSerializer = Class.forName("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer") // For version 1.17


    You might want to look into a Reflections guide, as I don't think you really have fully grasped how to use it.
     
    BrunoRM and KarimAKL like this.
  5. Offline

    KarimAKL

    @BrunoRM You are invoking the #sendPacket method with the first parameter being the PlayerConnection class rather than an instance of it. You should get the player's PlayerConnection and use that as the first parameter.
     
  6. Offline

    BrunoRM

    It worked!

    Final code:

    Code:
                        Class<?> chatSerializer = Class.forName(nms + ".IChatBaseComponent$ChatSerializer");
                        Method a = chatSerializer.getMethod("a", String.class);
                        Object chatTitle = a.invoke(chatSerializer, "{\"text\":\"" + "hola" + "\"}");
                        //IChatBaseComponent chatTitle = ChatSerializer.a("{\"text\":\"" + "hola" + "\"}");
                       
                        Class <?> iChatBaseComponent = Class.forName(nms + ".IChatBaseComponent");
                        Class <?> packetPlayOutTitle = Class.forName(nms + ".PacketPlayOutTitle");
                        Object enumTitleActionTitle = packetPlayOutTitle.getDeclaredClasses()[0].getEnumConstants()[0];
                        Constructor<?> packetPlayOutTitleConstructor = packetPlayOutTitle
                                .getConstructor(packetPlayOutTitle.getDeclaredClasses()[0], iChatBaseComponent);
                        Object titlePacket = packetPlayOutTitleConstructor.newInstance(enumTitleActionTitle, chatTitle);
                        //PacketPlayOutTitle titlePacket = new PacketPlayOutTitle(EnumTitleAction.TITLE, chatTitle);
    
                        Class <?> craftPlayerClass = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
                        Method getHandle = craftPlayerClass.getMethod("getHandle");
                        // CraftPlayer.getHandle();
                       
                        Object craftPlayerHandle = getHandle.invoke(player);
                        // (CraftPlayer) player.getHandle();
                       
                        Field playerConnectionField = craftPlayerHandle.getClass().getField("playerConnection");
                        Object playerConnection = playerConnectionField.get(craftPlayerHandle);
                        // (CraftPlayer) player.getHandle().playerConnection;
                       
                        Class<?> packetClass = Class.forName(nms + ".Packet");
                        Method sendPacket = playerConnectionField.getType().getMethod("sendPacket", packetClass);
                       
                        sendPacket.invoke(playerConnection, titlePacket);
    
     
    KarimAKL likes this.
Thread Status:
Not open for further replies.

Share This Page