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