001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.bukkit.entity;
020
021import com.plotsquared.core.configuration.Settings;
022import org.apache.logging.log4j.LogManager;
023import org.apache.logging.log4j.Logger;
024import org.bukkit.Art;
025import org.bukkit.DyeColor;
026import org.bukkit.Location;
027import org.bukkit.Rotation;
028import org.bukkit.TreeSpecies;
029import org.bukkit.World;
030import org.bukkit.block.BlockFace;
031import org.bukkit.entity.AbstractHorse;
032import org.bukkit.entity.Ageable;
033import org.bukkit.entity.ArmorStand;
034import org.bukkit.entity.Bat;
035import org.bukkit.entity.Boat;
036import org.bukkit.entity.ChestedHorse;
037import org.bukkit.entity.EnderDragon;
038import org.bukkit.entity.Entity;
039import org.bukkit.entity.IronGolem;
040import org.bukkit.entity.Item;
041import org.bukkit.entity.ItemFrame;
042import org.bukkit.entity.LivingEntity;
043import org.bukkit.entity.Painting;
044import org.bukkit.entity.Rabbit;
045import org.bukkit.entity.Sheep;
046import org.bukkit.entity.Slime;
047import org.bukkit.entity.Tameable;
048import org.bukkit.inventory.EntityEquipment;
049import org.bukkit.inventory.InventoryHolder;
050import org.bukkit.inventory.ItemStack;
051import org.bukkit.util.EulerAngle;
052import org.bukkit.util.Vector;
053
054import java.util.List;
055
056public final class ReplicatingEntityWrapper extends EntityWrapper {
057
058    private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + ReplicatingEntityWrapper.class.getSimpleName());
059
060    private final short depth;
061    private final int hash;
062    private final EntityBaseStats base = new EntityBaseStats();
063
064    private ItemStack[] inventory;
065    // Extended
066    private ItemStack stack;
067    private byte dataByte;
068    private byte dataByte2;
069    private String dataString;
070    private LivingEntityStats lived;
071    private AgeableStats aged;
072    private TameableStats tamed;
073    private ArmorStandStats stand;
074    private HorseStats horse;
075    private boolean noGravity;
076
077    public ReplicatingEntityWrapper(Entity entity, short depth) {
078        super(entity);
079
080        this.hash = entity.getEntityId();
081        this.depth = depth;
082
083        if (depth == 0) {
084            return;
085        }
086        List<Entity> passengers = entity.getPassengers();
087        if (passengers.size() > 0) {
088            this.base.passenger = new ReplicatingEntityWrapper(passengers.get(0), depth);
089        }
090        this.base.fall = entity.getFallDistance();
091        this.base.fire = (short) entity.getFireTicks();
092        this.base.age = entity.getTicksLived();
093        Vector velocity = entity.getVelocity();
094        this.base.vX = velocity.getX();
095        this.base.vY = velocity.getY();
096        this.base.vZ = velocity.getZ();
097        if (depth == 1) {
098            return;
099        }
100        if (!entity.hasGravity()) {
101            this.noGravity = true;
102        }
103        switch (entity.getType().toString()) {
104            case "BOAT":
105                Boat boat = (Boat) entity;
106                this.dataByte = getOrdinal(TreeSpecies.values(), boat.getWoodType());
107                return;
108            case "ARROW":
109            case "EGG":
110            case "ENDER_CRYSTAL":
111            case "ENDER_PEARL":
112            case "ENDER_SIGNAL":
113            case "EXPERIENCE_ORB":
114            case "FALLING_BLOCK":
115            case "FIREBALL":
116            case "FIREWORK":
117            case "FISHING_HOOK":
118            case "LEASH_HITCH":
119            case "LIGHTNING":
120            case "MINECART":
121            case "MINECART_COMMAND":
122            case "MINECART_MOB_SPAWNER":
123            case "MINECART_TNT":
124            case "PLAYER":
125            case "PRIMED_TNT":
126            case "SLIME":
127            case "SMALL_FIREBALL":
128            case "SNOWBALL":
129            case "MINECART_FURNACE":
130            case "SPLASH_POTION":
131            case "THROWN_EXP_BOTTLE":
132            case "WITHER_SKULL":
133            case "UNKNOWN":
134            case "SPECTRAL_ARROW":
135            case "SHULKER_BULLET":
136            case "DRAGON_FIREBALL":
137            case "AREA_EFFECT_CLOUD":
138            case "TRIDENT":
139            case "LLAMA_SPIT":
140                // Do this stuff later
141                return;
142            // MISC //
143            case "DROPPED_ITEM":
144                Item item = (Item) entity;
145                this.stack = item.getItemStack();
146                return;
147            case "ITEM_FRAME":
148                this.x = Math.floor(this.getX());
149                this.y = Math.floor(this.getY());
150                this.z = Math.floor(this.getZ());
151                ItemFrame itemFrame = (ItemFrame) entity;
152                this.dataByte = getOrdinal(Rotation.values(), itemFrame.getRotation());
153                this.stack = itemFrame.getItem().clone();
154                return;
155            case "PAINTING":
156                this.x = Math.floor(this.getX());
157                this.y = Math.floor(this.getY());
158                this.z = Math.floor(this.getZ());
159                Painting painting = (Painting) entity;
160                Art art = painting.getArt();
161                this.dataByte = getOrdinal(BlockFace.values(), painting.getFacing());
162                int h = art.getBlockHeight();
163                if (h % 2 == 0) {
164                    this.y -= 1;
165                }
166                this.dataString = art.name();
167                return;
168            // END MISC //
169            // INVENTORY HOLDER //
170            case "MINECART_CHEST":
171            case "MINECART_HOPPER":
172                storeInventory((InventoryHolder) entity);
173                return;
174            // START LIVING ENTITY //
175            // START AGEABLE //
176            // START TAMEABLE //
177            case "HORSE":
178            case "DONKEY":
179            case "LLAMA":
180            case "MULE":
181            case "SKELETON_HORSE":
182                AbstractHorse horse = (AbstractHorse) entity;
183                this.horse = new HorseStats();
184                this.horse.jump = horse.getJumpStrength();
185                if (horse instanceof ChestedHorse horse1) {
186                    this.horse.chest = horse1.isCarryingChest();
187                }
188                //todo these horse features need fixing
189                //this.horse.variant = horse.getVariant();
190                //this.horse.style = horse.getStyle();
191                //this.horse.color = horse.getColor();
192                storeTameable(horse);
193                storeAgeable(horse);
194                storeLiving(horse);
195                storeInventory(horse);
196                return;
197            // END INVENTORY HOLDER //
198            case "WOLF":
199            case "OCELOT":
200                storeTameable((Tameable) entity);
201                storeAgeable((Ageable) entity);
202                storeLiving((LivingEntity) entity);
203                return;
204            // END TAMEABLE //
205            //todo fix sheep
206            case "SHEEP":
207                Sheep sheep = (Sheep) entity;
208                if (sheep.isSheared()) {
209                    this.dataByte = (byte) 1;
210                } else {
211                    this.dataByte = (byte) 0;
212                }
213                this.dataByte2 = sheep.getColor().getDyeData();
214                storeAgeable(sheep);
215                storeLiving(sheep);
216                return;
217            case "VILLAGER":
218            case "CHICKEN":
219            case "COW":
220            case "MUSHROOM_COW":
221            case "PIG":
222            case "TURTLE":
223            case "POLAR_BEAR":
224                storeAgeable((Ageable) entity);
225                storeLiving((LivingEntity) entity);
226                return;
227            case "RABBIT":
228                this.dataByte = getOrdinal(Rabbit.Type.values(), ((Rabbit) entity).getRabbitType());
229                storeAgeable((Ageable) entity);
230                storeLiving((LivingEntity) entity);
231                return;
232            // END AGEABLE //
233            case "ARMOR_STAND":
234                ArmorStand stand = (ArmorStand) entity;
235                this.inventory =
236                        new ItemStack[]{stand.getItemInHand().clone(), stand.getHelmet().clone(),
237                                stand.getChestplate().clone(), stand.getLeggings().clone(),
238                                stand.getBoots().clone()};
239                storeLiving(stand);
240                this.stand = new ArmorStandStats();
241
242                EulerAngle head = stand.getHeadPose();
243                this.stand.head[0] = (float) head.getX();
244                this.stand.head[1] = (float) head.getY();
245                this.stand.head[2] = (float) head.getZ();
246
247                EulerAngle body = stand.getBodyPose();
248                this.stand.body[0] = (float) body.getX();
249                this.stand.body[1] = (float) body.getY();
250                this.stand.body[2] = (float) body.getZ();
251
252                EulerAngle leftLeg = stand.getLeftLegPose();
253                this.stand.leftLeg[0] = (float) leftLeg.getX();
254                this.stand.leftLeg[1] = (float) leftLeg.getY();
255                this.stand.leftLeg[2] = (float) leftLeg.getZ();
256
257                EulerAngle rightLeg = stand.getRightLegPose();
258                this.stand.rightLeg[0] = (float) rightLeg.getX();
259                this.stand.rightLeg[1] = (float) rightLeg.getY();
260                this.stand.rightLeg[2] = (float) rightLeg.getZ();
261
262                EulerAngle leftArm = stand.getLeftArmPose();
263                this.stand.leftArm[0] = (float) leftArm.getX();
264                this.stand.leftArm[1] = (float) leftArm.getY();
265                this.stand.leftArm[2] = (float) leftArm.getZ();
266
267                EulerAngle rightArm = stand.getRightArmPose();
268                this.stand.rightArm[0] = (float) rightArm.getX();
269                this.stand.rightArm[1] = (float) rightArm.getY();
270                this.stand.rightArm[2] = (float) rightArm.getZ();
271
272                if (stand.hasArms()) {
273                    this.stand.arms = true;
274                }
275                if (!stand.hasBasePlate()) {
276                    this.stand.noPlate = true;
277                }
278                if (!stand.isVisible()) {
279                    this.stand.invisible = true;
280                }
281                if (stand.isSmall()) {
282                    this.stand.small = true;
283                }
284                return;
285            case "ENDERMITE":
286                return;
287            case "BAT":
288                if (((Bat) entity).isAwake()) {
289                    this.dataByte = (byte) 1;
290                } else {
291                    this.dataByte = (byte) 0;
292                }
293                return;
294            case "ENDER_DRAGON":
295                EnderDragon entity1 = (EnderDragon) entity;
296                this.dataByte = (byte) entity1.getPhase().ordinal();
297                return;
298            case "SKELETON":
299            case "WITHER_SKELETON":
300            case "GUARDIAN":
301            case "ELDER_GUARDIAN":
302            case "GHAST":
303            case "MAGMA_CUBE":
304            case "SQUID":
305            case "PIG_ZOMBIE":
306            case "HOGLIN":
307            case "ZOMBIFIED_PIGLIN":
308            case "PIGLIN":
309            case "PIGLIN_BRUTE":
310            case "ZOMBIE":
311            case "WITHER":
312            case "WITCH":
313            case "SPIDER":
314            case "CAVE_SPIDER":
315            case "SILVERFISH":
316            case "GIANT":
317            case "ENDERMAN":
318            case "CREEPER":
319            case "BLAZE":
320            case "SHULKER":
321            case "SNOWMAN":
322                storeLiving((LivingEntity) entity);
323                return;
324            case "IRON_GOLEM":
325                if (((IronGolem) entity).isPlayerCreated()) {
326                    this.dataByte = (byte) 1;
327                } else {
328                    this.dataByte = (byte) 0;
329                }
330                storeLiving((LivingEntity) entity);
331                // END LIVING //
332        }
333    }
334
335    @Override
336    public boolean equals(Object obj) {
337        return this.hash == obj.hashCode();
338    }
339
340    @Override
341    public int hashCode() {
342        return this.hash;
343    }
344
345    public void storeInventory(InventoryHolder held) {
346        this.inventory = held.getInventory().getContents().clone();
347    }
348
349    void restoreLiving(LivingEntity entity) {
350        entity.setCanPickupItems(this.lived.loot);
351        if (this.lived.name != null) {
352            entity.setCustomName(this.lived.name);
353            entity.setCustomNameVisible(this.lived.visible);
354        }
355        if (this.lived.potions != null && !this.lived.potions.isEmpty()) {
356            entity.addPotionEffects(this.lived.potions);
357        }
358        entity.setRemainingAir(this.lived.air);
359        entity.setRemoveWhenFarAway(this.lived.persistent);
360        if (this.lived.equipped) {
361            this.restoreEquipment(entity);
362        }
363        if (this.lived.leashed) {
364            // TODO leashes
365            //            World world = entity.getWorld();
366            //            Entity leash = world.spawnEntity(new Location(world, Math.floor(x) +
367            //            lived.leashX, Math.floor(y) + lived.leashY, Math.floor(z) + lived.leashZ),
368            //            EntityType.LEASH_HITCH);
369            //            entity.setLeashHolder(leash);
370        }
371    }
372
373    void restoreEquipment(LivingEntity entity) {
374        EntityEquipment equipment = entity.getEquipment();
375        if (equipment != null) {
376            equipment.setItemInMainHand(this.lived.mainHand);
377            equipment.setItemInOffHand(this.lived.offHand);
378            equipment.setHelmet(this.lived.helmet);
379            equipment.setChestplate(this.lived.chestplate);
380            equipment.setLeggings(this.lived.leggings);
381            equipment.setBoots(this.lived.boots);
382        }
383    }
384
385    private void restoreInventory(InventoryHolder entity) {
386        try {
387            entity.getInventory().setContents(this.inventory);
388        } catch (IllegalArgumentException e) {
389            LOGGER.error("Failed to restore inventory", e);
390        }
391    }
392
393    private void storeLiving(LivingEntity lived) {
394        this.lived = new LivingEntityStats();
395        this.lived.potions = lived.getActivePotionEffects();
396        this.lived.loot = lived.getCanPickupItems();
397        this.lived.name = lived.getCustomName();
398        this.lived.visible = lived.isCustomNameVisible();
399        this.lived.health = (float) lived.getHealth();
400        this.lived.air = (short) lived.getRemainingAir();
401        this.lived.persistent = lived.getRemoveWhenFarAway();
402        this.lived.leashed = lived.isLeashed();
403        if (this.lived.leashed) {
404            Location location = lived.getLeashHolder().getLocation();
405            this.lived.leashX = (short) (this.getX() - location.getBlockX());
406            this.lived.leashY = (short) (this.getY() - location.getBlockY());
407            this.lived.leashZ = (short) (this.getZ() - location.getBlockZ());
408        }
409        EntityEquipment equipment = lived.getEquipment();
410        this.lived.equipped = equipment != null;
411        if (this.lived.equipped) {
412            storeEquipment(equipment);
413        }
414    }
415
416    void storeEquipment(EntityEquipment equipment) {
417        this.lived.mainHand = equipment.getItemInMainHand().clone();
418        this.lived.offHand = equipment.getItemInOffHand().clone();
419        this.lived.boots = equipment.getBoots().clone();
420        this.lived.leggings = equipment.getLeggings().clone();
421        this.lived.chestplate = equipment.getChestplate().clone();
422        this.lived.helmet = equipment.getHelmet().clone();
423    }
424
425    private void restoreTameable(Tameable entity) {
426        if (this.tamed.tamed) {
427            if (this.tamed.owner != null) {
428                entity.setTamed(true);
429                entity.setOwner(this.tamed.owner);
430            }
431        }
432    }
433
434    private void restoreAgeable(Ageable entity) {
435        if (!this.aged.adult) {
436            entity.setBaby();
437        }
438        entity.setAgeLock(this.aged.locked);
439        if (this.aged.age > 0) {
440            entity.setAge(this.aged.age);
441        }
442    }
443
444    public void storeAgeable(Ageable aged) {
445        this.aged = new AgeableStats();
446        this.aged.age = aged.getAge();
447        this.aged.locked = aged.getAgeLock();
448        this.aged.adult = aged.isAdult();
449    }
450
451    public void storeTameable(Tameable tamed) {
452        this.tamed = new TameableStats();
453        this.tamed.owner = tamed.getOwner();
454        this.tamed.tamed = tamed.isTamed();
455    }
456
457    @SuppressWarnings("deprecation") // Paper deprecation
458    @Override
459    public Entity spawn(World world, int xOffset, int zOffset) {
460        Location location = new Location(world, this.getX() + xOffset, this.getY(), this.z + zOffset);
461        location.setYaw(this.yaw);
462        location.setPitch(this.pitch);
463        if (!this.getType().isSpawnable()) {
464            return null;
465        }
466        Entity entity;
467        switch (this.getType().toString()) {
468            case "DROPPED_ITEM":
469                return world.dropItem(location, this.stack);
470            case "PLAYER":
471            case "LEASH_HITCH":
472                return null;
473            case "ITEM_FRAME":
474                entity = world.spawn(location, ItemFrame.class);
475                break;
476            case "PAINTING":
477                entity = world.spawn(location, Painting.class);
478                break;
479            default:
480                entity = world.spawnEntity(location, this.getType());
481                break;
482        }
483        if (this.depth == 0) {
484            return entity;
485        }
486        if (this.base.passenger != null) {
487            try {
488                entity.addPassenger(this.base.passenger.spawn(world, xOffset, zOffset));
489            } catch (Exception ignored) {
490            }
491        }
492        if (this.base.fall != 0) {
493            entity.setFallDistance(this.base.fall);
494        }
495        if (this.base.fire != 0) {
496            entity.setFireTicks(this.base.fire);
497        }
498        if (this.base.age != 0) {
499            entity.setTicksLived(this.base.age);
500        }
501        entity.setVelocity(new Vector(this.base.vX, this.base.vY, this.base.vZ));
502        if (this.depth == 1) {
503            return entity;
504        }
505        if (this.noGravity) {
506            entity.setGravity(false);
507        }
508        switch (entity.getType().toString()) {
509            case "BOAT":
510                Boat boat = (Boat) entity;
511                boat.setWoodType(TreeSpecies.values()[dataByte]);
512                return entity;
513            case "SLIME":
514                ((Slime) entity).setSize(this.dataByte);
515                return entity;
516            case "ARROW":
517            case "EGG":
518            case "ENDER_CRYSTAL":
519            case "ENDER_PEARL":
520            case "ENDER_SIGNAL":
521            case "DROPPED_ITEM":
522            case "EXPERIENCE_ORB":
523            case "FALLING_BLOCK":
524            case "FIREBALL":
525            case "FIREWORK":
526            case "FISHING_HOOK":
527            case "LEASH_HITCH":
528            case "LIGHTNING":
529            case "MINECART":
530            case "MINECART_COMMAND":
531            case "MINECART_MOB_SPAWNER":
532            case "MINECART_TNT":
533            case "PLAYER":
534            case "PRIMED_TNT":
535            case "SMALL_FIREBALL":
536            case "SNOWBALL":
537            case "SPLASH_POTION":
538            case "THROWN_EXP_BOTTLE":
539            case "SPECTRAL_ARROW":
540            case "SHULKER_BULLET":
541            case "AREA_EFFECT_CLOUD":
542            case "DRAGON_FIREBALL":
543            case "WITHER_SKULL":
544            case "MINECART_FURNACE":
545            case "LLAMA_SPIT":
546            case "TRIDENT":
547            case "UNKNOWN":
548                // Do this stuff later
549                return entity;
550            // MISC //
551            case "ITEM_FRAME":
552                ItemFrame itemframe = (ItemFrame) entity;
553                itemframe.setRotation(Rotation.values()[this.dataByte]);
554                itemframe.setItem(this.stack);
555                return entity;
556            case "PAINTING":
557                Painting painting = (Painting) entity;
558                painting.setFacingDirection(BlockFace.values()[this.dataByte], true);
559                painting.setArt(Art.getByName(this.dataString), true);
560                return entity;
561            // END MISC //
562            // INVENTORY HOLDER //
563            case "MINECART_CHEST":
564            case "MINECART_HOPPER":
565                restoreInventory((InventoryHolder) entity);
566                return entity;
567            // START LIVING ENTITY //
568            // START AGEABLE //
569            // START TAMEABLE //
570            case "HORSE":
571            case "LLAMA":
572            case "SKELETON_HORSE":
573            case "DONKEY":
574            case "MULE":
575                AbstractHorse horse = (AbstractHorse) entity;
576                horse.setJumpStrength(this.horse.jump);
577                if (horse instanceof ChestedHorse) {
578                    ((ChestedHorse) horse).setCarryingChest(this.horse.chest);
579                }
580                //todo broken as of 1.13
581                //horse.setVariant(this.horse.variant);
582                //horse.setStyle(this.horse.style);
583                //horse.setColor(this.horse.color);
584                restoreTameable(horse);
585                restoreAgeable(horse);
586                restoreLiving(horse);
587                restoreInventory(horse);
588                return entity;
589            // END INVENTORY HOLDER //
590            case "WOLF":
591            case "OCELOT":
592                restoreTameable((Tameable) entity);
593                restoreAgeable((Ageable) entity);
594                restoreLiving((LivingEntity) entity);
595                return entity;
596            // END AGEABLE //
597            case "SHEEP":
598                Sheep sheep = (Sheep) entity;
599                if (this.dataByte == 1) {
600                    sheep.setSheared(true);
601                }
602                if (this.dataByte2 != 0) {
603                    sheep.setColor(DyeColor.getByDyeData(this.dataByte2));
604                }
605                restoreAgeable(sheep);
606                restoreLiving(sheep);
607                return sheep;
608            case "VILLAGER":
609            case "CHICKEN":
610            case "COW":
611            case "TURTLE":
612            case "POLAR_BEAR":
613            case "MUSHROOM_COW":
614            case "PIG":
615                restoreAgeable((Ageable) entity);
616                restoreLiving((LivingEntity) entity);
617                return entity;
618            // END AGEABLE //
619            case "RABBIT":
620                if (this.dataByte != 0) {
621                    ((Rabbit) entity).setRabbitType(Rabbit.Type.values()[this.dataByte]);
622                }
623                restoreAgeable((Ageable) entity);
624                restoreLiving((LivingEntity) entity);
625                return entity;
626            case "ARMOR_STAND":
627                // CHECK positions
628                ArmorStand stand = (ArmorStand) entity;
629                if (this.inventory[0] != null) {
630                    stand.setItemInHand(this.inventory[0]);
631                }
632                if (this.inventory[1] != null) {
633                    stand.setHelmet(this.inventory[1]);
634                }
635                if (this.inventory[2] != null) {
636                    stand.setChestplate(this.inventory[2]);
637                }
638                if (this.inventory[3] != null) {
639                    stand.setLeggings(this.inventory[3]);
640                }
641                if (this.inventory[4] != null) {
642                    stand.setBoots(this.inventory[4]);
643                }
644                if (this.stand.head[0] != 0 || this.stand.head[1] != 0 || this.stand.head[2] != 0) {
645                    EulerAngle pose =
646                            new EulerAngle(this.stand.head[0], this.stand.head[1], this.stand.head[2]);
647                    stand.setHeadPose(pose);
648                }
649                if (this.stand.body[0] != 0 || this.stand.body[1] != 0 || this.stand.body[2] != 0) {
650                    EulerAngle pose =
651                            new EulerAngle(this.stand.body[0], this.stand.body[1], this.stand.body[2]);
652                    stand.setBodyPose(pose);
653                }
654                if (this.stand.leftLeg[0] != 0 || this.stand.leftLeg[1] != 0
655                        || this.stand.leftLeg[2] != 0) {
656                    EulerAngle pose = new EulerAngle(this.stand.leftLeg[0], this.stand.leftLeg[1],
657                            this.stand.leftLeg[2]
658                    );
659                    stand.setLeftLegPose(pose);
660                }
661                if (this.stand.rightLeg[0] != 0 || this.stand.rightLeg[1] != 0
662                        || this.stand.rightLeg[2] != 0) {
663                    EulerAngle pose = new EulerAngle(this.stand.rightLeg[0], this.stand.rightLeg[1],
664                            this.stand.rightLeg[2]
665                    );
666                    stand.setRightLegPose(pose);
667                }
668                if (this.stand.leftArm[0] != 0 || this.stand.leftArm[1] != 0
669                        || this.stand.leftArm[2] != 0) {
670                    EulerAngle pose = new EulerAngle(this.stand.leftArm[0], this.stand.leftArm[1],
671                            this.stand.leftArm[2]
672                    );
673                    stand.setLeftArmPose(pose);
674                }
675                if (this.stand.rightArm[0] != 0 || this.stand.rightArm[1] != 0
676                        || this.stand.rightArm[2] != 0) {
677                    EulerAngle pose = new EulerAngle(this.stand.rightArm[0], this.stand.rightArm[1],
678                            this.stand.rightArm[2]
679                    );
680                    stand.setRightArmPose(pose);
681                }
682                if (this.stand.invisible) {
683                    stand.setVisible(false);
684                }
685                if (this.stand.arms) {
686                    stand.setArms(true);
687                }
688                if (this.stand.noPlate) {
689                    stand.setBasePlate(false);
690                }
691                if (this.stand.small) {
692                    stand.setSmall(true);
693                }
694                restoreLiving(stand);
695                return stand;
696            case "BAT":
697                if (this.dataByte != 0) {
698                    ((Bat) entity).setAwake(true);
699                }
700                restoreLiving((LivingEntity) entity);
701                return entity;
702            case "ENDER_DRAGON":
703                if (this.dataByte != 0) {
704                    ((EnderDragon) entity).setPhase(EnderDragon.Phase.values()[this.dataByte]);
705                }
706                restoreLiving((LivingEntity) entity);
707                return entity;
708            case "ENDERMITE":
709            case "GHAST":
710            case "MAGMA_CUBE":
711            case "SQUID":
712            case "PIG_ZOMBIE":
713            case "HOGLIN":
714            case "PIGLIN":
715            case "ZOMBIFIED_PIGLIN":
716            case "PIGLIN_BRUTE":
717            case "ZOMBIE":
718            case "WITHER":
719            case "WITCH":
720            case "SPIDER":
721            case "CAVE_SPIDER":
722            case "SILVERFISH":
723            case "GIANT":
724            case "ENDERMAN":
725            case "CREEPER":
726            case "BLAZE":
727            case "SNOWMAN":
728            case "SHULKER":
729            case "GUARDIAN":
730            case "ELDER_GUARDIAN":
731            case "SKELETON":
732            case "WITHER_SKELETON":
733                restoreLiving((LivingEntity) entity);
734                return entity;
735            case "IRON_GOLEM":
736                if (this.dataByte != 0) {
737                    ((IronGolem) entity).setPlayerCreated(true);
738                }
739                restoreLiving((LivingEntity) entity);
740                return entity;
741            default:
742                if (Settings.DEBUG) {
743                    LOGGER.info("Could not identify entity: {}", entity.getType());
744                }
745                return entity;
746            // END LIVING
747        }
748    }
749
750    public void saveEntity() {
751    }
752
753    private byte getOrdinal(Object[] list, Object value) {
754        for (byte i = 0; i < list.length; i++) {
755            if (list[i].equals(value)) {
756                return i;
757            }
758        }
759        return 0;
760    }
761
762
763}