Util [Class] Overcomming the Action Bar problem.

Discussion in 'Resources' started by mine-care, Sep 26, 2015.

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

    mine-care

    Hello bukkit comunity.
    In recent versions of Bukkit alternatives (1.8.3+) trying to use the Version independed Action bar setting code doesnt quite work, and that is because the ChatSerialiser class throws a ClassNotFoundException when looked up by Class.forName(String param);

    So i made a class that overcomes that and it is still version independed! :D (Warning, if a class from the NMS packages changes, it will break.)

    The class can be found here:


    Code:
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;
    
    /**
    * @author fillpant AKA mine-care
    * <h1>Info:</h1> This is a utility class to overcome the
    * problem that occurs in the latest versions (1.8.3+) where
    * ChatSerialiser class cannot be found.
    */
    public class ActionBarUtil {
        private static final String BV = Bukkit.getServer().getClass().getPackage().getName().substring(23);
        private static boolean initialised = false;
        private static Constructor<?> chatSer;
        private static Constructor<?> packetChat;
        private static Method getPlayerHandle;
        private static Field playerConnection;
        private static Method sendPacket;
    
        static {
            try {
                chatSer = ReflectionUtils.getConstructor(
                        Class.forName("net.minecraft.server." + BV
                                + ".ChatComponentText"), String.class);
    
                packetChat = Class.forName(
                        "net.minecraft.server." + BV + ".PacketPlayOutChat")
                        .getConstructor(
                                Class.forName("net.minecraft.server." + BV
                                        + ".IChatBaseComponent"), byte.class);
                getPlayerHandle = Class.forName(
                        "org.bukkit.craftbukkit." + BV + ".entity.CraftPlayer")
                        .getDeclaredMethod("getHandle");
                playerConnection = Class.forName(
                        "net.minecraft.server." + BV + ".EntityPlayer")
                        .getDeclaredField("playerConnection");
                sendPacket = Class.forName(
                        "net.minecraft.server." + BV + ".PlayerConnection")
                        .getDeclaredMethod(
                                "sendPacket",
                                Class.forName("net.minecraft.server." + BV
                                        + ".Packet"));
                initialised = true;
            } catch (ReflectiveOperationException e) {
                e.printStackTrace();
                Bukkit.getServer()
                        .getLogger()
                        .warning(
                                "Cannot initialise Action Bar Utils (Blame fillpant)");
                initialised = false;
            }
        }
    
        /**
         * This method checks if the ActionBarUtil has been initialised properly
         * @return returns a boolean indicating initialization.
         */
        public static boolean isInitialised(){
            return initialised;
        }
       
        /**
         * This method sends an ActionBar message to a player, ChatColor works as well.
         * @param message The message to be sent to the player(s).
         * @param p The player(s) to receive the ActionBar message.
         * @return Returns a boolean indicating if the method ececution was successfull.
         * <br><b>NOTE: If anything goes wrong the utility will automatically
         *  stop working to prevent massive ammouts of errors showing up in console.</b>
         */
        public static boolean sendActionBar(String message, Player... p) {
            if (!initialised) return false;
            try {
                Object o = chatSer.newInstance(message);
                Object packet = packetChat.newInstance(o, (byte) 2);
                sendPacket(packet, p);
            } catch (ReflectiveOperationException e) {
                e.printStackTrace();
                initialised = false;
            }
            return initialised;
        }
    
        /**
          * IGNORE THIS!
          */
        private static void sendPacket(Object packet, Player... pl)
                throws ReflectiveOperationException {
            for (Player p : pl) {
                Object entityplayer = getPlayerHandle.invoke(p);
                Object PlayerConnection = playerConnection.get(entityplayer);
                sendPacket.invoke(PlayerConnection, packet);
            }
        }
    }
    Example usage:
    Code:
    Player p = ...;
    ActionBarUtil.sendActionBar(ChatColor.GOLD+"This is an action bar "+ChatColor.GREEN+" message!",p);
    
    Alternatively, you can also use this method with varargs,
    Code:
    Player p  = ...;
    Player o  = ...;
    ActionBarUtil.sendActionBar(ChatColor.GOLD+"This is an action bar "+ChatColor.GREEN+" message!",p,o);
    
    For any questions you may have let me know!
     
    Orange Tabby and ChipDev like this.
  2. Offline

    ChipDev

    I love how your tutorials are getting better and better!
    +1(00)
     
  3. Offline

    mine-care

  4. @mine-care
    I don't wanna ruin your moment but you can actually retrieve the class using net.minecraft.server.<version>.IChatBaseComponent$ChatSerializer ;p
     
  5. Offline

    mine-care

    @megamichiel to be Honnest I noticed it has become a nested class in between versions so I decided not to use it for my project since it is used by mojang a lot and they obviously can change it again. On the other hand, chatcomponenttect as you can see from early versions doesn't seem to change packages or names. So I thought I would go with it.
    You are not ruining the moment, instead you are saying something very correct! I should have mentioned.

    I based this thread to the latest reports of "ClassNotFoundException" with ChatSerialiser to get the attention of people, I could name it "Version independed action bar message" but it wouldn't be catchy to the ones tying to fix the CNFE :/

    Thanks for your post :)
     
  6. Offline

    teej107

    @mine-care This is why you don't use reflection and use abstraction instead.
     
    timtower likes this.
  7. Offline

    mine-care

    @teej107 I agree with you but abstraction has to be updated for every single version of CB and the snippet using reflection only has to be updated once a change is made and it breaks.
    Plus as many people would say, i am a reflection nerd :p
     
  8. mine-care likes this.
Thread Status:
Not open for further replies.

Share This Page