Reflection to access version-specific classes

Discussion in 'Spigot Plugin Development' started by Bolt, May 24, 2015.

  1. I'm creating a few plugins that involve packets now but a problem that I am encountering fairly often now is that when different versions of Bukkit/Spigot are used, it can make the plugin malfunction. I would like to know how to use reflection in order to ensure classes like CraftPlayer match the version contained in the server. I've also read about other methods that do not use reflection but it requires creating classes per version. I find reflection would be more convenient as even as the server updates, I do not need to constantly keep applying updates as the class names will change as necessary.
     
  2. I went through it but I'm still not sure how to use it. I can see it comes with plenty built-in static methods but I'm not sure how to apply them to get classes such as CraftPlayer.
     
  3. @dxdy Thanks! It helps a lot. I understand how to get nms classes now. My problem now though is that I'm not sure how to use the getCraftBukkitClass() method. It says "Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package." So should I use
    Code (Text):
    Class<?> cp_class = Reflection.getCraftBukkitClass("entity.CraftPlayer");
    The full import path for the CraftPlayer class in v1_8_R2 is
    Code (Text):
    import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
     
  4. Ok thanks for the clarification! I was wondering though how to cast it to use it and access its method like so :
    Code (Text):
    ((CraftPlayer)player).getHandle().playerConnection.sendPacket(new PacketPlayOutEntityDestroy(((CraftArrow) arrow).getHandle().getId()));
    I already got the Class<?> classes using the Reflection class but I don't know how to utilize them in order to get the same result as above.
    Code (Text):
    Class<?> packetclass = Reflection.getClass("{nms}.PacketPlayOutEntityDestroy");
    Class<?> cp_class = Reflection.getCraftBukkitClass("entity.CraftPlayer");
    Class<?> ca_class = Reflection.getCraftBukkitClass("entity.CraftArrow");
     
  5. Ok thanks. I didn't think Reflection would really need that much familiarization. I guess I was wrong. I looked up some tutorials and I think I can understand it better now. :) Here's also a good tutorial made by Skionz in using Reflection to send packets.
    https://bukkit.org/threads/basic-reflection-tutorial.329127/
     
    • Like Like x 1
  6. Basically, you get the class you want, you get the constructor you want, then you create an instance of the class using the constructor. Here is an example using the Integer wrapper class:
    Code (Text):
    Class<?> integerClass = Class.forName("java.lang.Integer");
    Constructor<?> integerConstructor = integerClass.getConstructor(int.class);
    Object integer = integerConstructor.newInstance(5);
    System.out.println(integer);
    This is essentially the same as doing:
    Code (Text):
    Integer integer = new Integer(5);
    System.out.println(integer);
     
  7. Thanks! :) I think I more or less understood the basics of reflections. I just get a bit confused when I start using it for long lines of code like the one I mentioned earlier because it's looking at it from a different perspective, one that's completely new to me. Something like looking at a bunch of scattered pixels all disarranged and looking at it as the picture it's supposed to form.
     
  8. also that class that I linked
    contains many useful method for easier method/constructor invoking and setting/getting fields.
    PS: you should "cache" all that FieldAccessor, MethodInvokers or if you don't using that util, then just Fields and Methods.
    Like:
    Code (Text):
    public static final MethodInvoker someInvoker = Reflections.getMethod...;
    and always use that pre-made stuff.

    Reflections are really slow, and getting new method/field/constructor each time may cause bigger lags.
    Ofc you don't need that if you only use that at server start ;)
    And reflections like that are still slower than normal java code, like 2 x slower.
    But without cache, is... like 30x times slower ;)
     
  9. My current way of doing this is :
    Code (Text):


    public static Class<?> packetclass = Reflection
                .getClass("{nms}.PacketPlayOutEntityDestroy");
        public static Class<?> cp_class = Reflection
                .getCraftBukkitClass("entity.CraftPlayer");
        public static Class<?> ca_class = Reflection
                .getCraftBukkitClass("entity.CraftArrow");

    try {
                                         
                        Constructor<?> packetConst = packetclass.getConstructor(int[].class);
                        Object ca_inst = ca_class.cast(arrow);
                        Method ca_getHandle = ca_inst.getClass().getMethod("getHandle");
                        Object ca_arrow = ca_getHandle.invoke(ca_inst);
                     
                        Object ca_id = ca_arrow.getClass().getMethod("getId").invoke(ca_arrow);
                        Object packetInst = packetConst.newInstance(new int[]{(int) ca_id});
                     
                        for (Player p : Bukkit.getOnlinePlayers()) {

                            Object cp_inst = cp_class.cast(p);
                            Method cp_getHandle = cp_inst.getClass().getMethod("getHandle");
                            Object cp_handle = cp_getHandle.invoke(cp_inst);
                            Field conField = cp_handle.getClass().getField("playerConnection");
                            Object playercon = conField.get(cp_handle);
                            Method sendpacket = playercon.getClass().getMethod("sendPacket", packetclass.getInterfaces()[0]);
                            sendpacket.invoke(playercon, packetInst);
                         

                        }
                     
                    } catch (SecurityException e1) {
                        e1.printStackTrace();
                    } catch (NoSuchMethodException e1) {
                        e1.printStackTrace();
                    } catch (IllegalAccessException e1) {
                        e1.printStackTrace();
                    } catch (IllegalArgumentException e1) {
                        e1.printStackTrace();
                    } catch (InvocationTargetException e1) {
                        e1.printStackTrace();
                    } catch (NoSuchFieldException e1) {
                        e1.printStackTrace();
                    } catch (InstantiationException e1) {
                        e1.printStackTrace();
                    }
    I realize that using reflection inside the loop will cause lag but I'm not sure how to make it more efficient as it requires the actual player instance.
     
  10. You can cache all of the Methods and Fields.
     
  11. Ah, I see. Thanks! :)