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