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