Actionbar 1.12

Discussion in 'Plugin Development' started by Minecraft1o1, Aug 18, 2017.

Thread Status:
Not open for further replies.
  1. How to create actionbar in 1.12? It seems like something was changed, because my older actionbar codes don't work.
     
  2. Offline

    Zombie_Striker

  3. @Zombie_Striker I think it's actually your code from the thread. https://bukkit.org/threads/send-hotbar-messages.440664/
    Code:
        /**
         * These are the Class instances. Use these to get fields or methods for classes.
         */
        private static Class<?> CRAFTPLAYERCLASS;
        private static Class<?> PACKET_PLAYER_CHAT_CLASS;
        private static Class<?> ICHATCOMP;
        private static Class<?> CHATMESSAGE;
        private static Class<?> PACKET_CLASS;
        private static Class<?> CHAT_MESSAGE_TYPE;
        /**
         * These are the constructors for those classes. You need these to create new objects.
         */
        private static Constructor<?> PACKET_PLAYER_CHAT_CONSTRUCTOR;
        private static Constructor<?> CHATMESSAGE_CONSTRUCTOR;
        /**
         * This is the server version. This is how we know the server version.
         */
        private static final String SERVER_VERSION;
      
      
        private static Method GET_CHAT_MESSAGE_TYPE;
      
        static {
            /**
             * This gets the server version.
             */
            String name = Bukkit.getServer().getClass().getName();
            name = name.substring(name.indexOf("craftbukkit.") + "craftbukkit.".length());
            name = name.substring(0, name.indexOf("."));
            SERVER_VERSION = name;
            try {
                /**
                 * This here sets the class fields.
                 */
                CRAFTPLAYERCLASS = Class.forName("org.bukkit.craftbukkit."
                        + SERVER_VERSION + ".entity.CraftPlayer");
                PACKET_PLAYER_CHAT_CLASS = Class.forName("net.minecraft.server."
                        + SERVER_VERSION + ".PacketPlayOutChat");
                PACKET_CLASS = Class.forName("net.minecraft.server."
                        + SERVER_VERSION + ".Packet");
                ICHATCOMP = Class.forName("net.minecraft.server." + SERVER_VERSION
                        + ".IChatBaseComponent");
                CHAT_MESSAGE_TYPE = Class.forName("net.minecraft.server." + SERVER_VERSION + ".ChatMessageType");
              
                PACKET_PLAYER_CHAT_CONSTRUCTOR = Optional.of(
                        PACKET_PLAYER_CHAT_CLASS.getConstructor(ICHATCOMP, CHAT_MESSAGE_TYPE)).get();
                CHATMESSAGE = Class.forName("net.minecraft.server."
                        + SERVER_VERSION + ".ChatMessage");
              
                GET_CHAT_MESSAGE_TYPE = CHAT_MESSAGE_TYPE.getDeclaredMethod("a", Byte.TYPE);
                /**
                 * If it cannot find the constructor one way, we try to get the declared constructor.
                 */
                try {
                    CHATMESSAGE_CONSTRUCTOR = Optional.of(
                            CHATMESSAGE
                                    .getConstructor(String.class, Object[].class))
                            .get();
                } catch (NoSuchMethodException e) {
                    CHATMESSAGE_CONSTRUCTOR = Optional.of(
                            CHATMESSAGE.getDeclaredConstructor(String.class,
                                    Object[].class)).get();
                }
            } catch (ClassNotFoundException | NoSuchMethodException
                    | SecurityException e) {
                e.printStackTrace();
            }
        }
        /**
         * Sends the hotbar message 'message' to the player 'player'
         * @param player
         * @param message
         */
        public static void sendHotBarMessage(Player player, String message) {
            try {
                //This creates the IChatComponentBase instance
                Object icb = CHATMESSAGE_CONSTRUCTOR.newInstance(message,
                        new Object[0]);
              
              
                Object cmt = GET_CHAT_MESSAGE_TYPE.invoke(CHAT_MESSAGE_TYPE, (byte) 2);
              
                //This creates the packet
                Object packet = PACKET_PLAYER_CHAT_CONSTRUCTOR.newInstance(icb,
                        cmt);
                //This casts the player to a craftplayer
                Object craftplayerInst = CRAFTPLAYERCLASS.cast(player);
                //This get's the method for craftplayer's handle
                Optional<Method> methodOptional = Optional.of(CRAFTPLAYERCLASS
                        .getMethod("getHandle"));
                //This invokes the method above.
                Object methodhHandle = methodOptional.get().invoke(craftplayerInst);
                //This gets the player's connection
                Object playerConnection = methodhHandle.getClass()
                        .getField("playerConnection").get(methodhHandle);
                //This sends the packet.
                Optional.of(
                        playerConnection.getClass().getMethod("sendPacket",
                                PACKET_CLASS)).get()
                        .invoke(playerConnection, packet);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
     
  4. Offline

    Zombie_Striker

  5. @Minecraft1o1
    The PacketPlayOutChatMessage constructor now takes a ChatMessageType instead of a byte.
     
  6. It's working. It just seems the code never reached the line from which i called the method. :)
     
  7. Offline

    Zombie_Striker

    @Minecraft1o1 @AlvinB
    Fixed. The code in the main thread has been updated to support 1.12+. Copy and paste the code from there into your project so it does not have to be version dependent.
     
  8. Offline

    Horsey

    Or you could use the spigot API and use player#spigot().send message(ChatMessageType, BaseComponent); however it works only on spigot servers.
     
  9. Offline

    Zombie_Striker

    @Horsey
    And is version dependent, as you have to import the NMS classes.
     
  10. Offline

    Horsey

    Is it? In that case there's no advantage to using the spigot API, nevermind.

    EDIT: It isn't? I've looked up all the relevant classes, none of them have the version in their name.
     
    Last edited: Aug 18, 2017
  11. @Horsey
    Yeah, spigot has some neat features, the only problem is that they only run in Spigot servers. To be honest, I don't understand why they cannot just merge Bukkit and Spigot at this point, it's not like they need to convince people to use one over the other.. both are developed by them.
     
  12. Offline

    Horsey

    @AlvinB I believe at this point most servers run Spigot anyway, as it's a lot more optimized.
     
  13. @Horsey
    Well, there are still a decent few servers that run CraftBukkit, and if you use Spigot features, you will have loads of people come asking why the plugin isn't working. I think that Spigot should just merge CraftBukkit and Spigot to make the whole situation easier.
     
  14. Offline

    Horsey

    According to mcStats, only ~11% of servers run on craftbukkit (all MC servers including Forge, Cauldron etc), but that's actually quite a lot of servers, however I doubt any of the server owners
    running CB know what goes on with the internals of their servers, and how Spigot is better than craftbukkit.
     
  15. @Horsey
    Yes, and that's my point. Explaining to a server owner who doesn't know a whole lot about Spigot and CraftBukkit why they have to use a to them completely different server brand just to get this one plugin to work probably won't be very easy.

    If Spigot just merged the two together, everyone would be happier, since no server owners would be running slow builds of minecraft servers, and developers won't do their head in because they only have one server software to worry about, not two.

    EDIT: Also, mcstats is apparently back online.. Only problem remaining is that it's unbearably slow..
     
  16. @Zombie_Striker
    I've just updated to your code and it generates NullPointerException.

    [12:18:12 WARN]: java.lang.NullPointerException
    [12:18:12 WARN]: at package.myplugin.dependencies.Utils.sendHotBar
    Message(Utils.java:91)
    [12:18:12 WARN]: at package.myplugin.messaging.Messenger.sendHotba
    rMessage(Messenger.java:44)
    [12:18:12 WARN]: at package.myplugin.commands.GPEvent$1.run(GPEven
    t.java:192)
    [12:18:12 WARN]: at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftTask.r
    un(CraftTask.java:53)
    [12:18:12 WARN]: at org.bukkit.craftbukkit.v1_12_R1.scheduler.CraftAsyncT
    ask.run(CraftAsyncTask.java:52)
    [12:18:12 WARN]: at java.util.concurrent.ThreadPoolExecutor.runWorker(Unk
    nown Source)
    [12:18:12 WARN]: at java.util.concurrent.ThreadPoolExecutor$Worker.run(Un
    known Source)
    [12:18:12 WARN]: at java.lang.Thread.run(Unknown Source)

    This is my Utils.java
    Utils.java (open)

    package package.myplugin.dependencies;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;

    public class Utils {
    /**
    * These are the Class instances. Use these to get fields or methods for
    * classes.
    */
    private static Class<?> CRAFTPLAYERCLASS, PACKET_PLAYER_CHAT_CLASS,
    ICHATCOMP, CHATMESSAGE, PACKET_CLASS, CHAT_MESSAGE_TYPE_CLASS;

    private static Field PLAYERCONNECTION;
    private static Method GETHANDLE,SENDPACKET;


    /**
    * These are the constructors for those classes. You need these to create
    * new objects.
    */
    private static Constructor<?> PACKET_PLAYER_CHAT_CONSTRUCTOR,
    CHATMESSAGE_CONSTRUCTOR;
    /**
    * Used in 1.12+. Bytes are replaced with this enum
    */
    private static Object CHAT_MESSAGE_TYPE_ENUM_OBJECT;

    /**
    * This is the server version. This is how we know the server version.
    */
    private static final String SERVER_VERSION;
    static {
    // This gets the server version.
    String name = Bukkit.getServer().getClass().getName();
    name = name.substring(name.indexOf("craftbukkit.")
    + "craftbukkit.".length());
    name = name.substring(0, name.indexOf("."));
    SERVER_VERSION = name;

    try {
    // This here sets the class fields.
    CRAFTPLAYERCLASS = Class.forName("org.bukkit.craftbukkit."
    + SERVER_VERSION + ".entity.CraftPlayer");
    PACKET_PLAYER_CHAT_CLASS = Class.forName("net.minecraft.server."
    + SERVER_VERSION + ".PacketPlayOutChat");
    PACKET_CLASS = Class.forName("net.minecraft.server."
    + SERVER_VERSION + ".Packet");
    ICHATCOMP = Class.forName("net.minecraft.server." + SERVER_VERSION
    + ".IChatBaseComponent");
    GETHANDLE = CRAFTPLAYERCLASS.getMethod("getHandle");
    PLAYERCONNECTION = GETHANDLE.getDeclaringClass().getClass()
    .getField("playerConnection");
    SENDPACKET = PLAYERCONNECTION.getDeclaringClass().getMethod("sendPacket", PACKET_CLASS);
    try {
    PACKET_PLAYER_CHAT_CONSTRUCTOR = PACKET_PLAYER_CHAT_CLASS
    .getConstructor(ICHATCOMP, byte.class);
    } catch (NoSuchMethodException e) {
    CHAT_MESSAGE_TYPE_CLASS = Class.forName("net.minecraft.server."
    + SERVER_VERSION + ".ChatMessageType");
    CHAT_MESSAGE_TYPE_ENUM_OBJECT = CHAT_MESSAGE_TYPE_CLASS
    .getEnumConstants()[2];

    PACKET_PLAYER_CHAT_CONSTRUCTOR = PACKET_PLAYER_CHAT_CLASS
    .getConstructor(ICHATCOMP, CHAT_MESSAGE_TYPE_CLASS);
    }

    CHATMESSAGE = Class.forName("net.minecraft.server."
    + SERVER_VERSION + ".ChatMessage");

    CHATMESSAGE_CONSTRUCTOR = CHATMESSAGE.getDeclaredConstructor(
    String.class, Object[].class);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * Sends the hotbar message 'message' to the player 'player'
    *
    * @param player
    * @param message
    */
    public static void sendHotBarMessage(Player player, String message) {
    try {
    // This creates the IChatComponentBase instance
    Object icb = CHATMESSAGE_CONSTRUCTOR.newInstance(message,
    new Object[0]);
    // This creates the packet
    Object packet;
    try {
    packet = PACKET_PLAYER_CHAT_CONSTRUCTOR.newInstance(icb,
    (byte) 2);
    } catch (Exception e) {
    packet = PACKET_PLAYER_CHAT_CONSTRUCTOR.newInstance(icb,
    CHAT_MESSAGE_TYPE_ENUM_OBJECT);
    }
    // This casts the player to a craftplayer
    Object craftplayerInst = CRAFTPLAYERCLASS.cast(player);
    // This invokes the method above.
    Object methodhHandle = GETHANDLE.invoke(craftplayerInst);
    // This gets the player's connection
    Object playerConnection = PLAYERCONNECTION.get(methodhHandle);
    // This sends the packet.
    SENDPACKET
    .invoke(playerConnection, packet);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }



    This seems to be the problem.
    Code:
    Object icb = CHATMESSAGE_CONSTRUCTOR.newInstance(message,
                  new Object[0]);
     
  17. Zombie_Striker likes this.
  18. Offline

    xpMatthewDev

    Code:
    Player player = (Player) sender;
    String bar = "String";
    player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(bar));
    
    works fine here
     
  19. Offline

    Zombie_Striker

    @xpMatthewDev
    But that requires you to import the NMS classes for ChatMessage and for TextComponent, meaning the plugin will be version dependent and break with all other versions. This util, however, will work on all versions that have hotbar messages.
     
  20. @Zombie_Striker
    It actually doesn't. It is part of the spigot API, and uses no NMS. Only problem is that it only works on spigot..
     
Thread Status:
Not open for further replies.

Share This Page