the sent packet breaks the elytra (ProtocolLib)

Discussion in 'Plugin Development' started by LIMPIX31, Jun 25, 2021.

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

    LIMPIX31

    I am creating a plugin, it is a team system (creating a team, invitations, etc.)
    I want all team members to be highlighted (like a spectral arrow) for each team member. I found a solution using ProtocolLib. I have a list of the Team class, which I iterate over, and I also iterate over all its members so that for each member all other members are highlighted.
    Code:
    public class SpectroThread implements Runnable{
    
        private SpectroTeam plugin = SpectroTeam.getPlugin(SpectroTeam.class);
    
        private boolean running = false;
        int taskId;
    
        public SpectroThread(){
    
        }
    
        public void start() {
            running = true;
            taskId = Bukkit.getScheduler().runTaskTimer(plugin,this,3,3).getTaskId();
        }
    
        public void stop(){
            running = false;
            Bukkit.getScheduler().cancelTask(taskId);
        }
    
        @Override
        public void run() {
            if(running){
    
                for(Team team : SpectroTeam.teams){
                    List<String> members = team.getAllMembers();
                    for(String memberXstring : members){
    
                        Player memberX = Bukkit.getPlayerExact(memberXstring);
    
                        if(memberX != null){
    
                            for(String memberYstring : members){
    
                                Player memberY = Bukkit.getPlayerExact(memberYstring);
    
                                if(memberY != null){
                                    PacketContainer packet = plugin.getProtocol().createPacket(PacketType.Play.Server.ENTITY_METADATA);
                                    packet.getIntegers().write(0, memberY.getEntityId());
                                    WrappedDataWatcher watcher = new WrappedDataWatcher();
                                    WrappedDataWatcher.Serializer serializer = WrappedDataWatcher.Registry.get(Byte.class);
                                    watcher.setEntity(memberY);
                                    watcher.setObject(0, serializer, (byte) (0x40));
                                    packet.getWatchableCollectionModifier().write(0, watcher.getWatchableObjects());
    
                                    try {
                                        plugin.getProtocol().sendServerPacket(memberX, packet);
                                    } catch (InvocationTargetException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    The problem is that when a packet is sent, then all the players flying on the elytra begin to fall, and since the packet is sent every 3 ticks, it is not possible to fly on the elytra. How to fix it? There is also a question about the performance of such a plugin, will it harm a server with a large online of players. For example, if there are 1000 players, then 1000x1000 = 1 000 000. That is, you need to perform 1,000,000 iterations. I have a suspicion that this is not the best solution.

    (Bukkit, ProtocolLib)
    Core: Paper 1.16.5

    Bad English
    Thanks
     
  2. Offline

    LIMPIX31

  3. Offline

    Shqep

    @LIMPIX31
    Yikes for the lack of replies.
    But I think the problem is because you create new DataWatcher objects, so the bit mask for the elytra flying part is not available. You only set the bit mask for the glowing effect to be enabled (0x40), not the elytra flying bit.
    I'd say, check if the player is flying with the elytra, if they are, use a bitwise or and set the bit mask to be:
    Code:Java
    1. // This means the entity is glowing AND using the elytra
    2. value = 0x40 | 0x80;
    3.  
    4. // This means the entity is glowing
    5. value = 0x40;


    EDIT: Oops, semi-colons.

    EDIT 2: I don't really think there's a better way to iterate and give out packets more efficiently. Either because there's no solutions, or because I'm bad. Most likely the latter.
    Quadratic time complexity (what you currently have) is probably the best here. Since each player needs to see the other members of their teams glowing, and the others have to see the player glowing.
    If there's a solution in linear time, please tell me too ;P

    EDIT 3: Oops, didn't read the post close enough. Do you have to send packets that frequently? Why not just only when another player joins, leaves or similar events?
     
    Last edited: Jun 29, 2021
  4. Offline

    LIMPIX31

    Thanks

    1
    Found it
    https://wiki.vg/Entity_metadata#Entity_Metadata_Format
    It looks like I should check the rest of this list too

    0x01 - Is on fire
    0x02 - Is crouching
    0x04 - Unused (previously riding)
    0x08 - Is sprinting
    0x10 - Is swimming
    0x20 - Is invisible
    0x40 - has glowing effect
    0x80 - Is flying with an elytra

    2
    I have to highlight all team members, the glow effect lasts a little, so I have to send it often

    EDIT:
    What should I do for this (code)?
     
    Last edited: Jun 30, 2021
  5. Offline

    Shqep

     
  6. Offline

    LIMPIX31

    how to check if a player is flying
     
  7. Offline

    Shqep

    A quick google search led to this.
     
  8. Offline

    LIMPIX31

    I have to iterate over many combinations in order to check the entire list, whether it is possible to set the value automatically, I do not understand bitwise operators.
     
  9. Offline

    Shqep

    @LIMPIX31
    I kinda don't understand what you're going at. Iterate what combinations to check what list, and set what value automatically?

    Bitwise operators are just operations done on literal bits.
    Grossly simplified bitwise operators (open)

    Bitwise AND:
    Code:
       0101
    &  0110
       ----
       0100
    Bitwise AND returns 1 if BOTH bits are 1, and 0 otherwise.

    Bitwise OR:
    Code:
       0010
    |  0001
       ----
       0011
    Bitwise OR returns 1 if either of the bits is 1, and 0 if none are 1.

    There are a few more types but you can look them up.


    We use bitwise operations here because if you notice, in the entity metadata bit mask table, the values are 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40 and 0x80 (which are 1, 2, 4, 8, 16, 32, 64 and 128), and every value is 2 raised to some power.
    In binary this would be numbers along the lines of "1" followed by some zeroes.
    The entity metadata values are represented as 8 bits:
    00000000

    Each bit corresponds to each value. The server uses bitwise ORs to check whether that bit is 1, or "enabled".

    That probably didn't make too much sense. You have to tinker around with bitwise operators until you get it.
     
  10. Offline

    LIMPIX31

    How do I set the mask by checking these values?
     
  11. Offline

    Shqep

    what?
     
  12. Offline

    LIMPIX31

    I need a bit mask based on the values in this list
    0x01 - Is on fire
    0x02 - Is crouching
    0x04 - Unused (previously riding)
    0x08 - Is sprinting
    0x10 - Is swimming
    0x20 - Is invisible
    0x40 - has glowing effect
    0x80 - Is flying with an elytra
     
  13. Offline

    Shqep

    @LIMPIX31
    Huh?? I said this for the third time already?
     
  14. Offline

    LIMPIX31

    I need a bit mask all over the list. How should I do it?
    0x01 - Is on fire
    0x02 - Is crouching
    0x04 - Unused (previously riding)
    0x08 - Is sprinting
    0x10 - Is swimming
    0x20 - Is invisible
    0x40 - has glowing effect
    0x80 - Is flying with an elytra
     
  15. Offline

    Shqep

    What do you even mean a bit mask all over the list?? I'm legit so lost.
    It's fine to not know or grasp properly a concept, like bitwise operators. But you gotta say something specific and something that makes sense so I can attempt to help dude.
     
  16. Offline

    LIMPIX31

    It's hard to explain. You see the list I wrote. I need a value of 0x?? The player can burn, glow, fly, swim, sprint.
     
  17. Offline

    Shqep

    0x??
    ??
    what??

    If you want that the player is burning, glowing, flying, swimming and sprinting at the same time it's:
    0x01 | 0x40 | 0x80 | 0x10 | 0x08.
     
  18. Offline

    LIMPIX31

    But how do I know what it does at the same time, imagine how many combinations I need to check
     
  19. Offline

    Shqep

    @LIMPIX31
    Ok, tell me exactly what you want to do. What are you trying to achieve? What what does at the same time? What combinations to check?

    It's hard to comprehend when you only type a single sentence about your problem vaguely. Elaborate?

    EDIT:
    After a bit of digging, it seems like the player only glows for a short time because other packets override and reset. If you don't want to keep sending the packets like that, you can intercept all incoming EntityMetadata packets to make sure the bit for glowing doesn't get reset.
    This seems a bit better than iterating with quadratic time complexity every 3 seconds or so.
     
    Last edited: Jul 9, 2021
  20. Offline

    LIMPIX31

    :-(

    The player can burn, swim, sprint and fly. For example:
    burn = 1
    swim = 2
    sprint = 3
    fly = 4
    If a player burns and flies it is 14, if he sprints and swims it is 32, and if all at once it is 1234. A player can send a packet with 256 combinations. In the case of maincraft it is 16777216 combinations. How do I set the bit mask if there are so many combinations.

    EDIT: On the interception of the packet in more detail
     
  21. Offline

    Shqep

    @LIMPIX31
    Ok, I think I'm getting where you're wrong at.
    Computers deal with bits, that mean 0s and 1s, not decimal numbers.
    Bit masks (open)

    Code:
    0x01 - Is on fire
    0x02 - Is crouching
    0x04 - Unused (previously riding)
    0x08 - Is sprinting
    0x10 - Is swimming
    0x20 - Is invisible
    0x40 - has glowing effect
    0x80 - Is flying with an elytra
    


    If you put convert these hexadecimal values to binary, you get:
    Binary bit masks (open)

    Code:
    0x01 -> 00000001
    0x02 -> 00000010
    0x04 -> 00000100
    0x08 -> 00001000
    0x10 -> 00010000
    0x20 -> 00100000
    0x40 -> 01000000
    0x80 -> 10000000
    


    As you can see, when you put these in a byte, which is exactly 8 bits to hold the metadata value for the player. So when the packet is sent, all metadata values needed can be sent in just 1 byte. When you want to set the metadata values, you set them in multiple bits. For example, if the player is SPRINTING and BURNING at the same time, we want the 8th bit and the 5th bit to be 1s.
    So we use a bitwise OR to combine these options:
    Code:
    0x01 | 0x08
    which is:
    
       00000001
    |  00001000
       --------
       00001001
    
    You already know, bitwise OR returns 1 if either of the bits is 1, and only returns 0 if both are 0. You can see now we have 8 bits for the metadata value, that has 1s for the "sprinting bit" and the "burning bit". That means the player is both SPRINTING and BURNING at the same time.

    I'm guessing this is how the server checks the player's data value. Let's say if the server wants to check if the player is SPRINTING, for example, it will check the 5th bit to see if it's 1 or 0. This is done with a bitwise AND.
    Code:
    int value = 0x01 | 0x08; // Our value, which is, again, sprinting & burning.
    int checking = value & 0x08; // Checking the "sprinting bit".
    
       00001001 (our value)
    &  00001000 (0x08 in binary)
       --------
       00001000
    
    You can see that this will only return a binary number that's not 0, if and only if the 5th bit is set to 1, which is the sprinting bit.

    I tried my best to explain, if you still couldn't understand. You can: pinpoint exactly where you didn't get, or just lookup other tutorials. Others can explain better than I can.

    And about intercepting, if you only send one metadata packet, when the server sends another one, it's going to OVERRIDE your glowing status. You want to add a packet listener, catch all Metadata packets being sent by the server, and put your glowing stuff in it.
    If you don't know how, look up ProtocolLibrary tutorials. I'm not going to explain it as well as them.
     
Thread Status:
Not open for further replies.

Share This Page