Disguises crashing players

Discussion in 'Spigot Plugin Development' started by Kingbluesapphire, Jun 22, 2018.

  1. I am working on a Spigot 1.8.9 plugin where players can disguise as mobs.

    I disguise the players with the following code (Not my code I got it from util thread but cannot find it to link)

    Code (Java):
    public class Disguise {

    private static final String bukkitversion = Bukkit.getServer().getClass().getPackage().getName().substring(23);
    private String customName;
    private EntityDisguise type;
    private Player disguised;
    private ItemStack hand, helm, chst, leg, boot;

    public Disguise(Player p, EntityDisguise type) {
        this(p, type, null);
    }

    public Disguise(Player p, EntityDisguise type, String name) {
        this(p, type, name, null, null, null, null, null);
    }

    public Disguise(Player p, EntityDisguise type, String name, ItemStack inhand, ItemStack helmet, ItemStack chestplate, ItemStack leggings, ItemStack boots) {
        this.customName = name;
        this.type = type;
        this.disguised = p;
        this.hand = inhand;
        this.helm = helmet;
        this.chst = chestplate;
        this.leg = leggings;
        this.boot = boots;
    }

    public void sendDisguise(Player to) throws Exception {
        if (to.equals(disguised))
            throw new IllegalArgumentException("Target Player cannot be the same as the disguised player");
        Object packetplayoutentitydestroy = ReflectionUtils.instantiateObject("PacketPlayOutEntityDestroy", PackageType.MINECRAFT_SERVER, new int[] { disguised.getEntityId() });
        Object world = ReflectionUtils.invokeMethod(disguised.getWorld(), "getHandle", null);
        Class<?> entity = Class.forName(type.getClassName());
        Object ent = ReflectionUtils.instantiateObject(entity, world);
        ReflectionUtils.invokeMethod(ent, "setPosition", disguised.getLocation().getX(), disguised.getLocation().getY(), disguised.getLocation().getZ());
        ReflectionUtils.getMethod(entity, "d", int.class).invoke(ent, disguised.getEntityId());
        if (customName != null) {
            ReflectionUtils.getMethod(entity, "setCustomName", String.class).invoke(ent, customName);
            ReflectionUtils.getMethod(entity, "setCustomNameVisible", boolean.class).invoke(ent, true);
        }
        handleSpecialTypes(type, ent);
        Object packetplayoutspawnentityliving = ReflectionUtils.instantiateObject("PacketPlayOutSpawnEntityLiving", PackageType.MINECRAFT_SERVER, ent);

        sendPacket(to, packetplayoutentitydestroy);
        sendPacket(to, packetplayoutspawnentityliving);
        if (hand != null)
            sendArmorContentPackets(to, disguised.getEntityId(), 0, hand);
        if (helm != null)
            sendArmorContentPackets(to, disguised.getEntityId(), 1, helm);
        if (chst != null)
            sendArmorContentPackets(to, disguised.getEntityId(), 2, chst);
        if (leg != null)
            sendArmorContentPackets(to, disguised.getEntityId(), 3, leg);
        if (boot != null)
            sendArmorContentPackets(to, disguised.getEntityId(), 4, boot);

    }

    public void removeDisguise() throws ReflectiveOperationException {
        Object ppoed = ReflectionUtils.instantiateObject("PacketPlayOutEntityDestroy", PackageType.MINECRAFT_SERVER, new int[] { disguised.getEntityId() });
        Object ppones = ReflectionUtils.instantiateObject("PacketPlayOutNamedEntitySpawn", PackageType.MINECRAFT_SERVER, ReflectionUtils.invokeMethod(disguised, "getHandle", null));
        for (Player p : Bukkit.getOnlinePlayers()) {
            if(p.equals(disguised)) continue;
            try {
                sendPacket(p, ppoed);
                sendPacket(p, ppones);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void sendDisguise(Player... players) {
        for (Player P : players) {
            if (P.equals(disguised))
                continue;
            try {
                sendDisguise(P);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void updateDisguise(Player forwho) throws Exception {
        sendDisguise(forwho);
    }

    public void updateDisguise(Player... players) {
        sendDisguise(players);
    }

    public void changePlayerDisguise(EntityDisguise type, Player sendto) throws Exception {
        this.type = type;
        sendDisguise(sendto);
    }

    public void changePlayerDisguise(EntityDisguise type, Player... sendto) throws Exception {
        this.type = type;
        sendDisguise(sendto);
    }

    private void sendPacket(Player p, Object pack) throws Exception {
        Class<?> packet = Class.forName("net.minecraft.server." + bukkitversion + ".Packet");
        Class<?> craftPlayer = Class.forName("org.bukkit.craftbukkit." + bukkitversion + ".entity.CraftPlayer");
        Object handle = craftPlayer.getMethod("getHandle").invoke(p);
        Object con = handle.getClass().getField("playerConnection").get(handle);
        con.getClass().getMethod("sendPacket", packet).invoke(con, pack);
    }

    private void sendArmorContentPackets(Player to, int entityID, int slot, ItemStack item) throws Exception {
        PackageType type;
        if (bukkitversion.startsWith("v1_7_"))
            type = PackageType.CRAFTBUKKIT;
        else
            type = PackageType.CRAFTBUKKIT_INVENTORY;
        Object craftitmstk = ReflectionUtils.getMethod("CraftItemStack", type, "asNMSCopy", item.getClass()).invoke(null, item);
        Object metadarapacket = ReflectionUtils.instantiateObject("PacketPlayOutEntityEquipment", PackageType.MINECRAFT_SERVER, entityID, slot, craftitmstk);
        sendPacket(to, metadarapacket);
    }

    private Object handleSpecialTypes(EntityDisguise type, Object entity)
            throws Exception {
        switch (type) {
        default:
            break;
        case WITHER_SKELETON:
            ReflectionUtils.invokeMethod(entity, "setSkeletonType", 1);
            break;
        }
        return entity;
    }

    public ItemStack getItemInHand() {
        return hand;
    }

    public void setItemInHand(ItemStack hand) {
        this.hand = hand;
    }

    public ItemStack getHelmet() {
        return helm;
    }

    public void setHelmet(ItemStack helm) {
        this.helm = helm;
    }

    public ItemStack getChestplate() {
        return chst;
    }

    public void setChestplate(ItemStack chst) {
        this.chst = chst;
    }

    public ItemStack getLeggings() {
        return leg;
    }

    public void setLeggings(ItemStack leg) {
        this.leg = leg;
    }

    public ItemStack getBoots() {
        return boot;
    }

    public void setBoots(ItemStack boot) {
        this.boot = boot;
    }

    public String getCustomName() {
        return customName;
    }

    public void setCustomName(String customName) {
        this.customName = customName;
    }

    public EntityDisguise getType() {
        return type;
    }

    public void setType(EntityDisguise type) {
        this.type = type;
    }

    public Player getDisguised() {
        return disguised;
    }

    }
    and my EntityType.java class

    Code (Java):
    public enum EntityDisguise {
    ZOMBIE("EntityZombie"), WITHER_SKELETON("EntitySkeleton"), SKELETON(
            "EntitySkeleton"), ZOMBIEPIG("EntityPigZombie"), BLAZE(
                    "EntityBlaze"), ENDERMAN("EntityEnderman"), CREEPER("EntityCreeper"), SPIDER(
                            "EntitySpider"), WITCH("EntityWitch"), WITHER_BOSS("EntityWither"), GHAST(
                                    "EntityGhast"), GIANT("EntityGiantZombie"), SLIME("EntitySlime"), CAVE_SPIDER(
                                            "EntityCaveSpider"), SILVERFISH("EntitySilverfish"), MAGMA_CUBE(
                                                    "EntityMagmaCube"), BAT("EntityBat"), PIG("EntityPig"), SHEEP(
                                                            "EntitySheep"), COW("EntityCow"), CHICKEN("EntityChicken"), SQUID(
                                                                    "EntitySquid"), WOLF("EntityWolf"), OCELOT("EntityOcelot"), HORSE(
                                                                            "EntityHorse"), VILLAGER("EntityVillager"), IRON_GOLEM(
                                                                                    "EntityIronGolem"), SNOWMAN("EntitySnowman"), ENDER_DRAGON(
                                                                                            "EntityEnderDragon"), MOOSHROOM("EntityMushroomCow"),
    RABBIT("EntityRabbit"),
    GUARDIAN("EntityGuardian"),
    ENDERMITE("EntityEndermite");

    private final String cls;

    EntityDisguise(String cls) {
        this.cls = cls;
    }

    public String getClassName() {
        return "net.minecraft.server."
                + Bukkit.getServer().getClass().getPackage().getName()
                .substring(23) + "." + cls;
    }

    }
    The code works and players become disguised the problem. One problem is if a player clicks a player disguised as a Horse or Creeper (and a couple others) they crash, and if they click an ocelot (and some other small ones) the disguised player becomes invisible to other players.
     
  2. Well specific entitiy types need additional metadata and you'll need to send them to all players when disguising your player.
     
  3. How would I do that? And what kind of metadata? Is there a doc about what each entity needs?
     
    • Agree Agree x 1