package net.minecraft.network.protocol.game;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.phys.Vec3;

import com.bergerkiller.bukkit.common.internal.logic.ProtocolMath;

import org.bukkit.block.BlockFace;
import com.bergerkiller.bukkit.common.bases.IntVector3;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundAddMobPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundAddEntityPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundAddPlayerPacketHandle;
import com.bergerkiller.generated.net.minecraft.world.entity.EntityTypeHandle;

class ClientboundAddEntityPacket extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.17
    private int entityId:id;
    private optional UUID entityUUID:uuid;
  #if version >= 1.19
    private optional (EntityTypeHandle) EntityType<?> opt_entityType:type;
  #endif
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
  #if version >= 1.19
    private unknown byte xRot;
    private unknown byte yRot;
    private unknown byte yHeadRot;
  #else
    private unknown int xRot;
    private unknown int yRot;
  #endif
    private optional int opt_entityTypeId:###;

  #if version <= 1.18.2
    private optional (EntityTypeHandle) EntityType<?> opt_entityType:type;
  #endif
    private int extraData:data;
#elseif version >= 1.9
    private int entityId:a;
    private optional UUID entityUUID:b;
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:c;
    private optional double posY_1_10_2:d;
    private optional double posZ_1_10_2:e;
    private unknown int pitch_raw:i;
    private unknown int yaw_raw:j;

  #if version >= 1.14
    private optional int opt_entityTypeId:###;
    private optional (EntityTypeHandle) EntityType<?> opt_entityType:k;
  #else
    private optional int opt_entityTypeId:k;
    private optional (EntityTypeHandle) EntityType<?> opt_entityType:###;
  #endif

    private int extraData:l;
#else
    private int entityId:a;
    private optional UUID entityUUID:###;
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
    private unknown int pitch_raw:h;
    private unknown int yaw_raw:i;
    private optional int opt_entityTypeId:j;
    private optional (EntityTypeHandle) EntityType<?> opt_entityType:###;
    private int extraData:k;
#endif

#if version >= 1.21.9
    #require ClientboundAddEntityPacket private final Vec3 movement;
#elseif version >= 1.17
    #require ClientboundAddEntityPacket private int motX_raw:xa;
    #require ClientboundAddEntityPacket private int motY_raw:ya;
    #require ClientboundAddEntityPacket private int motZ_raw:za;
#elseif version >= 1.9
    #require ClientboundAddEntityPacket private int motX_raw:f;
    #require ClientboundAddEntityPacket private int motY_raw:g;
    #require ClientboundAddEntityPacket private int motZ_raw:h;
#else
    #require ClientboundAddEntityPacket private int motX_raw:e;
    #require ClientboundAddEntityPacket private int motY_raw:f;
    #require ClientboundAddEntityPacket private int motZ_raw:g;
#endif

    public static (ClientboundAddEntityPacketHandle) ClientboundAddEntityPacket createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundAddEntityPacket private ClientboundAddEntityPacket createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        ClientboundAddEntityPacket packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #if version >= 1.21.9
        packet#movement = Vec3.ZERO;
  #endif
        return packet;
#elseif version >= 1.17
        return new ClientboundAddEntityPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new ClientboundAddEntityPacket();
#endif
    }

#if version >= 1.19
    #require ClientboundAddEntityPacket private final byte xRot;
    #require ClientboundAddEntityPacket private final byte yRot;

    public float getYaw() {
        byte prot = instance#yRot;
        return ProtocolMath.deserializeRotation((int) prot);
    }
    public float getPitch() {
        byte prot = instance#xRot;
        return ProtocolMath.deserializeRotation((int) prot);
    }
    public void setYaw(float yaw) {
        byte prot = (byte) ProtocolMath.serializeRotation(yaw);
        instance#yRot = prot;
    }
    public void setPitch(float pitch) {
        byte prot = (byte) ProtocolMath.serializeRotation(pitch);
        instance#xRot = prot;
    }
#else
  #if version >= 1.17
    #require ClientboundAddEntityPacket private int xRot;
    #require ClientboundAddEntityPacket private int yRot;
  #elseif version >= 1.9
    #require ClientboundAddEntityPacket private int xRot:i;
    #require ClientboundAddEntityPacket private int yRot:j;
  #else
    #require ClientboundAddEntityPacket private int xRot:h;
    #require ClientboundAddEntityPacket private int yRot:i;
  #endif

    public float getYaw() {
        int prot = instance#yRot;
        return ProtocolMath.deserializeRotation(prot);
    }
    public float getPitch() {
        int prot = instance#xRot;
        return ProtocolMath.deserializeRotation(prot);
    }
    public void setYaw(float yaw) {
        int prot = ProtocolMath.serializeRotation(yaw);
        instance#yRot = prot;
    }
    public void setPitch(float pitch) {
        int prot = ProtocolMath.serializeRotation(pitch);
        instance#xRot = prot;
    }
#endif

#if version >= 1.21.9
    public (org.bukkit.util.Vector) Vec3 getMotVector:getMovement();

    public void setMotVector((org.bukkit.util.Vector) Vec3 motVector) {
        instance#movement = motVector;
    }

    public double getMotX() { return instance.getMovement().x; }
    public double getMotY() { return instance.getMovement().y; }
    public double getMotZ() { return instance.getMovement().z; }

    public void setMotX(double motX) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(motX, movement.y, movement.z);
        instance#movement = updated;
    }

    public void setMotY(double motY) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(movement.x, motY, movement.z);
        instance#movement = updated;
    }

    public void setMotZ(double motZ) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(movement.x, movement.y, motZ);
        instance#movement = updated;
    }
#else
    public (org.bukkit.util.Vector) Vec3 getMotVector() {
        int motX_raw = instance#motX_raw;
        int motY_raw = instance#motY_raw;
        int motZ_raw = instance#motZ_raw;
        return new Vec3( com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw) );
    }

    public void setMotVector((org.bukkit.util.Vector) Vec3 motVector) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.x);
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.y);
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.z);
        instance#motX_raw = motX_raw;
        instance#motY_raw = motY_raw;
        instance#motZ_raw = motZ_raw;
    }

    public double getMotX() {
        int motX_raw = instance#motX_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw);
    }

    public double getMotY() {
        int motY_raw = instance#motY_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw);
    }

    public double getMotZ() {
        int motZ_raw = instance#motZ_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw);
    }

    public void setMotX(double motX) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motX);
        instance#motX_raw = motX_raw;
    }

    public void setMotY(double motY) {
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motY);
        instance#motY_raw = motY_raw;
    }

    public void setMotZ(double motZ) {
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motZ);
        instance#motZ_raw = motZ_raw;
    }
#endif

    <code>
    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN;
    }

    public void setEntityUUID(UUID uuid) {
        if (T.entityUUID.isAvailable()) {
            T.entityUUID.set(getRaw(), uuid);
        }
    }

    public static boolean isEntityTypeSupported(org.bukkit.entity.EntityType type) {
        return isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public static boolean isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            return false;
        } else if (T.opt_entityTypeId.isAvailable()) {
            return (commonEntityType.objectTypeId != -1);
        } else {
            return (commonEntityType.nmsEntityType != null);
        }
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        if (T.opt_entityTypeId.isAvailable()) {
            if (commonEntityType.objectTypeId == -1) {
                throw new IllegalArgumentException("Input " + commonEntityType.toString() + " cannot be spawned using this packet");
            }
            T.opt_entityTypeId.setInteger(getRaw(), commonEntityType.objectTypeId);
        } else {
            if (commonEntityType.nmsEntityType == null) {
                throw new IllegalArgumentException("Input " + commonEntityType.toString() + " cannot be spawned using this packet");
            }
            T.opt_entityType.set(getRaw(), commonEntityType.nmsEntityType);
        }
        if (commonEntityType.objectExtraData != -1) {
            setExtraData(commonEntityType.objectExtraData);
        }
    }

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        if (T.opt_entityTypeId.isAvailable()) {
            int id = T.opt_entityTypeId.getInteger(getRaw());
            return com.bergerkiller.bukkit.common.entity.CommonEntityType.byObjectTypeId(id);
        } else {
            EntityTypeHandle nmsType = T.opt_entityType.get(getRaw());
            return com.bergerkiller.bukkit.common.entity.CommonEntityType.byNMSEntityType(nmsType);
        }
    }

    public void setEntityType(org.bukkit.entity.EntityType type) {
        setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public org.bukkit.entity.EntityType getEntityType() {
        return getCommonEntityType().entityType;
    }

    @Deprecated
    public void setEntityTypeId(int typeId) {
        if (T.opt_entityTypeId.isAvailable()) {
            T.opt_entityTypeId.setInteger(getRaw(), typeId);
        } else {
            setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byObjectTypeId(typeId));
        }
    }

    @Deprecated
    public int getEntityTypeId() {
        if (T.opt_entityTypeId.isAvailable()) {
            return T.opt_entityTypeId.getInteger(getRaw());
        } else {
            return getCommonEntityType().objectTypeId;
        }
    }

    public void setFallingBlockData(com.bergerkiller.bukkit.common.wrappers.BlockData blockData) {
        setExtraData(blockData.getCombinedId());
    }

    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}

// Note: since MC 1.20.2 this is identical to PacketPlayOutSpawnEntity
class ClientboundAddPlayerPacket extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.20.2
    private int entityId:id;
    private UUID entityUUID:uuid;
#elseif version >= 1.17
    private int entityId;
    private UUID entityUUID:playerId;
#else
    private int entityId:a;
    private UUID entityUUID:b;
#endif

#if version >= 1.17
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
#elseif version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:c;
    private optional double posY_1_10_2:d;
    private optional double posZ_1_10_2:e;
#else
    private optional int posX_1_8_8:c;
    private optional int posY_1_8_8:d;
    private optional int posZ_1_8_8:e;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

#if version >= 1.17
    private optional byte yaw_raw:yRot;
    private optional byte pitch_raw:xRot;
#else
    private optional byte yaw_raw:f;
    private optional byte pitch_raw:g;
#endif

#if version >= 1.9
    private optional (org.bukkit.Material) int heldItem:###;
#else
    private optional (org.bukkit.Material) int heldItem:h;
#endif

#if version >= 1.15
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:###;
#elseif version >= 1.9
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:h;
#else
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:i;
#endif

#if version >= 1.21.9
    #require ClientboundAddPlayerPacket private final Vec3 movement;
#endif

    public static (ClientboundAddPlayerPacketHandle) ClientboundAddPlayerPacket createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundAddPlayerPacket private ClientboundAddPlayerPacket createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        ClientboundAddPlayerPacket packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
        #require ClientboundAddPlayerPacket private final EntityType<?> type;
        packet#type = EntityTypes.PLAYER;
  #if version >= 1.21.9
        packet#movement = Vec3.ZERO;
  #endif
        return packet;
#elseif version >= 1.20.2
        ClientboundAddPlayerPacket packet = new ClientboundAddPlayerPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
        #require ClientboundAddPlayerPacket private final EntityType<?> type;
        packet#type = EntityTypes.PLAYER;
        return packet;
#elseif version >= 1.17
        return new ClientboundAddPlayerPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new ClientboundAddPlayerPacket();
#endif
    }

    <code>
    public boolean hasDataWatcherSupport() {
        return T.opt_dataWatcher.isAvailable();
    }

    @Deprecated
    public com.bergerkiller.bukkit.common.wrappers.DataWatcher getDataWatcher() {
        if (T.opt_dataWatcher.isAvailable()) {
            return T.opt_dataWatcher.get(getRaw());
        } else {
            return null;
        }
    }

    @Deprecated
    public void setDataWatcher(com.bergerkiller.bukkit.common.wrappers.DataWatcher dataWatcher) {
        if (T.opt_dataWatcher.isAvailable()) {
            T.opt_dataWatcher.set(getRaw(), dataWatcher);
        }
    }

    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN_NAMED;
    }

    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }

    public float getYaw() {
        return getProtocolRotation(T.yaw_raw);
    }

    public float getPitch() {
        return getProtocolRotation(T.pitch_raw);
    }

    public void setYaw(float yaw) {
        setProtocolRotation(T.yaw_raw, yaw);
    }

    public void setPitch(float pitch) {
        setProtocolRotation(T.pitch_raw, pitch);
    }

    public void setHeldItem(Material type) {
        if (T.heldItem.isAvailable()) {
            T.heldItem.set(getRaw(), type);
        }
    }

    public org.bukkit.Material getHeldItem() {
        if (T.heldItem.isAvailable()) {
            return T.heldItem.get(getRaw());
        } else {
            return org.bukkit.Material.AIR;
        }
    }
    </code>
}

class ClientboundAddMobPacket extends Packet {
#if version >= 1.20.5
    #bootstrap com.bergerkiller.bukkit.common.internal.CommonBootstrap.initServer();
#endif

#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.19
    private optional UUID entityUUID:uuid;
    private optional byte pitch_raw:xRot;
    private optional byte yaw_raw:yRot;
    private optional byte headYaw_raw:yHeadRot;
#elseif version >= 1.17
    private optional UUID entityUUID:uuid;
    private optional byte yaw_raw:yRot;
    private optional byte pitch_raw:xRot;
    private optional byte headYaw_raw:yHeadRot;
#elseif version >= 1.9
    private optional UUID entityUUID:b;
    private optional byte yaw_raw:j;
    private optional byte pitch_raw:k;
    private optional byte headYaw_raw:l;
#else
    private optional UUID entityUUID:###;
    private optional byte yaw_raw:i;
    private optional byte pitch_raw:j;
    private optional byte headYaw_raw:k;
#endif

#if version >= 1.21.9
    #require ClientboundAddMobPacket private final Vec3 movement;
#elseif version >= 1.19
    #require ClientboundAddMobPacket private int motX_raw:xa;
    #require ClientboundAddMobPacket private int motY_raw:ya;
    #require ClientboundAddMobPacket private int motZ_raw:za;
#elseif version >= 1.17
    #require ClientboundAddMobPacket private int motX_raw:xd;
    #require ClientboundAddMobPacket private int motY_raw:yd;
    #require ClientboundAddMobPacket private int motZ_raw:zd;
#elseif version >= 1.9
    #require ClientboundAddMobPacket private int motX_raw:g;
    #require ClientboundAddMobPacket private int motY_raw:h;
    #require ClientboundAddMobPacket private int motZ_raw:i;
#else
    #require ClientboundAddMobPacket private int motX_raw:f;
    #require ClientboundAddMobPacket private int motY_raw:g;
    #require ClientboundAddMobPacket private int motZ_raw:h;
#endif

    // DataWatcher and items list was removed since MC 1.15
#if version >= 1.15
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:###;
#elseif version >= 1.9
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:m;
#else
    private optional (com.bergerkiller.bukkit.common.wrappers.DataWatcher) SynchedEntityData opt_dataWatcher:l;
#endif

    public static (ClientboundAddMobPacketHandle) ClientboundAddMobPacket createNew() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundAddMobPacket private ClientboundAddMobPacket createSpawnPacket:<init>(net.minecraft.network.RegistryFriendlyByteBuf serializer);
        ClientboundAddMobPacket packet = #createSpawnPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #if version >= 1.21.9
        packet#movement = Vec3.ZERO;
  #endif
        return packet;
#elseif version >= 1.17
        return new ClientboundAddMobPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new ClientboundAddMobPacket();
#endif
    }

    // Entity type property
#if version >= 1.19
    #require ClientboundAddMobPacket private final EntityType<?> entityType:type;

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        Object nmsEntityType = instance.getType();
        return com.bergerkiller.bukkit.common.entity.CommonEntityType.byNMSEntityTypeRaw(nmsEntityType);
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        instance#entityType = (EntityType) commonEntityType.nmsEntityType.getRaw();
    }
#else
  #if version >= 1.17
    #require ClientboundAddMobPacket private int entityTypeId:type;
  #elseif version >= 1.9
    #require ClientboundAddMobPacket private int entityTypeId:c;
  #else
    #require ClientboundAddMobPacket private int entityTypeId:b;
  #endif

    public com.bergerkiller.bukkit.common.entity.CommonEntityType getCommonEntityType() {
        int typeId = instance#entityTypeId;
        return com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityTypeId(typeId);
    }

    public void setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType commonEntityType) {
        if (commonEntityType == null) {
            throw new IllegalArgumentException("Input CommonEntityType is null");
        }
        if (commonEntityType.entityTypeId == -1) {
            throw new IllegalArgumentException("Input CommonEntityType " + commonEntityType.toString() + " cannot be spawned using this packet");
        }
        instance#entityTypeId = commonEntityType.entityTypeId;
    }
#endif

    // Position property
#if version >= 1.17
    // Has getter accessor methods. Fields are double x/y/z
  #if version >= 1.18
    public double getPosX:getX();
    public double getPosY:getY();
    public double getPosZ:getZ();
  #else
    public double getPosX:e();
    public double getPosY:f();
    public double getPosZ:g();
  #endif

    // Used by setters down below
    #require ClientboundAddMobPacket private final double posX:x;
    #require ClientboundAddMobPacket private final double posY:y;
    #require ClientboundAddMobPacket private final double posZ:z;

#elseif version >= 1.9
    // Used by getters/setters down below
    #require ClientboundAddMobPacket private double posX:d;
    #require ClientboundAddMobPacket private double posY:e;
    #require ClientboundAddMobPacket private double posZ:f;

    public double getPosX() {
        return instance#posX;
    }
    public double getPosY() {
        return instance#posY;
    }
    public double getPosZ() {
        return instance#posZ;
    }
#else
    // Uses private int fields, requiring protocol encoding/decoding
    // Used by getters/setters down below
    #require ClientboundAddMobPacket private int posX:c;
    #require ClientboundAddMobPacket private int posY:d;
    #require ClientboundAddMobPacket private int posZ:e;

    public double getPosX() {
        int prot = instance#posX;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
    public double getPosY() {
        int prot = instance#posY;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
    public double getPosZ() {
        int prot = instance#posZ;
        return ProtocolMath.deserializePosition_1_8_8(prot);
    }
#endif
#if version >= 1.9
    public void setPosX(double x) {
        instance#posX = x;
    }
    public void setPosY(double y) {
        instance#posY = y;
    }
    public void setPosZ(double z) {
        instance#posZ = z;
    }
#else
    public void setPosX(double x) {
        int prot = ProtocolMath.serializePosition_1_8_8(x);
        instance#posX = prot;
    }
    public void setPosY(double y) {
        int prot = ProtocolMath.serializePosition_1_8_8(y);
        instance#posY = prot;
    }
    public void setPosZ(double z) {
        int prot = ProtocolMath.serializePosition_1_8_8(z);
        instance#posZ = prot;
    }
#endif

    // Velocity (Motion)
#if version >= 1.21.9
    public (org.bukkit.util.Vector) Vec3 getMotVector:getMovement();

    public void setMotVector((org.bukkit.util.Vector) Vec3 motVector) {
        instance#movement = motVector;
    }

    public double getMotX() { return instance.getMovement().x; }
    public double getMotY() { return instance.getMovement().y; }
    public double getMotZ() { return instance.getMovement().z; }

    public void setMotX(double motX) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(motX, movement.y, movement.z);
        instance#movement = updated;
    }

    public void setMotY(double motY) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(movement.x, motY, movement.z);
        instance#movement = updated;
    }

    public void setMotZ(double motZ) {
        Vec3 movement = instance.getMovement();
        if (movement == null) movement = Vec3.ZERO; // Bugfix for past use of newHandleNull()
        Vec3 updated = new Vec3(movement.x, movement.y, motZ);
        instance#movement = updated;
    }
#else
    public (org.bukkit.util.Vector) Vec3 getMotVector() {
        int motX_raw = instance#motX_raw;
        int motY_raw = instance#motY_raw;
        int motZ_raw = instance#motZ_raw;
        return new Vec3( com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw),
                          com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw) );
    }

    public void setMotVector((org.bukkit.util.Vector) Vec3 motVector) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.x);
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.y);
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motVector.z);
        instance#motX_raw = motX_raw;
        instance#motY_raw = motY_raw;
        instance#motZ_raw = motZ_raw;
    }

    public double getMotX() {
        int motX_raw = instance#motX_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motX_raw);
    }

    public double getMotY() {
        int motY_raw = instance#motY_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motY_raw);
    }

    public double getMotZ() {
        int motZ_raw = instance#motZ_raw;
        return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializeVelocity(motZ_raw);
    }

    public void setMotX(double motX) {
        int motX_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motX);
        instance#motX_raw = motX_raw;
    }

    public void setMotY(double motY) {
        int motY_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motY);
        instance#motY_raw = motY_raw;
    }

    public void setMotZ(double motZ) {
        int motZ_raw = com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializeVelocity(motZ);
        instance#motZ_raw = motZ_raw;
    }
#endif

    <code>
    public boolean hasDataWatcherSupport() {
        return T.opt_dataWatcher.isAvailable();
    }

    @Deprecated
    public com.bergerkiller.bukkit.common.wrappers.DataWatcher getDataWatcher() {
        if (T.opt_dataWatcher.isAvailable()) {
            return T.opt_dataWatcher.get(getRaw());
        } else {
            return null;
        }
    }

    @Deprecated
    public void setDataWatcher(com.bergerkiller.bukkit.common.wrappers.DataWatcher dataWatcher) {
        if (T.opt_dataWatcher.isAvailable()) {
            T.opt_dataWatcher.set(getRaw(), dataWatcher);
        }
    }

    @Override
    public com.bergerkiller.bukkit.common.protocol.PacketType getPacketType() {
        return com.bergerkiller.bukkit.common.protocol.PacketType.OUT_ENTITY_SPAWN_LIVING;
    }

    public void setEntityUUID(UUID uuid) {
        if (T.entityUUID.isAvailable()) {
            T.entityUUID.set(getRaw(), uuid);
        }
    }

    public static boolean isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType type) {
        return type != null && type.entityTypeId != -1;
    }

    public static boolean isEntityTypeSupported(org.bukkit.entity.EntityType type) {
        return type != null && isCommonEntityTypeSupported(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public void setEntityType(org.bukkit.entity.EntityType type) {
        if (type == null) {
            throw new IllegalArgumentException("Input EntityType is null");
        }
        setCommonEntityType(com.bergerkiller.bukkit.common.entity.CommonEntityType.byEntityType(type));
    }

    public org.bukkit.entity.EntityType getEntityType() {
        return getCommonEntityType().entityType;
    }

    public float getYaw() {
        return getProtocolRotation(T.yaw_raw);
    }

    public float getPitch() {
        return getProtocolRotation(T.pitch_raw);
    }

    public float getHeadYaw() {
        return getProtocolRotation(T.headYaw_raw);
    }

    public void setYaw(float yaw) {
        setProtocolRotation(T.yaw_raw, yaw);
    }

    public void setPitch(float pitch) {
        setProtocolRotation(T.pitch_raw, pitch);
    }

    public void setHeadYaw(float headYaw) {
        setProtocolRotation(T.headYaw_raw, headYaw);
    }
    </code>
}

// No longer used since 1.21.5, where it uses a normal PacketPlayOutSpawnEntity
// The experience value (size of orb) is stored in the DataWatcher metadata instead
// This logic is incompatible so this class is not kept around for 1.21.5+
optional class ClientboundAddExperienceOrbPacket extends Packet {
#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.17
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:x;
    private optional double posY_1_10_2:y;
    private optional double posZ_1_10_2:z;
#elseif version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:b;
    private optional double posY_1_10_2:c;
    private optional double posZ_1_10_2:d;
#else
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

#if version >= 1.17
    private int experience:value;
#else
    private int experience:e;
#endif

    <code>
    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}

// Is ClientboundAddEntityPacket on 1.19+
class ClientboundAddPaintingPacket extends Packet {
#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

#if version >= 1.19
    // Art field doesn't exist. Is entity metadata now
    public static boolean hasArtField() {
        return false;
    }
    public (org.bukkit.Art) Object getArt() {
        return null;
    }
    public void setArt((org.bukkit.Art) Object art) {
    }

    // Position/Facing integrated in ClientboundAddEntityPacket itself as position/data
    public (IntVector3) BlockPos getPosition() {
  #if version >= 1.19.4
        return BlockPos.containing(instance.getX(), instance.getY(), instance.getZ());
  #else
        return new BlockPos(instance.getX(), instance.getY(), instance.getZ());
  #endif
    }
    public void setPosition((IntVector3) BlockPos position) {
        #require ClientboundAddPaintingPacket private final double posX:x;
        #require ClientboundAddPaintingPacket private final double posY:y;
        #require ClientboundAddPaintingPacket private final double posZ:z;
        instance#posX = (double)position.getX();
        instance#posY = (double)position.getY();
        instance#posZ = (double)position.getZ();
    }
    public (BlockFace) Direction getFacing() {
        return Direction.from3DDataValue(instance.getData());
    }
    public void setFacing((BlockFace) Direction facing) {
        #require ClientboundAddPaintingPacket private final int data;
        instance#data = facing.get3DDataValue();
    }
#else
    // Art field is an int index into a registry
    public static boolean hasArtField() {
        return true;
    }
  #select version >=
  #case 1.17:   #require ClientboundAddPaintingPacket private int artId:motive;
  #case 1.13:   #require ClientboundAddPaintingPacket private int artId:e;
  #case 1.9:    #require ClientboundAddPaintingPacket private String artName:e;
  #case else:   #require ClientboundAddPaintingPacket private String artName:d;
  #endselect
  #if version >= 1.13
    public (org.bukkit.Art) int getArt() {
        return instance#artId;
    }
    public void setArt((org.bukkit.Art) int art) {
        instance#artId = art;
    }
  #else
    public (org.bukkit.Art) String getArt() {
        return instance#artName;
    }
    public void setArt((org.bukkit.Art) String art) {
        instance#artName = art;
    }
  #endif

    // Position/Facing were fields of their own
  #if version >= 1.17
    #require ClientboundAddPaintingPacket private BlockPos position:pos;
    #require ClientboundAddPaintingPacket private Direction facing:direction;
  #elseif version >= 1.9
    #require ClientboundAddPaintingPacket private BlockPos position:c;
    #require ClientboundAddPaintingPacket private Direction facing:d;
  #else
    #require ClientboundAddPaintingPacket private BlockPos position:b;
    #require ClientboundAddPaintingPacket private Direction facing:c;
  #endif

    public (IntVector3) BlockPos getPosition() {
        return instance#position;
    }
    public void setPosition((IntVector3) BlockPos position) {
        instance#position = position;
    }
    public (BlockFace) Direction getFacing() {
        return instance#facing;
    }
    public void setFacing((BlockFace) Direction facing) {
        instance#facing = facing;
    }
#endif

    public void setEntityUUID(UUID uuid) {
#if version >= 1.17
        #require ClientboundAddPaintingPacket private UUID entityUUID:uuid;
        instance#entityUUID = uuid;
#elseif version >= 1.9
        #require ClientboundAddPaintingPacket private UUID entityUUID:b;
        instance#entityUUID = uuid;
#endif
    }
}

// This class is used from Minecraft 1.8 to 1.15.2
// Since Minecraft 1.16 the ClientboundAddEntityPacket packet is used instead,
// and this packet was removed.
optional class PacketPlayOutSpawnEntityWeather extends Packet {
    private int entityId:a;

#if version >= 1.9
    private optional int posX_1_8_8:###;
    private optional int posY_1_8_8:###;
    private optional int posZ_1_8_8:###;
    private optional double posX_1_10_2:b;
    private optional double posY_1_10_2:c;
    private optional double posZ_1_10_2:d;
#else
    private optional int posX_1_8_8:b;
    private optional int posY_1_8_8:c;
    private optional int posZ_1_8_8:d;
    private optional double posX_1_10_2:###;
    private optional double posY_1_10_2:###;
    private optional double posZ_1_10_2:###;
#endif

    private int type:e;

    <code>
    public double getPosX() {
        return getProtocolPosition(T.posX_1_8_8, T.posX_1_10_2);
    }

    public double getPosY() {
        return getProtocolPosition(T.posY_1_8_8, T.posY_1_10_2);
    }

    public double getPosZ() {
        return getProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2);
    }

    public void setPosX(double posX) {
        setProtocolPosition(T.posX_1_8_8, T.posX_1_10_2, posX);
    }

    public void setPosY(double posY) {
        setProtocolPosition(T.posY_1_8_8, T.posY_1_10_2, posY);
    }

    public void setPosZ(double posZ) {
        setProtocolPosition(T.posZ_1_8_8, T.posZ_1_10_2, posZ);
    }
    </code>
}