package net.minecraft.world.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.nbt.NbtOps;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.EntityEquipment;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.DefaultAttributes;
import net.minecraft.world.entity.ai.attributes.AttributeMap;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;

import com.bergerkiller.bukkit.common.bases.IntVector3;
import com.bergerkiller.bukkit.common.nbt.CommonTagCompound;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher;
import com.bergerkiller.bukkit.common.wrappers.DataWatcher.Key;
import com.bergerkiller.bukkit.common.wrappers.Holder;

import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;

import com.bergerkiller.generated.net.minecraft.world.entity.ai.attributes.AttributeHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.ai.attributes.AttributeMapHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.ai.attributes.AttributeInstanceHandle;
import com.bergerkiller.generated.net.minecraft.nbt.CompoundTagHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.EntityHandle;

import com.bergerkiller.generated.net.minecraft.world.effect.MobEffectHandle;
import com.bergerkiller.generated.net.minecraft.world.effect.MobEffectInstanceHandle;

class LivingEntity extends Entity {

#if version >= 1.17
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:DATA_LIVING_ENTITY_FLAGS;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:DATA_HEALTH_ID;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:DATA_EFFECT_COLOR_ID;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:DATA_EFFECT_AMBIENCE_ID;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:DATA_ARROW_COUNT_ID;
    //TODO: DATA_STINGER_COUNT_ID data field was added
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:SLEEPING_POS_ID;
#elseif version >= 1.16.2
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:ag;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:g;
  #if version >= 1.16.3
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:ARROWS_IN_BODY;
  #else
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:bh;
  #endif
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:bj;
#elseif version >= 1.16
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:an;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:g;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:bo;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:bq;
#elseif version >= 1.15
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:ao;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:e;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:f;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:g;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:bq;
#elseif version >= 1.14
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:ar;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:e;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:f;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:g;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:bs;
#elseif version >= 1.13
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:aw;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:g;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:h;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:bx;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#elseif version >= 1.12
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:at;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:g;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:h;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:br;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#elseif version >= 1.11
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:at;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:g;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:h;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:bq;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#elseif version >= 1.10.2
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:au;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:g;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:h;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#elseif version >= 1.9.4
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:at;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:g;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:h;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#elseif version >= 1.9
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:as;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:HEALTH;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:f;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:g;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:h;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#else
    protected static optional final (DataWatcher.Key<Byte>) EntityDataAccessor<Byte> DATA_LIVING_FLAGS:###;
    public static optional final (DataWatcher.Key<Float>) EntityDataAccessor<Float> DATA_HEALTH:###;
    //private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_PARTICLES_COLOR_RGB:###;
    private static optional final (DataWatcher.Key<Boolean>) EntityDataAccessor<Boolean> DATA_PARTICLES_HIDDEN:###;
    private static optional final (DataWatcher.Key<Integer>) EntityDataAccessor<Integer> DATA_ARROWCOUNT:###;
    private static optional final (DataWatcher.Key<IntVector3>) EntityDataAccessor<Optional<BlockPos>> DATA_BEDPOSITION:###;
#endif

    <code>
    public static final Key<Byte> DATA_LIVING_FLAGS = Key.Type.BYTE.createKey(T.DATA_LIVING_FLAGS, -1);
    public static final Key<Float> DATA_HEALTH = Key.Type.FLOAT.createKey(T.DATA_HEALTH, 6);
    //public static final Key<Integer> DATA_PARTICLES_COLOR_RGB = Key.Type.INTEGER.createKey(T.DATA_PARTICLES_COLOR_RGB, 7);
    public static final Key<Boolean> DATA_PARTICLES_HIDDEN = Key.Type.BOOLEAN.createKey(T.DATA_PARTICLES_HIDDEN, 8);
    public static final Key<Integer> DATA_UNKNOWN1 = Key.Type.INTEGER.createKey(T.DATA_ARROWCOUNT, 9);
    public static final Key<IntVector3> DATA_BEDPOSITION = Key.Type.BLOCK_POSITION.createKey(T.DATA_BEDPOSITION, -1);
    </code>

    // Used in multiple places to refresh equipment changes on older server versions
#if version < 1.17
  #if version >= 1.16.2
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bn;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bo;
  #elseif version >= 1.15
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bu;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bv;
  #elseif version >= 1.14
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bw;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bx;
  #elseif version >= 1.13
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bB;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bC;
  #elseif version >= 1.12
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bv;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bw;
  #elseif version >= 1.11
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bu;
    #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bv;
  #elseif version >= 1.10.2
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] handItems:bu;
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] armorItems:bv;
  #elseif version >= 1.9.4
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] handItems:bt;
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] armorItems:bu;
  #elseif version >= 1.9
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] handItems:bs;
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] armorItems:bt;
  #else
    #require net.minecraft.world.entity.LivingEntity private final ItemStack[] equipmentItems:h;
  #endif
#endif

    public void resetAttributes() {
#if version >= 1.17
        // Is only set in constructor, so we'll have to force it
        // TODO: Is there no cleaner way to do this? Maybe modify AttributeMap directly?
        #require net.minecraft.world.entity.LivingEntity private final AttributeMap attributeMapField:attributes;
        #require net.minecraft.world.entity.LivingEntity private final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
  #if version >= 1.18
        AttributeMap new_attributes = new AttributeMap(DefaultAttributes.getSupplier(instance.getType()));
  #else
        AttributeMap new_attributes = new AttributeMap(DefaultAttributes.a(instance.getEntityType()));
  #endif
        org.bukkit.craftbukkit.attribute.CraftAttributeMap new_craftAttributes = new org.bukkit.craftbukkit.attribute.CraftAttributeMap(new_attributes);
        instance#attributeMapField = new_attributes;
        instance#craftAttributes = new_craftAttributes;

        // Item attributes need to be re-applied later, they are lost as part of this reset
        // If Inventory is also changed this is less important, as the item changes will trigger an update too.
        // But when the inventory does not change, no change will be detected.
        //
        // [MyWorlds] To reproduce, give yourself armor, then teleport between worlds where inventory is shared.
        // Your armor slots will go away when you go to the other world despite still wearing armor.
  #if exists net.minecraft.world.entity.LivingEntity private final net.minecraft.world.item.ItemStack[] lastEquipmentItems;
        // Some 1.17+ odd fork defines it as an array (performance?)
        #require net.minecraft.world.entity.LivingEntity private final ItemStack[] lastEquipmentItems;
        ItemStack[] lastEquipmentItems = instance#lastEquipmentItems;
        for (int i = 0; i < lastEquipmentItems.length; i++) {
            lastEquipmentItems[i] = ItemStack.EMPTY;
        }
  #elseif version >= 1.21.5
        #require net.minecraft.world.entity.LivingEntity private final Map<EquipmentSlot, ItemStack> lastEquipmentItems;
        Map lastEquipmentItems = instance#lastEquipmentItems;

    #if exists net.minecraft.world.entity.EquipmentSlot public static final EquipmentSlot[] VALUES;
        // Universe server uses an array for VALUES, instead of a collection
        EquipmentSlot[] slotTypes = EquipmentSlot.VALUES;
        int len = slotTypes.length;
        for (int i = 0; i < len; i++) {
            EquipmentSlot slot = slotTypes[i];
            if (lastEquipmentItems.containsKey(slot)) {
                lastEquipmentItems.put(slot, ItemStack.EMPTY);
            }
        }
    #else
        // Spigot / paper normal
        java.util.Iterator slotTypesIter = EquipmentSlot.VALUES.iterator();
        while (slotTypesIter.hasNext()) {
            EquipmentSlot slot = (EquipmentSlot) slotTypesIter.next();
            if (lastEquipmentItems.containsKey(slot)) {
                lastEquipmentItems.put(slot, ItemStack.EMPTY);
            }
        }
    #endif
  #else
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> lastHandItemStacks;
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> lastArmorItemStacks;
        NonNullList lastHandItemStacks = instance#lastHandItemStacks;
        NonNullList lastArmorItemStacks = instance#lastArmorItemStacks;
        lastHandItemStacks.clear();
        lastArmorItemStacks.clear();
  #endif
#elseif version >= 1.16
        // Is only set in constructor, so we'll have to force it
        // TODO: Is there no cleaner way to do this? Maybe modify AttributeMap directly?
        #require net.minecraft.world.entity.LivingEntity private final AttributeMap attributeMapField:attributeMap;
        #require net.minecraft.world.entity.LivingEntity private final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
        AttributeMap new_attributes = new AttributeMap(DefaultAttributes.a(instance.getEntityType()));
        org.bukkit.craftbukkit.attribute.CraftAttributeMap new_craftAttributes = new org.bukkit.craftbukkit.attribute.CraftAttributeMap(new_attributes);
        instance#attributeMapField = new_attributes;
        instance#craftAttributes = new_craftAttributes;

        // Item attributes need to be re-applied later, they are lost as part of this reset
  #if version >= 1.16.2
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bn;
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bo;
  #else
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> handItems:bu;
        #require net.minecraft.world.entity.LivingEntity private final NonNullList<ItemStack> armorItems:bv;
  #endif
        NonNullList handItems = instance#handItems;
        NonNullList armorItems = instance#armorItems;
        handItems.clear();
        armorItems.clear();
#else
        // Set attributeMapField to null and call getAttributeMap()
        // This will initialize the field (and possibly related CraftAttributeMap >= 1.11)
        #if version >= 1.11
            #require net.minecraft.world.entity.LivingEntity private AttributeMap attributeMapField:attributeMap;
        #elseif version >= 1.10.2
            #require net.minecraft.world.entity.LivingEntity private AttributeMap attributeMapField:br;
        #elseif version >= 1.9.4
            #require net.minecraft.world.entity.LivingEntity private AttributeMap attributeMapField:bq;
        #elseif version >= 1.9
            #require net.minecraft.world.entity.LivingEntity private AttributeMap attributeMapField:bp;
        #else
            #require net.minecraft.world.entity.LivingEntity private AttributeMap attributeMapField:c;
        #endif
        instance#attributeMapField = null;
        instance.getAttributeMap();

        // Then call initAttributes() to initialize the values used by the entity
        #if version >= 1.8.3
            #require net.minecraft.world.entity.LivingEntity protected void initAttributes();
        #else
            #require net.minecraft.world.entity.LivingEntity protected void initAttributes:aW();
        #endif
        instance#initAttributes();
#endif
    }

#if version >= 1.21.5
  #if exists net.minecraft.world.entity.LivingEntity public net.minecraft.world.entity.EntityEquipment equipment;
    #require LivingEntity public EntityEquipment equipment;
  #else
    // Paper yay!
    #require LivingEntity protected EntityEquipment equipment;
  #endif
#endif

#if exists net.minecraft.world.entity.LivingEntity public void detectEquipmentUpdatesPublic();
    // Spigot does it different I guess
    public void detectEquipmentChanges:detectEquipmentUpdatesPublic();
#elseif version >= 1.18
    private void detectEquipmentChanges:detectEquipmentUpdates();
#elseif version >= 1.16 && exists net.minecraft.server.level.ServerPlayer private void updateEquipment();
    // De-obfuscated by spigot devs
    private void detectEquipmentChanges:updateEquipment();
#elseif version >= 1.16.2
    private void detectEquipmentChanges:p();
#elseif version >= 1.16
    private void detectEquipmentChanges:q();
#elseif version >= 1.11
    // Unfortunately, on these old versions this logic is coded directly into tick()
    // I've had to re-implement it outside of it to make this work
    public void detectEquipmentChanges() {
        EquipmentSlot[] aenumitemslot = EquipmentSlot.values();
        int k = aenumitemslot.length;

        NonNullList handItems = instance#handItems;
        NonNullList armorItems = instance#armorItems;

        for (int l = 0; l < k; ++l) {
            EquipmentSlot enumitemslot = aenumitemslot[l];
            ItemStack itemstack;

            switch (enumitemslot.a().ordinal()) {
                case 0: // HAND
                    itemstack = (ItemStack) handItems.get(enumitemslot.b());
                    break;
                case 1: // ARMOR
                    itemstack = (ItemStack) armorItems.get(enumitemslot.b());
                    break;
                default:
                    continue;
            }

            ItemStack itemstack1 = instance.getEquipment(enumitemslot);

            if (!ItemStack.matches(itemstack1, itemstack)) {
  #if version >= 1.14
                ((net.minecraft.server.level.ServerLevel) instance.world).getChunkProvider().broadcast((Entity) instance,
                    new ClientboundSetEquipmentPacket(instance.getId(), enumitemslot, itemstack1));
  #else
                ((net.minecraft.server.level.ServerLevel) instance.world).getTracker().a((Entity) instance,
                    new ClientboundSetEquipmentPacket(instance.getId(), enumitemslot, itemstack1));
  #endif

                if (!itemstack.isEmpty()) {
                    instance.getAttributeMap().a(itemstack.a(enumitemslot));
                }

                if (!itemstack1.isEmpty()) {
                    instance.getAttributeMap().b(itemstack1.a(enumitemslot));
                }

  #if version >= 1.15
                switch (enumitemslot.a().ordinal()) {
                    case 0: // HAND
                        handItems.set(enumitemslot.b(), itemstack1.cloneItemStack());
                        break;
                    case 1: // ARMOR
                        armorItems.set(enumitemslot.b(), itemstack1.cloneItemStack());
                        break;
                }
  #else
                switch (enumitemslot.a().ordinal()) {
                    case 0: // HAND
                        handItems.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
                        break;
                    case 1: // ARMOR
                        armorItems.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
                        break;
                }
  #endif
            }
        }
    }
#elseif version >= 1.9
    // Unfortunately, on these old versions this logic is coded directly into tick()
    // I've had to re-implement it outside of it to make this work
    public void detectEquipmentChanges() {
        EquipmentSlot[] aenumitemslot = EquipmentSlot.values();
        int k = aenumitemslot.length;

        ItemStack[] handItems = instance#handItems;
        ItemStack[] armorItems = instance#armorItems;

        for (int l = 0; l < k; ++l) {
            EquipmentSlot enumitemslot = aenumitemslot[l];
            ItemStack itemstack;

            switch (enumitemslot.a().ordinal()) {
                case 0: // HAND
                    itemstack = (ItemStack) handItems[enumitemslot.b()];
                    break;
                case 1: // ARMOR
                    itemstack = (ItemStack) armorItems[enumitemslot.b()];
                    break;
                default:
                    continue;
            }

            ItemStack itemstack1 = instance.getEquipment(enumitemslot);

            if (!ItemStack.matches(itemstack1, itemstack)) {
                ((net.minecraft.server.level.ServerLevel) instance.world).getTracker().a((Entity) instance,
                    new ClientboundSetEquipmentPacket(instance.getId(), enumitemslot, itemstack1));

                if (itemstack != null) {
                    instance.getAttributeMap().a(itemstack.a(enumitemslot));
                }

                if (itemstack1 != null) {
                    instance.getAttributeMap().b(itemstack1.a(enumitemslot));
                }

                switch (enumitemslot.a().ordinal()) {
                    case 0: // HAND
                        handItems[enumitemslot.b()] = itemstack1 == null ? (ItemStack) null : itemstack1.cloneItemStack();
                        break;
                    case 1: // ARMOR
                        armorItems[enumitemslot.b()] = itemstack1 == null ? (ItemStack) null : itemstack1.cloneItemStack();
                        break;
                }
            }
        }
    }
#else
    // No dual wielding on 1.8. Really old shit.
    public void detectEquipmentChanges() {
        ItemStack[] equipmentItems = instance#equipmentItems;

        for (int j = 0; j < 5; ++j) {
            ItemStack itemstack = equipmentItems[j];
            ItemStack itemstack1 = instance.getEquipment(j);

            if (!ItemStack.matches(itemstack1, itemstack)) {
                ((net.minecraft.server.level.ServerLevel) instance.world).getTracker().a((Entity) instance,
                    (net.minecraft.network.protocol.Packet) (new ClientboundSetEquipmentPacket(instance.getId(), j, itemstack1)));
                if (itemstack != null) {
                    instance.getAttributeMap().a(itemstack.B());
                }
                if (itemstack1 != null) {
                    instance.getAttributeMap().b(itemstack1.B());
                }

                equipmentItems[j] = itemstack1 == null ? null : itemstack1.cloneItemStack();
            }
        }
    }
#endif

    public void loadEquipment((CommonTagCompound) CompoundTag data) {
#if version >= 1.21.5
        EntityEquipment equipment = instance#equipment;

        RegistryOps registryops = instance.registryAccess().createSerializationContext(NbtOps.INSTANCE);

        EntityEquipment loadedEquipment = (EntityEquipment) EntityEquipment.CODEC.parse(registryops, data)
                        .resultOrPartial(CompoundTagHandle.createPartialErrorLogger(data))
                        .orElse(null);
        if (loadedEquipment == null) {
            loadedEquipment = new EntityEquipment();
        }

        equipment.setAll(loadedEquipment);
#endif
    }

    public (CommonTagCompound) CompoundTag saveEquipment() {
#if version >= 1.21.5
        EntityEquipment equipment = instance#equipment;

        if (equipment.isEmpty()) {
            return null;
        }

        RegistryOps registryops = instance.registryAccess().createSerializationContext(NbtOps.INSTANCE);

        return (CompoundTag) EntityEquipment.CODEC.encode(equipment, registryops, new CompoundTag());
#else
        return null;
#endif
    }

#if version >= 1.18
    public (AttributeMapHandle) AttributeMap getAttributeMap:getAttributes();
#else
    public (AttributeMapHandle) AttributeMap getAttributeMap();
#endif

#if version >= 1.20.5
    public (AttributeInstanceHandle) AttributeInstance getAttribute((Holder<AttributeHandle>) net.minecraft.core.Holder<Attribute> attribute);
#elseif version >= 1.18
    public (AttributeInstanceHandle) AttributeInstance getAttribute((Holder<AttributeHandle>) Attribute attribute);
#elseif version >= 1.16
    public (AttributeInstanceHandle) AttributeInstance getAttribute:getAttributeInstance((Holder<AttributeHandle>) Attribute attribute);
#else
    public (AttributeInstanceHandle) AttributeInstance getAttribute((Holder<AttributeHandle>) IAttribute attribute) {
        return (AttributeInstance) instance.getAttributeInstance(attribute);
    }
#endif

#if version >= 1.20.5
    public final (Map<Holder<MobEffectHandle>, MobEffectInstanceHandle>) Map<net.minecraft.core.Holder<MobEffect>, MobEffectInstance> mobEffects:activeEffects;
#elseif version >= 1.17
    public final (Map<Holder<MobEffectHandle>, MobEffectInstanceHandle>) Map<MobEffect, MobEffectInstance> mobEffects:activeEffects;
#elseif version >= 1.9
    public final (Map<Holder<MobEffectHandle>, MobEffectInstanceHandle>) Map<MobEffect, MobEffectInstance> mobEffects:effects;
#else
    public final (Map<Holder<MobEffectHandle>, MobEffectInstanceHandle>) Map<Integer, MobEffectInstance> mobEffects:effects;
#endif

#if version >= 1.17
    public float lastDamage:lastHurt;
#else
    public float lastDamage;
#endif

    public (org.bukkit.util.Vector) Vec3 getMoveIntent() {
#if version >= 1.21.2
        if (instance instanceof net.minecraft.server.level.ServerPlayer) {
            return ((net.minecraft.server.level.ServerPlayer) instance).getLastClientMoveIntent();
        }
#endif

#if version >= 1.17
        #require LivingEntity public readonly float moveIntentX:xxa;
        #require LivingEntity public readonly float moveIntentY:yya;
        #require LivingEntity public readonly float moveIntentZ:yya;
#elseif version >= 1.16.2
        #require LivingEntity public readonly float moveIntentX:aR;
        #require LivingEntity public readonly float moveIntentY:aS;
        #require LivingEntity public readonly float moveIntentZ:aT;
#elseif version >= 1.16
        #require LivingEntity public readonly float moveIntentX:aY;
        #require LivingEntity public readonly float moveIntentY:aZ;
        #require LivingEntity public readonly float moveIntentZ:ba;
#elseif version >= 1.15
        #require LivingEntity public readonly float moveIntentX:aZ;
        #require LivingEntity public readonly float moveIntentY:ba;
        #require LivingEntity public readonly float moveIntentZ:bb;
#elseif version >= 1.14
        #require LivingEntity public readonly float moveIntentX:bb;
        #require LivingEntity public readonly float moveIntentY:bc;
        #require LivingEntity public readonly float moveIntentZ:bd;
#elseif version >= 1.13
        #require LivingEntity public readonly float moveIntentX:bh;
        #require LivingEntity public readonly float moveIntentY:bi;
        #require LivingEntity public readonly float moveIntentZ:bj;
#elseif version >= 1.12
        #require LivingEntity public readonly float moveIntentX:be;
        #require LivingEntity public readonly float moveIntentY:bf;
        #require LivingEntity public readonly float moveIntentZ:bg;
#elseif version >= 1.11
        #require LivingEntity public readonly float moveIntentX:be;
        #require LivingEntity public readonly float moveIntentZ:bf;
#elseif version >= 1.10.2
        #require LivingEntity public readonly float moveIntentX:bf;
        #require LivingEntity public readonly float moveIntentZ:bg;
#elseif version >= 1.9.4
        #require LivingEntity public readonly float moveIntentX:be;
        #require LivingEntity public readonly float moveIntentZ:bf;
#elseif version >= 1.9
        #require LivingEntity public readonly float moveIntentX:bd;
        #require LivingEntity public readonly float moveIntentZ:be;
#elseif version >= 1.8.3
        #require LivingEntity public readonly float moveIntentX:aZ;
        #require LivingEntity public readonly float moveIntentZ:ba;
#else
        #require LivingEntity public readonly float moveIntentX:aX;
        #require LivingEntity public readonly float moveIntentZ:aY;
#endif

        float x = instance#moveIntentX;
#if version >= 1.12
        float y = instance#moveIntentY;
#else
        float y = 0.0f;
#endif
        float z = instance#moveIntentZ;

        return new Vec3((double) x, (double) y, (double) z);
    }

    <code>
    public float getSideMovement() {
        return (float) getMoveIntent().getX();
    }

    public float getForwardMovement() {
        return (float) getMoveIntent().getZ();
    }
    </code>

#if version >= 1.17
    public boolean updateEffects:effectsDirty;
#else
    public boolean updateEffects;
#endif

#if version >= 1.18
    public (Collection<MobEffectInstanceHandle>) Collection<MobEffectInstance> getEffects:getActiveEffects();
#else
    public (Collection<MobEffectInstanceHandle>) Collection<MobEffectInstance> getEffects();
#endif

#if version >= 1.18
    public abstract (org.bukkit.inventory.ItemStack) ItemStack getEquipment:getItemBySlot((org.bukkit.inventory.EquipmentSlot) EquipmentSlot paramEnumItemSlot);
#elseif version >= 1.9
    public abstract (org.bukkit.inventory.ItemStack) ItemStack getEquipment((org.bukkit.inventory.EquipmentSlot) EquipmentSlot paramEnumItemSlot);
#else
    public (org.bukkit.inventory.ItemStack) ItemStack getEquipment((org.bukkit.inventory.EquipmentSlot) EquipmentSlot paramEnumItemSlot) {
        if (instance instanceof net.minecraft.world.entity.player.Player) {
            if (paramEnumItemSlot == EquipmentSlot.MAINHAND) {
                // Technically not supported, we implement a workaround
                return ((net.minecraft.world.entity.player.Player) instance).inventory.getItemInHand();
            } else {
                return instance.getEquipment(paramEnumItemSlot.b());
            }
        } else {
            return instance.getEquipment(paramEnumItemSlot.c());
        }
    }
#endif

    public final float getHealth();

    public final float getMaxHealth();

#if version >= 1.18
    public float getAbsorptionAmount();
    public void setAbsorptionAmount(float extraHealth);
#else
    public float getAbsorptionAmount:getAbsorptionHearts();
    public void setAbsorptionAmount:setAbsorptionHearts(float extraHealth);
#endif

    <code>
    public static LivingEntityHandle fromBukkit(org.bukkit.entity.LivingEntity livingEntity) {
        return createHandle(com.bergerkiller.bukkit.common.conversion.type.HandleConversion.toEntityHandle(livingEntity));
    }
    </code>
}
