Glow Packets per player - help

Discussion in 'Spigot Plugin Development' started by abandoncaptian, Jul 19, 2017.

  1. I'm in the process of coding a massive minigame for a network, I have a team functionality and I want to make it so when the round starts, your teammates glow say for example GREEN, but the enemy team doesn't see any glow. This is to make it easier for you to tell your teammate from your enemies. I know very little knowledge about working with packets. I looked around for a few days looking for a Glow API that worked on 1.12 and wasn't buggy but nothing. I did find a forum post to try to understand it but I'm still having issues.

    Links I used to attempt at solving this:
    https://www.spigotmc.org/resources/api-glowapi-1-9-1-10.19422/
    https://bukkit.org/threads/glowing-for-one-person.446790/
     
  2. FrostedSnowman

    Resource Staff

  3. FrostedSnowman

    Resource Staff

    eh, just referenced the link since they did the same over on the protocol wiki :p
     
  4. @FrostedSnowman I know that way but i only want the glow to be seen by teammates and not enemies.
     
  5. FrostedSnowman

    Resource Staff

    do you know how packets work?
     
  6. i somewhat know how they work
     
  7. You send the packets to those who you want to see the effect.
     
  8. ok can u explain like how would i send these packets and when i don't want the glow effect on a player it removes it
    .
     
  9. This feature is very important, any help would be much appreciated.
     
  10. Code (Text):
    PotionEffect effect = new PotionEffect(PotionEffectType.GLOWING,200,1);
    Pack.setEntityGlow(p,effect);
    Code (Text):
    public static void setEntityGlow(Player p, PotionEffect effect){
            Main plugin = Main.instance;
            PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_EFFECT);
            int effectID = effect.getType().getId();
            int amplifier = effect.getAmplifier();
            int duration = effect.getDuration();
            int entityID = p.getEntityId();
            packet.getIntegers().write(0,entityID);
            packet.getBytes().write(0,(byte) effectID);
            packet.getBytes().write(1,(byte) amplifier);
            packet.getIntegers().write(1,duration);
            packet.getBytes().write(2,(byte) 0);
            try {
                plugin.pm.sendServerPacket(p,packet,false);
            }
            catch (InvocationTargetException error){
                error.printStackTrace();
            }
        }
    Although the packet is no problem, but no effect
    @FrostedSnowman
    Look:
    [​IMG]
    [​IMG]
     
  11. Ok I'll see what I can do, if I find something I'll let you know.

    Edit: From the looks of it, I'm trying different things with it and you are missing something that sends the packet to the player that is supposed to glow and a packet to the players you want to see the glow.
     
    #12 abandoncaptian, Aug 1, 2017
    Last edited: Aug 1, 2017
  12. You’ll need to use PacketPlayOutEntityMetadata for this. I believe the PacketPlayOutEntityEffect is simply for the player to see what effects they have in their inventory.
    EntityMetadata packet takes an entity ID, a data watcher object, and a Boolean (which I believe is wether to update the entities metadata immediately, but I’m not sure... just set it to true).
    Now to change the entity metadata for the glowing effect, you can use this table to see the indexes and their meaning for the bit shifting that occurs in the NMS code. I’m on my phone so I don’t know the code exactly, but it will be something like this:
    Code (Text):
    // Dont use     entityPlayer.setFlag(6, true); // sets the data watcher object’s data internally
    Above I was wrong :P
    See below post


    PacketPlayOutEntityMetadata data = new PacketPlayOutEntityMetadat(entityPlayer.getEntityId(), entityPlayer.getDataWatcher(), true);
    // send the packet here.
    If that doesn’t work, I can get on my computer later and check it out.
     
    #13 Petersoj, Aug 1, 2017
    Last edited: Aug 1, 2017
  13. How would I use something like this to send these packets to players where only certain players can see the glow on other certain players, and when I want the glow to stop, how do I make it the glow stop working on certain players.
     
  14. Okay this is spoon feeding, but I kinda needed this method for my own project too. So I will share the love. ;)
    Code (Text):
    @SuppressWarnings("unchecked")
        public void setGlowing(Player glowingPlayer, Player sendPacketPlayer, boolean glow) {
            try {
                EntityPlayer entityPlayer = ((CraftPlayer) glowingPlayer).getHandle();

                DataWatcher dataWatcher = entityPlayer.getDataWatcher();

                entityPlayer.glowing = glow; // For the update method in EntityPlayer to prevent switching back.

                // The map that stores the DataWatcherItems is private within the DataWatcher Object.
                // We need to use Reflection to access it from Apache Commons and change it.
                Map<Integer, DataWatcher.Item<?>> map = (Map<Integer, DataWatcher.Item<?>>) FieldUtils.readDeclaredField(dataWatcher, "d", true);

                // Get the 0th index for the BitMask value. http://wiki.vg/Entities#Entity
                DataWatcher.Item item = map.get(0);
                byte initialBitMask = (Byte) item.b(); // Gets the initial bitmask/byte value so we don't overwrite anything.
                byte bitMaskIndex = (byte) 0x40; // The index as specified in wiki.vg/Entities
                if (glow) {
                    item.a((byte) (initialBitMask | 1 << bitMaskIndex));
                } else {
                    item.a((byte) (initialBitMask & ~(1 << bitMaskIndex))); // Inverts the specified bit from the index.
                }

                PacketPlayOutEntityMetadata metadataPacket = new PacketPlayOutEntityMetadata(glowingPlayer.getEntityId(), dataWatcher, true);

                ((CraftPlayer) sendPacketPlayer).getHandle().playerConnection.sendPacket(metadataPacket);
            } catch (IllegalAccessException e) { // Catch statement necessary for FieldUtils.readDeclaredField()
                e.printStackTrace();
            }
        }
     
    #15 Petersoj, Aug 1, 2017
    Last edited: Aug 2, 2017
  15. This makes way more sense than what I was finding out with my research. I'm going to try it out

    Edit: Now if I wanted to change the color of the glow could you point me to where I could solve this.
     
    • Like Like x 1
  16. To change the color of the glow you need to create scoreboard teams and give them a color.
     
    • Agree Agree x 1
  17. ok, thank you.
    Also quick question, when ur close to another player you aren't supposed to see glow for, u see them glow. how do I fix this? I don't want players to see the enemy team because they glow at close range.
     
  18. Okay, I see the issues with the code I gave you.
    entityPlayer.glowing = glow; will cause the next tick update for Entity to set the player glowing for everyone on the server (send PacketPlayOutEntityMetadata to all players on the server), so I removed this line. But now, on the tick update for entityPlayer, it will check if the dataWatcher has the 0 index, bitmask 6 (0x40), set to 1 (aka true) and will remove this and remove the glowing effect from the player (so you will only see the glowing effect for 1 tick).
    So here is my final attempt:
    Code (Text):
    @SuppressWarnings("unchecked")
        public void setGlowing(Player glowingPlayer, Player sendPacketPlayer, boolean glow) {
            try {
                EntityPlayer entityPlayer = ((CraftPlayer) glowingPlayer).getHandle();

                DataWatcher toCloneDataWatcher = entityPlayer.getDataWatcher();
                DataWatcher newDataWatcher = new DataWatcher(entityPlayer);

                // The map that stores the DataWatcherItems is private within the DataWatcher Object.
                // We need to use Reflection to access it from Apache Commons and change it.
                Map<Integer, DataWatcher.Item<?>> currentMap = (Map<Integer, DataWatcher.Item<?>>) FieldUtils.readDeclaredField(toCloneDataWatcher, "d", true);
                Map<Integer, DataWatcher.Item<?>> newMap = Maps.newHashMap();

                // We need to clone the DataWatcher.Items because we don't want to point to those values anymore.
                for (Integer integer : currentMap.keySet()) {
                    newMap.put(integer, currentMap.get(integer).d()); // Puts a copy of the DataWatcher.Item in newMap
                }

                // Get the 0th index for the BitMask value. http://wiki.vg/Entities#Entity
                DataWatcher.Item item = newMap.get(0);
                byte initialBitMask = (Byte) item.b(); // Gets the initial bitmask/byte value so we don't overwrite anything.
                byte bitMaskIndex = (byte) 6; // The index as specified in wiki.vg/Entities
                if (glow) {
                    item.a((byte) (initialBitMask | 1 << bitMaskIndex));
                } else {
                    item.a((byte) (initialBitMask & ~(1 << bitMaskIndex))); // Inverts the specified bit from the index.
                }

                // Set the newDataWatcher's (unlinked) map data
                FieldUtils.writeDeclaredField(newDataWatcher, "d", newMap, true);

                PacketPlayOutEntityMetadata metadataPacket = new PacketPlayOutEntityMetadata(glowingPlayer.getEntityId(), newDataWatcher, true);

                ((CraftPlayer) sendPacketPlayer).getHandle().playerConnection.sendPacket(metadataPacket);
            } catch (IllegalAccessException e) { // Catch statement necessary for FieldUtils.readDeclaredField()
                e.printStackTrace();
            }
        }
    BUT, the problem here is that as soon as another PacketPlayOutEntityMetaData is sent to players on the server, it will override the glowing effect because our newDataWatcher is not tied to the current datawatcher.
    So really the only way to go about this is, packet listening with ProtocolLib or using a simple netty injector. Like the one used in the thread you gave in your first post.
    Or a really hacky way would be to use the setGlowing() method everytime there is a PacketPlayOutEntityMetaData event. Such as PlayerToggleSprintEvent or PlayerToggleSneakEvent and such.
     
  19. What you're saying is that this only lasts for 1 tick? or that you can only allow 1 player to glow at a time