Util ActionBar Util

Discussion in 'Resources' started by PhantomUnicorns, May 3, 2018.

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

    PhantomUnicorns

    In one of the recent updates they changed the action bar code for creating the packet. They added an enum which is replacing the byte (nicer code), because of this (and I needed an actionbar) I coded a reflection util that is multi version. I'm not sure if I got the versions right (I don't know when they switched it) so if anyone finds anything wrong with that, pls comment so I may change the code. Here it is (It has one method):
    Code (open)

    Code:
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.entity.Player;
    
    public class ActionBar {
    
        private static final String _VERSION;
        private static Class<?> _CRAFTPLAYER_CLASS;
        private static Constructor<?> _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR;
        private static Method _A_METHOD, _GETHANDLE_METHOD, _SEND_PACKET_METHOD;
        private static Field _PLAYER_CONNECTION_FIELD;
        private static Object _GAME_INFO_CONSTANT;
    
        static {
            String name = Bukkit.getServer().getClass().getName();
            name = name.substring(name.indexOf("craftbukkit.") + "craftbukkit.".length());
            _VERSION = name.substring(0, name.indexOf("."));
            try {
                Class<?> _ICHAT_BASE_COMPONENT_CLASS = Class
                        .forName("net.minecraft.server." + _VERSION + ".IChatBaseComponent");
                Class<?> _PACKET_PLAY_OUT_CHAT_CLASS = Class
                        .forName("net.minecraft.server." + _VERSION + ".PacketPlayOutChat");
                _CRAFTPLAYER_CLASS = Class.forName("org.bukkit.craftbukkit." + _VERSION + ".entity.CraftPlayer");
                Class<?> _ENTITYPLAYER_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".EntityPlayer");
                Class<?> _PLAYER_CONNECTION_CLASS = Class.forName("net.minecraft.server." + _VERSION + ".PlayerConnection");
    
                if (_VERSION.contains("1.9")) {
                    _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR = _PACKET_PLAY_OUT_CHAT_CLASS
                            .getConstructor(_ICHAT_BASE_COMPONENT_CLASS, byte.class);
                } else {
                    Class<?> _CHAT_MESSAGE_TYPE_ENUM = Class
                            .forName("net.minecraft.server." + _VERSION + ".ChatMessageType");
                    _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR = _PACKET_PLAY_OUT_CHAT_CLASS
                            .getConstructor(_ICHAT_BASE_COMPONENT_CLASS, _CHAT_MESSAGE_TYPE_ENUM);
                    for (Object obj : _CHAT_MESSAGE_TYPE_ENUM.getEnumConstants()) {
                        if (obj.toString().equals("GAME_INFO")) {
                            _GAME_INFO_CONSTANT = obj;
                        }
                    }
                }
    
                _A_METHOD = Class.forName("net.minecraft.server." + _VERSION + ".IChatBaseComponent$ChatSerializer")
                        .getMethod("a", String.class);
                _GETHANDLE_METHOD = _CRAFTPLAYER_CLASS.getMethod("getHandle");
                _SEND_PACKET_METHOD = _PLAYER_CONNECTION_CLASS.getMethod("sendPacket",
                        Class.forName("net.minecraft.server." + _VERSION + ".Packet"));
    
                _PLAYER_CONNECTION_FIELD = _ENTITYPLAYER_CLASS.getDeclaredField("playerConnection");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        public static void send(Player player, String message) {
            try {
                Object messageComponent = _A_METHOD.invoke(null,
                        "{\"text\": \"" + ChatColor.translateAlternateColorCodes('&', message) + "\"}");
                if (_VERSION.contains("1.9")) {
                    Object packet = _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR.newInstance(messageComponent, (byte) 2);
                    Object craftPlayer = _CRAFTPLAYER_CLASS.cast(player);
                    Object entityPlayer = _GETHANDLE_METHOD.invoke(craftPlayer);
                    Object playerConnection = _PLAYER_CONNECTION_FIELD.get(entityPlayer);
                    _SEND_PACKET_METHOD.invoke(playerConnection, packet);
                } else {
                    Object packet = _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR.newInstance(messageComponent, _GAME_INFO_CONSTANT);
                    Object craftPlayer = _CRAFTPLAYER_CLASS.cast(player);
                    Object entityPlayer = _GETHANDLE_METHOD.invoke(craftPlayer);
                    Object playerConnection = _PLAYER_CONNECTION_FIELD.get(entityPlayer);
                    _SEND_PACKET_METHOD.invoke(playerConnection, packet);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
    

    I've tested it for 1.12.2 and it works.
     
    Reflxction and Zombie_Striker like this.
  2. Offline

    Zombie_Striker

    @PhantomUnicorns
    Nice util.

    However, the send message could be refined to reduce the amount of repeated lines by only having the thing that changes (the packet instnace) inside of the version check like so:
    Code:
        public static void send(Player player, String message) {
            try {
                Object messageComponent = _A_METHOD.invoke(null,
                        "{\"text\": \"" + ChatColor.translateAlternateColorCodes('&', message) + "\"}");
                Object packet;
                if (_VERSION.contains("1.9")) 
                    packet = _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR.newInstance(messageComponent, (byte) 2);
                else 
                    packet = _PACKET_PLAY_OUT_CHAT_CONSTRUCTOR.newInstance(messageComponent, _GAME_INFO_CONSTANT);
                 Object craftPlayer = _CRAFTPLAYER_CLASS.cast(player);
                 Object entityPlayer = _GETHANDLE_METHOD.invoke(craftPlayer);
                 Object playerConnection = _PLAYER_CONNECTION_FIELD.get(entityPlayer);
                 _SEND_PACKET_METHOD.invoke(playerConnection, packet);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    (If you wanted, you could also add the version check within the parameters of the newInstance, but I feel that makes it a bit more unreadable.)
     
  3. Offline

    PhantomUnicorns

    Yeah I thought of that but I didn't want a if just in the middle so I decided to base it on two ifs (the if and the else)
     
Thread Status:
Not open for further replies.

Share This Page