Solved Hardcore hearts

Discussion in 'Plugin Development' started by poma123, Jun 21, 2018.

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

    I would like to write a plugin which is sends a 'hardcore gamemode' packet to the client when the player joins to the server. I found lots of tutorials about ProtocolLib, but I don't understand those.

    Can somebody help me?

    Thanks in advance!
     
  2. Offline

    Zombie_Striker

    poma123 likes this.
  3. I've found this wiki, but I do not know how to do it. Can you give me more information to make it clearer?
     
  4. Offline

    Zombie_Striker

    @poma123
    I'm assuming you understand how to listen to packets (if not, read the tutorial github page: https://github.com/aadnk/ProtocolLib)

    What you need to do is listen to the out-going join packet. Then, get the first field that is a byte and set it equal to "0x8".
     
    poma123 likes this.
  5. @Zombie_Striker
    Okay, I tried with this code, but something is wrong and it's not working.
    Code (open)

    Code:
    import org.bukkit.Bukkit;
    import org.bukkit.plugin.java.JavaPlugin;
    
    import com.comphenix.protocol.PacketType;
    import com.comphenix.protocol.ProtocolLibrary;
    import com.comphenix.protocol.ProtocolManager;
    import com.comphenix.protocol.events.ListenerPriority;
    import com.comphenix.protocol.events.PacketAdapter;
    import com.comphenix.protocol.events.PacketEvent;
    
    public class Main extends JavaPlugin {
        private ProtocolManager protocolManager;
    
        @Override
        public void onEnable() {
            protocolManager = ProtocolLibrary.getProtocolManager();
            protocolManager.addPacketListener(new PacketAdapter(this, ListenerPriority.NORMAL, PacketType.Play.Server.LOGIN) {
                @Override
                public void onPacketSending(PacketEvent e) {
                    if (e.getPacketType().equals(PacketType.Play.Server.LOGIN)) {
                        e.getPacket().getBytes().write(0x8, (byte) 1);
                     
                 
                    }
                }
            });
        }
    }
     
  6. Offline

    CommonSenze

    @poma123
    What I think @Zombie_Striker is saying is you get the first bite and set it to 0x8.

    So you would write(0, (byte) 0x8) because the write method has the first argument a int as the place of what field to edit (So you want to get the first which, in java, is 0) and the second argument taking in the byte you want to set it to.
     
    poma123 likes this.
  7. @CommonSenze
    The same exception on the 'e.getPacket().getBytes().write(0, (byte) 0x8);' line:
    com.comphenix.protocol.reflect.FieldAccessException: No field with type byte exists in class PacketPlayOutLogin.
     
  8. Offline

    CommonSenze

    @poma123
    I just did a LOT of research, looking into the spigot jar itself and learned that there is a boolean that deals with determining if it is in hardcore mode or not. The field name is b and it takes in the byte and checks if it is 0x8.

    I never used reflection personally but try this and see if it works in your login packet call.
    Code:java
    1. try {
    2. Field field = PacketPlayOutLogin.class.getField("b");
    3. field.setAccessible(true);
    4. field.set(PacketPlayOutLogin.class, true);
    5. field.setAccessible(!field.isAccessible());
    6. } catch (NoSuchFieldException e) {
    7. // TODO Auto-generated catch block
    8. e.printStackTrace();
    9. } catch (SecurityException e) {
    10. // TODO Auto-generated catch block
    11. e.printStackTrace();
    12. // TODO Auto-generated catch block
    13. e.printStackTrace();
    14. // TODO Auto-generated catch block
    15. e.printStackTrace();
    16. }
     
    poma123 likes this.
  9. @CommonSenze
    Well, it didn't work :(.
    java.lang.NoSuchFieldException: b at java.lang.Class.getField(Class.java:1703)

    Code:
    @Override
                public void onPacketSending(PacketEvent event) {
                    if (event.getPacketType().equals(PacketType.Play.Server.LOGIN)) {
              
                        try {
                            Field field = PacketPlayOutLogin.class.getField("b");
                            field.setAccessible(true);
                            field.set(PacketPlayOutLogin.class, true);
                            field.setAccessible(!field.isAccessible());
                        } catch (NoSuchFieldException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (SecurityException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalArgumentException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                      
                  
                    }
                }
     
  10. Offline

    CommonSenze

    @poma123
    Hmm. It may be a different version of what spigot I looked at. May I ask what version your server runs?
     
    poma123 likes this.
  11. @CommonSenze
    Of course. git-Paper-1436 (MC: 1.12.2)
     
  12. Offline

    Zombie_Striker

    @CommonSenze
    You don't need to use NMS for this. That is why PL exists: To have wrappers that can work for any update without needing NMS or reflection.

    @poma123
    It seems I was mistaken for what you need to do. Yes, the packet requires a byte, but it seems the wrapper uses booleans to determine if it is hardcore (which is then converted to a byte). Instead of writing the value as a byte, get the booleans and set the flag at 0 equal to true (i.e, to set "isHardcore" to true) using getBooleans().write(0, true)
     
    poma123 likes this.
  13. Last edited: Jun 21, 2018
  14. Offline

    CommonSenze

    @Zombie_Striker
    Thank you for helping me with that. I would've never thought of that.
     
  15. @Zombie_Striker
    It has not printed an error at this time, but it does not work.
    Code (open)

    Code:java
    1. import org.bukkit.plugin.java.JavaPlugin;
    2.  
    3. import com.comphenix.protocol.PacketType;
    4. import com.comphenix.protocol.ProtocolLibrary;
    5. import com.comphenix.protocol.ProtocolManager;
    6. import com.comphenix.protocol.events.ListenerPriority;
    7. import com.comphenix.protocol.events.PacketAdapter;
    8. import com.comphenix.protocol.events.PacketEvent;
    9.  
    10. public class Main extends JavaPlugin {
    11. private ProtocolManager protocolManager;
    12.  
    13. @Override
    14. public void onEnable() {
    15. protocolManager = ProtocolLibrary.getProtocolManager();
    16. protocolManager.addPacketListener(new PacketAdapter(this, ListenerPriority.NORMAL, PacketType.Play.Server.LOGIN) {
    17. @Override
    18. public void onPacketSending(PacketEvent event) {
    19. if (event.getPacketType().equals(PacketType.Play.Server.LOGIN)) {
    20. event.getPacket().getBooleans().write(0, true);
    21.  
    22.  
    23. }
    24. }
    25. });
    26. }
    27. }

     
  16. Offline

    Zombie_Striker

    @poma123
    By any chance, does doing this remove information from the debug screen? If so, the boolean may be used to represent the reduce-debug-info tag and not the hardcore flag.

    If this is the case, it may need to look through all of the variables stored for the packet. Use packet#getModifier() to get all of the objects stored by the packet, and compare the packet sent when not in hard-core mode with the packet sent when in hardcore mode.
     
  17. Offline

    CommonSenze

    @Zombie_Striker
    Just so you can see what I see and not think blindly here's the PacketPlayOutLogin class for 1.12
    Code:java
    1. public class PacketPlayOutLogin
    2. implements Packet<PacketListenerPlayOut>
    3. {
    4. private int a;
    5. private boolean b;
    6. private EnumGamemode c;
    7. private int d;
    8. private EnumDifficulty e;
    9. private int f;
    10. private WorldType g;
    11. private boolean h;
    12. public PacketPlayOutLogin() {}
    13. public PacketPlayOutLogin(int paramInt1, EnumGamemode paramEnumGamemode, boolean paramBoolean1, int paramInt2, EnumDifficulty paramEnumDifficulty, int paramInt3, WorldType paramWorldType, boolean paramBoolean2)
    14. {
    15. this.a = paramInt1;
    16. this.d = paramInt2;
    17. this.e = paramEnumDifficulty;
    18. this.c = paramEnumGamemode;
    19. this.f = paramInt3;
    20. this.b = paramBoolean1;
    21. this.g = paramWorldType;
    22. this.h = paramBoolean2;
    23. }
    24. public void a(PacketDataSerializer paramPacketDataSerializer)
    25. throws IOException
    26. {
    27. this.a = paramPacketDataSerializer.readInt();
    28.  
    29. int i = paramPacketDataSerializer.readUnsignedByte();
    30. this.b = ((i & 0x8) == 8);
    31. i &= 0xFFFFFFF7;
    32. this.c = EnumGamemode.getById(i);
    33.  
    34. this.d = paramPacketDataSerializer.readInt();
    35. this.e = EnumDifficulty.getById(paramPacketDataSerializer.readUnsignedByte());
    36. this.f = paramPacketDataSerializer.readUnsignedByte();
    37. this.g = WorldType.getType(paramPacketDataSerializer.e(16));
    38. if (this.g == null) {
    39. this.g = WorldType.NORMAL;
    40. }
    41. this.h = paramPacketDataSerializer.readBoolean();
    42. }
    43. public void b(PacketDataSerializer paramPacketDataSerializer)
    44. throws IOException
    45. {
    46. paramPacketDataSerializer.writeInt(this.a);
    47. int i = this.c.getId();
    48. if (this.b) {
    49. i |= 0x8;
    50. }
    51. paramPacketDataSerializer.writeByte(i);
    52. paramPacketDataSerializer.writeInt(this.d);
    53. paramPacketDataSerializer.writeByte(this.e.a());
    54. paramPacketDataSerializer.writeByte(this.f);
    55. paramPacketDataSerializer.a(this.g.name());
    56. paramPacketDataSerializer.writeBoolean(this.h);
    57. }
    58. public void a(PacketListenerPlayOut paramPacketListenerPlayOut)
    59. {
    60. paramPacketListenerPlayOut.a(this);
    61. }
    62. }

    As you can see in these lines (29 & 30):
    Code:java
    1. int i = paramPacketDataSerializer.readUnsignedByte();
    2. this.b = ((i & 0x8) == 8);

    boolean b checks i, an imported byte brought by the PacketDataSerializer class, and is converted and compared to the int 8 (By the way, if int i is 0x8 in bytes, when it goes through the process of "& 0x8", it does equal 8 so it will be true).

    That's why I did the things I did about checking for that field. But there you go. Just want to allow you to see the class to see if you have a better way of solving this and also give you some insight.
     
    Zombie_Striker likes this.
  18. Offline

    Zombie_Striker

    @poma123
    Code:
    for(int i = 0; i < 50;i++){
    try{
    Bukkit.broadcastMessage(""+PACKET.getModifier().Safely(i));
    }catch(Error|Exception e){
    break;}
    }
    This will try to print out all of the variables that the modifier contains. This is really hacky, but it should be enough to print out everything.

    Add this somewhere to the code (changing PACKET to the actual packet) and run the code for when the server is and is not in hardcore mode and see which variable changes.
     
  19. @Zombie_Striker
    Let's see. I changed the server.properties file 'hardcore' section all two times.

    hardcore=false (open)

    Code:
    10
    true
    SURVIVAL
    0
    HARD
    20
    net.minecraft.server.v1_12_R1.WorldType@41bdec8f
    false


    hardcore=true (open)

    Code:
    10
    true
    SURVIVAL
    0
    HARD
    20
    net.minecraft.server.v1_12_R1.WorldType@31ab97f4
    false


    @Zombie_Striker
    Okay. I tried the packet#getBooleans().write(0, true); on a simple server, and it worked! The problem is the bungeecord, because the PacketPlayOutLogin packet doesn't work properly on server switch.
    (I have two test servers:
    - Server1 - There isn't hardcorehearts plugin.
    - Server2 - There is hardcorehearts plugin.
    Now. If I connect to Server2 and then switch to Server1 the hardcore hearts still there. But if I connect to Server1 and then switch to Server2 hearts are the same like before: no hardcore hearts.)
    :/
    How can I fix this?
     
    Last edited: Jun 22, 2018
  20. Offline

    CommonSenze

    @poma123
    You might have to listen to that packet of the server switch and send a packet that involves the hardcore setter again.

    Now I don't know how to code that specifically unless you use reflection as I did before but I feel like that would be the answer.
     
    poma123 likes this.
  21. @CommonSenze
    Would you help me, if I would use reflection as you before?
     
  22. Offline

    Zombie_Striker

    @poma123
    The Login packet is only sent once, right when the player joins. You cannot change the hearts while the player is online.

    If you want the heardcore hearts to be displayed, you may need to use this plugin on the Bungeecord-core server, as that seems to be the only one that will handle the joining packet.
     
    poma123 likes this.
Thread Status:
Not open for further replies.

Share This Page