Solved Getting UseEntity Packet

Discussion in 'Spigot Plugin Development' started by Colink02, Feb 4, 2020.

  1. I'm trying to get when a player clicks on a Packet Only Entity (NPC Player) so when I created this:
    Code (Java):
            ChannelDuplexHandler cdh = new ChannelDuplexHandler() {

                @Override
                public void channelRead(ChannelHandlerContext context, Object Obj) throws Exception {
                    if(Obj instanceof PacketPlayInUseEntity) {
                        PacketPlayInUseEntity entityHandler = (PacketPlayInUseEntity) Obj;
                        for(Field s: Obj.getClass().getFields()) {
                            System.out.println(s.toGenericString());
                        }
                    }
                    super.channelRead(context, Obj);
                }

            };
            ChannelPipeline pipeline = ((CraftPlayer) player).getHandle().playerConnection.networkManager.channel.pipeline();
            pipeline.addBefore("packet_handler", player.getName(), cdh);
    I can't seem to get the Fields of the Packet or the entity id. I'm not sure what field I need or what I'm doing incorrectly. I've tried field "eid", "target", and "Target".
    I stopped using Citizens2 NPCs because I wanted more control over the entities. I was think about using ProtocolLib but I'm not fully understanding how that works. So if anyone could help me with getting the packets and checking if the npc equals the npc created from my plugin that'd be great :D
    For check if its the right npc I'd assume I would check their ID's
    Heres my NPC Class:
    Code (Java):
    package com.colink02;

    import com.mojang.authlib.GameProfile;
    import com.mojang.authlib.properties.Property;
    import net.minecraft.server.v1_15_R1.*;
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.Location;
    import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
    import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
    import org.bukkit.craftbukkit.v1_15_R1.entity.CraftLivingEntity;
    import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.event.entity.CreatureSpawnEvent;

    import java.util.UUID;

    public class NPC {
        private Location loc;
        private Location lookingAt;
        private String name;
        private GameProfile gameProfile;
        private EntityPlayer entityPlayer;
        private String skin_texture;
        private String skin_texture_signature;
        private PlayerInteractManager interactManager;


        public NPC(Location loc, String name) {
            this.loc = loc;
            this.name = name;
        }
        public NPC spawn() {
            MinecraftServer mcServer = ((CraftServer) Bukkit.getServer()).getServer();
            WorldServer worldServer = ((CraftWorld) loc.getWorld()).getHandle();

            if(this.name.length() > 16) {
                this.name = this.name.substring(0,16);
            }
            this.interactManager = new PlayerInteractManager(worldServer);
            this.gameProfile = new GameProfile(UUID.randomUUID(), ChatColor.translateAlternateColorCodes('&',this.name));
            this.entityPlayer = new EntityPlayer(mcServer,worldServer,gameProfile, this.interactManager);
            if(lookingAt != null) {
                System.out.println("lookingAt is not null " + this.name + " Yaw: " + loc.getYaw() + " Pitch: " + loc.getPitch());
                this.entityPlayer.setLocation(loc.getX(), loc.getY(), loc.getZ(), clampYaw(loc.getYaw()), loc.getPitch());

            } else {
                this.entityPlayer.setLocation(loc.getX(), loc.getY(), loc.getZ(), clampYaw(loc.getYaw()), loc.getPitch());
            }
            this.gameProfile.getProperties().put("textures", new Property("textures", skin_texture, skin_texture_signature));
            return this;
        }
        public NPC faceLocation(Location loc) {
            this.lookingAt = loc;
            return this;
        }
        public void show(Player p) {
            PlayerConnection playerConnection = ((CraftPlayer) p).getHandle().playerConnection;
            playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, this.entityPlayer));
            playerConnection.sendPacket(new PacketPlayOutNamedEntitySpawn(this.entityPlayer));
            playerConnection.sendPacket(new PacketPlayOutEntityHeadRotation(this.entityPlayer, (byte) ((loc.getYaw() * 256.0) / 360.0f)));
            playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER));
        }

        public NPC addSkin(String texture, String texture_sign) {
            this.skin_texture = texture;
            this.skin_texture_signature = texture_sign;
            return this;
        }

        public EntityPlayer getEntityPlayer() {
            return entityPlayer;
        }

        public String getName() {
            return name;
        }
        public void destroy() {
            for(Player p: Bukkit.getOnlinePlayers()) {
                ((CraftPlayer) p).getHandle().playerConnection.sendPacket(new PacketPlayOutEntityDestroy(this.getEntityPlayer().getId()));
            }
        }
        public static float clampYaw(float yaw) {
            while (yaw < -180.0F) {
                yaw += 360.0F;
            }

            while (yaw >= 180.0F) {
                yaw -= 360.0F;
            }
            return yaw;
        }
    }
     
  2. getFields only returns public fields. You need to use getDeclaredFields to get all fields including the private ones. Which in this instance are the fields located inside of PacketPlayInUseEntity.

    the name of the id field changes between updates, so your best bet would just be at the start of your plugin get the first field inside of PacketPlayInUseEntity that is an int. Then store that field somewhere.
     
  3. Ok so how would I get field? When I try to get it says I can't access it which makes sense because its a private field.
     
  4. Field.setAccessible(true)
     
  5. Ok so now I'm getting
    Code (Java):
    ChannelDuplexHandler cdh = new ChannelDuplexHandler() {

                @Override
                public void channelRead(ChannelHandlerContext context, Object Obj) throws Exception {
                    if(Obj instanceof PacketPlayInUseEntity) {
                        PacketPlayInUseEntity entityHandler = (PacketPlayInUseEntity) Obj;
                        Field entityID = Obj.getClass().getDeclaredField("a");
                        System.out.println(entityID);
                        entityID.setAccessible(true);
                        for(NPC npc : __globalNPCs__) {
                            if(npc.getEntityPlayer().getId() == entityID.getInt(entityID)) {
                                System.out.println("Player is " + npc.getName());
                                break;
                            }
                        }
                    }
                    super.channelRead(context, Obj);
                }

            };
     
  6. For the future, I would recommend surrounding everything inside of the channelRead (excluding super.channelRead) inside of a try/catch block. This will stop the player from getting disconnected when you have an error inside of your code, and just broadcast the error to console instead.

    the parameter for getInt is the object you are trying to get the value from. For some reason you are inputting the reflections field for it when that is incorrect. Your if statement should look like this:

    Obj is the incoming packet, and that is the object you want to get the field value from.
    Code (Text):
    if (npc.getEntityPlayer().getId() == entityID.getInt(Obj))
     
  7. Ah that makes more sense. Thank you very much. I can't think right now clearly so imma take a nap lol