package net.minecraft.network.protocol.game;

import net.minecraft.network.protocol.Packet;
import net.minecraft.core.PositionAndRotation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.vehicle.minecart.NewMinecartBehavior;
import net.minecraft.world.entity.PositionPath;
import net.minecraft.world.entity.PositionStep;

import net.minecraft.network.protocol.game.VecDelta;
import com.bergerkiller.bukkit.common.internal.logic.ProtocolMath;

import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundMoveEntityPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundRotateHeadPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacketHandle
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundMoveVehiclePacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundMoveVehiclePacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundMoveMinecartPacketHandle;

import com.bergerkiller.generated.net.minecraft.world.entity.vehicle.minecart.NewMinecartBehaviorHandle;

import com.bergerkiller.bukkit.common.wrappers.RelativeFlags;

// Only >= MC 1.9
optional class ServerboundMoveVehiclePacket extends Packet {
#if version >= 26.3
    public double getPosX() { return instance.movingTo().position().x; }
    public double getPosY() { return instance.movingTo().position().y; }
    public double getPosZ() { return instance.movingTo().position().z; }
    public float getYaw() { return instance.movingTo().yRot(); }
    public float getPitch() { return instance.movingTo().xRot(); }
    public boolean isOnGround:onGround();

    public static (ServerboundMoveVehiclePacketHandle) ServerboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
        return new ServerboundMoveVehiclePacket(PositionAndRotation.of(new Vec3(posX, posY, posZ), yaw, pitch), onGround);
    }
#elseif version >= 1.21.4
    public double getPosX() { return instance.position().x; }
    public double getPosY() { return instance.position().y; }
    public double getPosZ() { return instance.position().z; }
    public float getYaw:yRot();
    public float getPitch:xRot();
    public boolean isOnGround:onGround();

    public static (ServerboundMoveVehiclePacketHandle) ServerboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
        return new ServerboundMoveVehiclePacket(new Vec3(posX, posY, posZ), yaw, pitch, onGround);
    }
#else
  #if version >= 1.17
    #require ServerboundMoveVehiclePacket private double posX:x;
    #require ServerboundMoveVehiclePacket private double posY:y;
    #require ServerboundMoveVehiclePacket private double posZ:z;
    #require ServerboundMoveVehiclePacket private float yaw:yRot;
    #require ServerboundMoveVehiclePacket private float pitch:xRot;
  #elseif version >= 1.9
    #require ServerboundMoveVehiclePacket private double posX:a;
    #require ServerboundMoveVehiclePacket private double posY:b;
    #require ServerboundMoveVehiclePacket private double posZ:c;
    #require ServerboundMoveVehiclePacket private float yaw:d;
    #require ServerboundMoveVehiclePacket private float pitch:e;
  #endif

    public double getPosX() { return instance#posX; }
    public double getPosY() { return instance#posY; }
    public double getPosZ() { return instance#posZ; }
    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }
    public boolean isOnGround() { return false; /* not supported */ }

    public static (ServerboundMoveVehiclePacketHandle) ServerboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
  #if version >= 1.17
        #require ServerboundMoveVehiclePacket private ServerboundMoveVehiclePacket createVehicleMovePacket:<init>(net.minecraft.network.FriendlyByteBuf packetdataserializer);
        ServerboundMoveVehiclePacket packet = #createVehicleMovePacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #else
        ServerboundMoveVehiclePacket packet = new ServerboundMoveVehiclePacket();
  #endif
        packet#posX = posX;
        packet#posY = posY;
        packet#posZ = posZ;
        packet#yaw = yaw;
        packet#pitch = pitch;
        //onGround is ignored (doesn't exist)
        return packet;
    }
#endif
}

// Only >= MC 1.9
optional class ClientboundMoveVehiclePacket extends Packet {
#if version >= 26.3
    public double getPosX() { return instance.movingTo().position().x; }
    public double getPosY() { return instance.movingTo().position().y; }
    public double getPosZ() { return instance.movingTo().position().z; }
    public float getYaw() { return instance.movingTo().yRot(); }
    public float getPitch() { return instance.movingTo().xRot(); }

    public static (ClientboundMoveVehiclePacketHandle) ClientboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch) {
        return new ClientboundMoveVehiclePacket(PositionAndRotation.of(new Vec3(posX, posY, posZ), yaw, pitch));
    }
#elseif version >= 1.21.4
    public double getPosX() { return instance.position().x; }
    public double getPosY() { return instance.position().y; }
    public double getPosZ() { return instance.position().z; }
    public float getYaw:yRot();
    public float getPitch:xRot();

    public static (ClientboundMoveVehiclePacketHandle) ClientboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch) {
        return new ClientboundMoveVehiclePacket(new Vec3(posX, posY, posZ), yaw, pitch);
    }
#else
  #if version >= 1.17
    #require ClientboundMoveVehiclePacket private double posX:x;
    #require ClientboundMoveVehiclePacket private double posY:y;
    #require ClientboundMoveVehiclePacket private double posZ:z;
    #require ClientboundMoveVehiclePacket private float yaw:yRot;
    #require ClientboundMoveVehiclePacket private float pitch:xRot;
  #elseif version >= 1.9
    #require ClientboundMoveVehiclePacket private double posX:a;
    #require ClientboundMoveVehiclePacket private double posY:b;
    #require ClientboundMoveVehiclePacket private double posZ:c;
    #require ClientboundMoveVehiclePacket private float yaw:d;
    #require ClientboundMoveVehiclePacket private float pitch:e;
  #endif

    public double getPosX() { return instance#posX; }
    public double getPosY() { return instance#posY; }
    public double getPosZ() { return instance#posZ; }
    public float getYaw() { return instance#yaw; }
    public float getPitch() { return instance#pitch; }

    public static (ClientboundMoveVehiclePacketHandle) ClientboundMoveVehiclePacket createNew(double posX, double posY, double posZ, float yaw, float pitch) {
  #if version >= 1.17
        #require ClientboundMoveVehiclePacket private ClientboundMoveVehiclePacket createVehicleMovePacket:<init>(net.minecraft.network.FriendlyByteBuf packetdataserializer);
        ClientboundMoveVehiclePacket packet = #createVehicleMovePacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
  #else
        ClientboundMoveVehiclePacket packet = new ClientboundMoveVehiclePacket();
  #endif
        packet#posX = posX;
        packet#posY = posY;
        packet#posZ = posZ;
        packet#yaw = yaw;
        packet#pitch = pitch;
        return packet;
    }
#endif
}

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

    // Reflection needed on some versions of Minecraft, and also for creating a new packet
#if version >= 1.21.2
    // Record class, no fields need to be set using reflection
#elseif version >= 1.17
    #require ClientboundEntityPositionSyncPacket private final int entityId:id;
    #require ClientboundEntityPositionSyncPacket private final double posX:x;
    #require ClientboundEntityPositionSyncPacket private final double posY:y;
    #require ClientboundEntityPositionSyncPacket private final double posZ:z;
    #require ClientboundEntityPositionSyncPacket private final byte encodedYaw:yRot;
    #require ClientboundEntityPositionSyncPacket private final byte encodedPitch:xRot;
    #require ClientboundEntityPositionSyncPacket private final boolean onGround;
#else
    #require ClientboundEntityPositionSyncPacket private int entityId:a;
  #if version >= 1.9
    #require ClientboundEntityPositionSyncPacket private double posX:b;
    #require ClientboundEntityPositionSyncPacket private double posY:c;
    #require ClientboundEntityPositionSyncPacket private double posZ:d;
  #else
    #require ClientboundEntityPositionSyncPacket private int encodedPosX:b;
    #require ClientboundEntityPositionSyncPacket private int encodedPosY:c;
    #require ClientboundEntityPositionSyncPacket private int encodedPosZ:d;
  #endif
    #require ClientboundEntityPositionSyncPacket private byte encodedYaw:e;
    #require ClientboundEntityPositionSyncPacket private byte encodedPitch:f;
    #require ClientboundEntityPositionSyncPacket private boolean onGround:g;
#endif

#if version >= 26.3
    public int getEntityId:id();
    public float getYaw() { return instance.yRot(); }
    public float getPitch() { return instance.xRot(); }
    public boolean isOnGround:onGround();

    public ClientboundEntityPositionSyncPacketHandle.Position getPosition() {
        PositionPath path = instance.position();
        if (path instanceof PositionPath$Linear) {
            PositionPath$Linear linear = (PositionPath$Linear) path;
            Vec3 pos = linear.endPosition();
            return new ClientboundEntityPositionSyncPacketHandle$LinearPosition(pos.x, pos.y, pos.z);
        } else if (path instanceof PositionPath$Stepped) {
            PositionPath$Stepped stepped = (PositionPath$Stepped) path;
            java.util.List steps = stepped.steps();
            java.util.List bkclSteps = new java.util.ArrayList(steps.size());
            for (java.util.Iterator iter = steps.iterator(); iter.hasNext();) {
                PositionStep step = (PositionStep) iter.next();
                Vec3 stepPos = step.position();
                bkclSteps.add(new ClientboundEntityPositionSyncPacketHandle$SteppedPosition$Step(stepPos.x, stepPos.y, stepPos.z, step.tickOffset()));
            }
            return new ClientboundEntityPositionSyncPacketHandle$SteppedPosition(bkclSteps);
        } else {
            throw new IllegalArgumentException("Unsupported Position type: " + (path == null ? "null" : path.getClass().getName()));
        }
    }

    // Not really used anymore, but here for completion sake
    public int getEncodedYaw() {
        return ProtocolMath.serializeRotation(instance.yRot());
    }
    public int getEncodedPitch() {
        return ProtocolMath.serializeRotation(instance.xRot());
    }
#elseif version >= 1.21.2
    public int getEntityId:id();
    public float getYaw() { return instance.values().yRot(); }
    public float getPitch() { return instance.values().xRot(); }
    public boolean isOnGround:onGround();

    public ClientboundEntityPositionSyncPacketHandle.Position getPosition() {
        PositionMoveRotation values = instance.values();
        return new ClientboundEntityPositionSyncPacketHandle$LinearPosition(values.position().x, values.position().y, values.position().z);
    }

    // Not really used anymore, but here for completion sake
    public int getEncodedYaw() {
        return ProtocolMath.serializeRotation(instance.values().yRot());
    }
    public int getEncodedPitch() {
        return ProtocolMath.serializeRotation(instance.values().xRot());
    }
#elseif version >= 1.18
    public int getEntityId:getId();
    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance.getyRot());
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance.getxRot());
    }
    public boolean isOnGround();

    public ClientboundEntityPositionSyncPacketHandle.Position getPosition() {
        return new ClientboundEntityPositionSyncPacketHandle$LinearPosition(instance.getX(), instance.getY(), instance.getZ());
    }

    public int getEncodedYaw() { return (int) instance.getyRot() & 0xFF; }
    public int getEncodedPitch() { return (int) instance.getxRot() & 0xFF; }
#elseif version >= 1.17
    public int getEntityId:b();
    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance.f());
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance.g());
    }
    public boolean isOnGround:h();

    public ClientboundEntityPositionSyncPacketHandle.Position getPosition() {
        return new ClientboundEntityPositionSyncPacketHandle$LinearPosition(instance.c(), instance.d(), instance.e());
    }

    public int getEncodedYaw() { return (int) instance.f() & 0xFF; }
    public int getEncodedPitch() { return (int) instance.g() & 0xFF; }
#else
    // Relies on reflection to access the private fields
    public int getEntityId() { return instance#entityId; }

    public float getYaw() {
        return ProtocolMath.deserializeRotation( instance#encodedYaw );
    }
    public float getPitch() {
        return ProtocolMath.deserializeRotation( instance#encodedPitch );
    }

    public boolean isOnGround() { return instance#onGround; }

    public ClientboundEntityPositionSyncPacketHandle.Position getPosition() {
  #if version >= 1.9
        return new ClientboundEntityPositionSyncPacketHandle$LinearPosition(instance#posX, instance#posY, instance#posZ);
  #else
        return new ClientboundEntityPositionSyncPacketHandle$LinearPositionLegacy(instance#encodedPosX, instance#encodedPosY, instance#encodedPosZ);
  #endif
    }

    public int getEncodedYaw() { return (int) instance#encodedYaw & 0xFF; }
    public int getEncodedPitch() { return (int) instance#encodedPitch & 0xFF; }
#endif

    public static (ClientboundEntityPositionSyncPacketHandle) ClientboundEntityPositionSyncPacket createNewForEntity((org.bukkit.entity.Entity) Entity entity) {
#if version >= 26.3
        PositionMoveRotation values = PositionMoveRotation.of(entity);
        PositionPath path = PositionPath.of(values.position());
        return new ClientboundEntityPositionSyncPacket(entity.getId(), path, values.yRot(), values.xRot(), entity.onGround());
#elseif version >= 1.21.2
        PositionMoveRotation values = PositionMoveRotation.of(entity);
        return new ClientboundEntityPositionSyncPacket(entity.getId(), values, entity.onGround());
#else
        return new ClientboundEntityPositionSyncPacket(entity);
#endif
    }

    public static (ClientboundEntityPositionSyncPacketHandle) ClientboundEntityPositionSyncPacket createNew(int entityId, ClientboundEntityPositionSyncPacketHandle.Position pos, float yaw, float pitch, boolean onGround) {
#if version >= 26.3
        // Since 26.3, the position supports multi-step changes. If the position is a stepped change, we can use it directly. Otherwise, we convert it to a linear change.
        PositionPath path;
        if (pos instanceof ClientboundEntityPositionSyncPacketHandle$LinearPosition) {
            ClientboundEntityPositionSyncPacketHandle$LinearPosition linear = (ClientboundEntityPositionSyncPacketHandle$LinearPosition) pos;
            path = PositionPath.of(new Vec3(linear.getX(), linear.getY(), linear.getZ()));
        } else if (pos instanceof ClientboundEntityPositionSyncPacketHandle$SteppedPosition) {
            ClientboundEntityPositionSyncPacketHandle$SteppedPosition stepped = (ClientboundEntityPositionSyncPacketHandle$SteppedPosition) pos;
            if (stepped.getSteps().isEmpty()) {
                throw new IllegalArgumentException("SteppedPosition must have at least one step");
            }
            java.util.List steps = new java.util.ArrayList(stepped.getSteps().size());
            for (java.util.Iterator iter = stepped.getSteps().iterator(); iter.hasNext();) {
                ClientboundEntityPositionSyncPacketHandle$SteppedPosition$Step step = (ClientboundEntityPositionSyncPacketHandle$SteppedPosition$Step) iter.next();
                steps.add(new PositionStep(new Vec3(step.getX(), step.getY(), step.getZ()), step.ticks));
            }
            path = PositionPath.stepped(steps);
        } else {
            throw new IllegalArgumentException("Unsupported Position type: " + (pos == null ? "null" : pos.getClass().getName()));
        }

        return new ClientboundEntityPositionSyncPacket(entityId, path, yaw, pitch, onGround);

#elseif version >= 1.9
        if (!(pos instanceof ClientboundEntityPositionSyncPacketHandle$LinearPosition)) {
            throw new IllegalArgumentException("Position must be a LinearPosition for Minecraft 1.9 - 26.2");
        }

        ClientboundEntityPositionSyncPacketHandle$LinearPosition linear = (ClientboundEntityPositionSyncPacketHandle$LinearPosition) pos;

  #if version >= 1.21.2
        PositionMoveRotation values = new PositionMoveRotation(new Vec3(linear.getX(), linear.getY(), linear.getZ()), new Vec3(0.0, 0.0, 0.0), yaw, pitch);
        return new ClientboundEntityPositionSyncPacket(entityId, values, onGround);

  #else
        // Create a new packet and use reflection to set the fields
        // Unfortunately, no nice constructor :(
    #if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundEntityPositionSyncPacket private ClientboundEntityPositionSyncPacket createTeleportPacket:<init>(net.minecraft.network.FriendlyByteBuf serializer);
        ClientboundEntityPositionSyncPacket packet = #createTeleportPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
    #elseif version >= 1.17
        ClientboundEntityPositionSyncPacket packet = new ClientboundEntityPositionSyncPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
    #else
        ClientboundEntityPositionSyncPacket packet = new ClientboundEntityPositionSyncPacket();
    #endif

        byte encodedYaw = (byte) ProtocolMath.serializeRotation(yaw);
        byte encodedPitch = (byte) ProtocolMath.serializeRotation(pitch);

        packet#entityId = entityId;
        packet#posX = linear.getX();
        packet#posY = linear.getY();
        packet#posZ = linear.getZ();
        packet#encodedYaw = encodedYaw;
        packet#encodedPitch = encodedPitch;
        packet#onGround = onGround;
        return packet;
  #endif

#else
        // 1.8: legacy int encoded
        if (!(pos instanceof ClientboundEntityPositionSyncPacketHandle$LinearPositionLegacy)) {
            throw new IllegalArgumentException("Position must be a LinearPositionLegacy for Minecraft 1.8");
        }

        ClientboundEntityPositionSyncPacketHandle$LinearPositionLegacy linear = (ClientboundEntityPositionSyncPacketHandle$LinearPositionLegacy) pos;
        byte encodedYaw = (byte) ProtocolMath.serializeRotation(yaw);
        byte encodedPitch = (byte) ProtocolMath.serializeRotation(pitch);
        return new ClientboundEntityPositionSyncPacket(entityId, linear.encodedX, linear.encodedY, linear.encodedZ, encodedYaw, encodedPitch, onGround);
#endif
    }

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

    public static ClientboundEntityPositionSyncPacketHandle createNew(int entityId, double posX, double posY, double posZ, float yaw, float pitch, boolean onGround) {
        return createNew(entityId, Position.encodeLinear(posX, posY, posZ), yaw, pitch, onGround);
    }

    /** @deprecated Use {@link #getPosition()} instead */
    @Deprecated
    public double getPosX() {
        return getPosition().getX();
    }

    /** @deprecated Use {@link #getPosition()} instead */
    @Deprecated
    public double getPosY() {
        return getPosition().getY();
    }

    /** @deprecated Use {@link #getPosition()} instead */
    @Deprecated
    public double getPosZ() {
        return getPosition().getZ();
    }

    /**
     * Represents the updated position of an entity, encoded in the way that the Minecraft protocol expects.
     * The encoding used depends on the Minecraft version.
     */
    public interface Position {
        Position UNSET = new LinearPosition(Double.NaN, Double.NaN, Double.NaN);

        /**
         * Gets the X-component of the new position the client will teleport the entity to
         *
         * @return new position X
         */
        double getX();

        /**
         * Gets the Y-component of the new position the client will teleport the entity to
         *
         * @return new position Y
         */
        double getY();

        /**
         * Gets the Z-component of the new position the client will teleport the entity to
         *
         * @return new position Z
         */
        double getZ();

        /**
         * Creates a new, initially-empty, stepped position update. Add steps to it (chaining), then assign it
         * to a ClientboundEntityPositionSyncPacket packet position field.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @return stepped position
         */
        static SteppedPosition steppedBuilder() {
            return new SteppedPosition();
        }

        /**
         * Creates a new Position instance that encodes the given position values, teleporting the
         * the player towards that position following linear interpolation.
         * The encoding used depends on the Minecraft version.
         *
         * @param x New position to encode, X-coordinate
         * @param y New position to encode, Y-coordinate
         * @param z New position to encode, Z-coordinate
         * @return position instance that can be assigned to a ClientboundEntityPositionSyncPacket packet position field
         */
        static Position encodeLinear(double x, double y, double z) {
            if (com.bergerkiller.bukkit.common.internal.CommonCapabilities.PROTOCOL_TELEPORT_IS_DOUBLE_POSITION) {
                return new LinearPosition(x, y, z);
            } else {
                int encodedX = (int) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(x);
                int encodedY = (int) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(y);
                int encodedZ = (int) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(z);
                return new LinearPositionLegacy(encodedX, encodedY, encodedZ);
            }
        }
    }

    /**
     * Encoding used since 1.9, where the position is encoded as double.
     * Interpolates the movement over the standard entity interpolation tick time (usually 3 ticks),
     * and moves the entity linearly.
     */
    public static class LinearPosition implements Position {
        private final double x;
        private final double y;
        private final double z;

        public LinearPosition(double x, double y, double z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        @Override
        public double getX() {
            return x;
        }

        @Override
        public double getY() {
            return y;
        }

        @Override
        public double getZ() {
            return z;
        }
    }

    /**
     * Encoding used before 1.9, where the position is encoded as an int..
     * Interpolates the movement over the standard entity interpolation tick time (usually 3 ticks),
     * and moves the entity linearly.
     */
    public static final class LinearPositionLegacy implements Position {
        public final int encodedX;
        public final int encodedY;
        public final int encodedZ;

        public LinearPositionLegacy(int encodedX, int encodedY, int encodedZ) {
            this.encodedX = encodedX;
            this.encodedY = encodedY;
            this.encodedZ = encodedZ;
        }

        @Override
        public double getX() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedX);
        }

        @Override
        public double getY() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedY);
        }

        @Override
        public double getZ() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedZ);
        }
    }

    /**
     * Encoding used since 26.3, where the position is encoded as a list of steps.
     * Each step has its own absolute position and duration in ticks to interpolate over.
     * Multi-step changes are only supported on Minecraft 26.3 and later.
     */
    public static final class SteppedPosition implements Position {
        public final java.util.List<Step> steps;

        public SteppedPosition() {
            this(new java.util.ArrayList<>());
        }

        public SteppedPosition(java.util.List<Step> steps) {
            this.steps = steps;
        }

        /**
         * Gets the list of steps that make up this multi-step position update.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @return list of steps
         */
        public java.util.List<Step> getSteps() {
            return steps;
        }

        /**
         * Adds a new step to this multi-step change.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @param step Step to add
         * @return this, for chaining
         */
        public SteppedPosition addStep(Step step) {
            steps.add(step);
            return this;
        }

        /**
         * Adds a new position step to this multi-step position update, encoding the given position and tick duration as a single step.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @param x Step postion, X-coordinate
         * @param y Step postion, Y-coordinate
         * @param z Step postion, Z-coordinate
         * @param ticks Duration of this step in ticks
         * @return this, for chaining
         */
        public SteppedPosition addStep(double x, double y, double z, int ticks) {
            steps.add(new Step(x, y, z, ticks));
            return this;
        }

        @Override
        public double getX() {
            int count = steps.size();
            return count == 0 ? Double.NaN : steps.get(count - 1).getX();
        }

        @Override
        public double getY() {
            int count = steps.size();
            return count == 0 ? Double.NaN : steps.get(count - 1).getY();
        }

        @Override
        public double getZ() {
            int count = steps.size();
            return count == 0 ? Double.NaN : steps.get(count - 1).getZ();
        }

        /**
         * Gets the total number of ticks that this multi-step change will take to complete.
         *
         * @return total ticks
         */
        public int getTicks() {
            int sum = 0;
            for (Step change : steps) {
                sum += change.ticks;
            }
            return sum;
        }

        /**
         * Represents a single step in a multi-step position teleport.
         * Each step has its own position and duration in ticks.
         */
        public static final class Step extends LinearPosition {
            public final int ticks;

            public Step(double x, double y, double z, int ticks) {
                super(x, y, z);
                this.ticks = ticks;
            }
        }
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private int entityId;
        private Position pos = Position.UNSET;
        private float yaw, pitch;
        private boolean onGround;

        public Builder entityId(int entityId) { this.entityId = entityId; return this; }

        /** @deprecated Use the position() style methods instead */
        @Deprecated
        public Builder posX(double posX) {
            return position(posX, pos.getY(), pos.getZ());
        }
        /** @deprecated Use the position() style methods instead */
        @Deprecated
        public Builder posY(double posY) {
            return position(pos.getX(), posY, pos.getZ());
        }
        /** @deprecated Use the position() style methods instead */
        @Deprecated
        public Builder posZ(double posZ) {
            return position(pos.getX(), pos.getY(), posZ);
        }

        public Builder position(org.bukkit.util.Vector position) {
            return position(position.getX(), position.getY(), position.getZ());
        }
        public Builder position(double x, double y, double z) {
            return position(Position.encodeLinear(x, y, z));
        }
        public Builder position(Position pos) { this.pos = pos; return this; }
        public Builder yaw(float yaw) { this.yaw = yaw; return this; }
        public Builder pitch(float pitch) { this.pitch = pitch; return this; }
        public Builder rotation(float yaw, float pitch) {
            this.yaw = yaw;
            this.pitch = pitch;
            return this;
        }
        public Builder onGround(boolean onGround) { this.onGround = onGround; return this; }

        public ClientboundEntityPositionSyncPacketHandle create() {
            return ClientboundEntityPositionSyncPacketHandle.createNew(entityId, pos, yaw, pitch, onGround);
        }
    }
    </code>
}

// Only >= MC 1.9
optional class ServerboundPaddleBoatPacket {
#if version >= 1.17
    private boolean leftPaddle:left;
    private boolean rightPaddle:right;
#else
    private boolean leftPaddle:a;
    private boolean rightPaddle:b;
#endif
}

class ClientboundSetEntityMotionPacket extends Packet {
#if version >= 1.17
    private int entityId:id;
#else
    private int entityId:a;
#endif

    public static (ClientboundSetEntityMotionPacketHandle) ClientboundSetEntityMotionPacket createNew(int entityId, double motX, double motY, double motZ) {
#if version >= 1.14
        return new ClientboundSetEntityMotionPacket(entityId, new Vec3(motX, motY, motZ));
#else
        return new ClientboundSetEntityMotionPacket(entityId, motX, motY, motZ);
#endif
    }

    public (ClientboundSetEntityMotionPacketHandle) ClientboundSetEntityMotionPacket((org.bukkit.entity.Entity) Entity entity);

#if version >= 26.1
    public (org.bukkit.util.Vector) Vec3 getMotVector:movement();
    public double getMotX() { return instance.movement().x; }
    public double getMotY() { return instance.movement().y; }
    public double getMotZ() { return instance.movement().z; }
#elseif version >= 1.21.9
    public (org.bukkit.util.Vector) Vec3 getMotVector:getMovement();
    public double getMotX() { return instance.getMovement().x; }
    public double getMotY() { return instance.getMovement().y; }
    public double getMotZ() { return instance.getMovement().z; }
#else
  #if version >= 1.17
    #require ClientboundSetEntityMotionPacket private int motX_raw:xa;
    #require ClientboundSetEntityMotionPacket private int motY_raw:ya;
    #require ClientboundSetEntityMotionPacket private int motZ_raw:za;
  #else
    #require ClientboundSetEntityMotionPacket private int motX_raw:b;
    #require ClientboundSetEntityMotionPacket private int motY_raw:c;
    #require ClientboundSetEntityMotionPacket private int motZ_raw:d;
  #endif

    public (org.bukkit.util.Vector) Vec3 getMotVector:getMovement() {
        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 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);
    }
#endif

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

class ClientboundMoveEntityPacket extends Packet {
#if version >= 1.17
    protected int entityId;
    protected boolean onGround;
#else
    protected int entityId:a;
    protected boolean onGround:g;
#endif

#if version >= 26.3
    #require ClientboundMoveEntityPacket protected final VecDelta delta;

    public ClientboundMoveEntityPacketHandle.PositionChange getPositionChange() {
        VecDelta delta = instance.getPositionDelta();
        if (delta instanceof VecDelta.Linear) {
            VecDelta$Linear linear = (VecDelta$Linear) delta;
            return new ClientboundMoveEntityPacketHandle$LinearPositionChange(linear.xa(), linear.ya(), linear.za());
        } else if (delta instanceof VecDelta.Stepped) {
            VecDelta$Stepped stepped = (VecDelta$Stepped) delta;
            java.util.List steps = stepped.steps();
            java.util.List bkclSteps = new java.util.ArrayList(steps.size());
            for (java.util.Iterator iter = steps.iterator(); iter.hasNext();) {
                VecDelta$Stepped$DeltaStep step = (VecDelta$Stepped$DeltaStep) iter.next();
                bkclSteps.add(new ClientboundMoveEntityPacketHandle$SteppedPositionChange$Step(
                    step.xa(), step.ya(), step.za(), step.ticks()
                ));
            }
            return new ClientboundMoveEntityPacketHandle$SteppedPositionChange(bkclSteps);
        } else {
            throw new IllegalArgumentException("Unsupported PositionChange type: " + (delta == null ? "null" : delta.getClass().getName()));
        }
    }

    public void setPositionChange(ClientboundMoveEntityPacketHandle.PositionChange change) {
        if (change instanceof ClientboundMoveEntityPacketHandle$LinearPositionChange) {
            ClientboundMoveEntityPacketHandle$LinearPositionChange linearChange = (ClientboundMoveEntityPacketHandle$LinearPositionChange) change;

            VecDelta$Linear linear = new VecDelta$Linear(linearChange.encodedDeltaX, linearChange.encodedDeltaY, linearChange.encodedDeltaZ);
            instance#delta = linear;
        } else if (change instanceof ClientboundMoveEntityPacketHandle.SteppedPositionChange) {
            ClientboundMoveEntityPacketHandle$SteppedPositionChange steppedChange = (ClientboundMoveEntityPacketHandle$SteppedPositionChange) change;

            java.util.List steps = new java.util.ArrayList(steppedChange.steps.size());
            for (java.util.Iterator iter = steppedChange.getSteps().iterator(); iter.hasNext();) {
                ClientboundMoveEntityPacketHandle$SteppedPositionChange$Step step = (ClientboundMoveEntityPacketHandle$SteppedPositionChange$Step) iter.next();
                steps.add(new VecDelta$Stepped$DeltaStep(step.encodedDeltaX, step.encodedDeltaY, step.encodedDeltaZ, step.ticks));
            }
            VecDelta$Stepped stepped = new VecDelta$Stepped(steps);
            instance#delta = stepped;
        } else if (change == null) {
            throw new IllegalArgumentException("PositionChange cannot be null");
        } else {
            throw new IllegalArgumentException("Unsupported PositionChange type: " + change.getClass().getName());
        }
    }

#elseif version >= 1.14
  #if version >= 1.17
    #require ClientboundMoveEntityPacket protected short dx:xa;
    #require ClientboundMoveEntityPacket protected short dy:ya;
    #require ClientboundMoveEntityPacket protected short dz:za;
  #else
    #require ClientboundMoveEntityPacket protected short dx:b;
    #require ClientboundMoveEntityPacket protected short dy:c;
    #require ClientboundMoveEntityPacket protected short dz:d;
  #endif

    public ClientboundMoveEntityPacketHandle.PositionChange getPositionChange() {
        return new ClientboundMoveEntityPacketHandle$LinearPositionChange(instance#dx, instance#dy, instance#dz);
    }

    public void setPositionChange(ClientboundMoveEntityPacketHandle.PositionChange change) {
        if (change instanceof ClientboundMoveEntityPacketHandle$LinearPositionChange) {
            ClientboundMoveEntityPacketHandle$LinearPositionChange linear = (ClientboundMoveEntityPacketHandle$LinearPositionChange) change;
            instance#dx = linear.encodedDeltaX;
            instance#dy = linear.encodedDeltaY;
            instance#dz = linear.encodedDeltaZ;
        } else if (change == null) {
            throw new IllegalArgumentException("PositionChange cannot be null");
        } else {
            throw new IllegalArgumentException("Unsupported PositionChange type: " + change.getClass().getName());
        }
    }
#elseif version >= 1.9
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected int dx:b;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected int dy:c;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected int dz:d;

    public ClientboundMoveEntityPacketHandle.PositionChange getPositionChange() {
        return new ClientboundMoveEntityPacketHandle$LinearPositionChange((short) instance#dx, (short) instance#dy, (short) instance#dz);
    }

    public void setPositionChange(ClientboundMoveEntityPacketHandle.PositionChange change) {
        if (change instanceof ClientboundMoveEntityPacketHandle$LinearPositionChange) {
            ClientboundMoveEntityPacketHandle$LinearPositionChange linear = (ClientboundMoveEntityPacketHandle$LinearPositionChange) change;
            instance#dx = (int)linear.encodedDeltaX;
            instance#dy = (int)linear.encodedDeltaY;
            instance#dz = (int)linear.encodedDeltaZ;
        } else if (change == null) {
            throw new IllegalArgumentException("PositionChange cannot be null");
        } else {
            throw new IllegalArgumentException("Unsupported PositionChange type: " + change.getClass().getName());
        }
    }
#else
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte dx:b;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte dy:c;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte dz:d;

    public ClientboundMoveEntityPacketHandle.PositionChange getPositionChange() {
        return new ClientboundMoveEntityPacketHandle$LinearPositionChangeLegacy(instance#dx, instance#dy, instance#dz);
    }

    public void setPositionChange(ClientboundMoveEntityPacketHandle.PositionChange change) {
        if (change instanceof ClientboundMoveEntityPacketHandle$LinearPositionChangeLegacy) {
            ClientboundMoveEntityPacketHandle$LinearPositionChangeLegacy linear = (ClientboundMoveEntityPacketHandle$LinearPositionChangeLegacy) change;
            instance#dx = linear.encodedDeltaX;
            instance#dy = linear.encodedDeltaY;
            instance#dz = linear.encodedDeltaZ;
        } else if (change == null) {
            throw new IllegalArgumentException("PositionChange cannot be null");
        } else {
            throw new IllegalArgumentException("Unsupported PositionChange type: " + change.getClass().getName());
        }
    }
#endif

#if version >= 1.17
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte yaw:yRot;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte pitch:xRot;
#else
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte yaw:e;
    #require net.minecraft.network.protocol.game.ClientboundMoveEntityPacket protected byte pitch:f;
#endif

    public float getYaw() {
        return ProtocolMath.deserializeRotation(instance#yaw);
    }

    public float getPitch() {
        return ProtocolMath.deserializeRotation(instance#pitch);
    }

    public void setYaw(float yaw) {
        instance#yaw = (byte) ProtocolMath.serializeRotation(yaw);
    }

    public void setPitch(float pitch) {
        instance#pitch = (byte) ProtocolMath.serializeRotation(pitch);
    }

    <code>
    /**
     * Deprecated: is not actually a delta, use getYaw() instead
     */
    @Deprecated
    public float getDeltaYaw() {
        return getYaw();
    }

    /**
     * Deprecated: is not actually a delta, use getPitch() instead
     */
    @Deprecated
    public float getDeltaPitch() {
        return getPitch();
    }

    /**
     * Deprecated: is not actually a delta, use setYaw(yaw) instead
     */
    @Deprecated
    public void setDeltaYaw(float deltaYaw) {
        setYaw(deltaYaw);
    }

    /**
     * Deprecated: is not actually a delta, use setPitch(pitch) instead
     */
    @Deprecated
    public void setDeltaPitch(float deltaPitch) {
        setPitch(deltaPitch);
    }

    /** @deprecated Use getPositionChange() instead */
    @Deprecated
    public double getDeltaX() {
        return getPositionChange().getDeltaX();
    }

    /** @deprecated Use getPositionChange() instead */
    @Deprecated
    public double getDeltaY() {
        return getPositionChange().getDeltaY();
    }

    /** @deprecated Use getPositionChange() instead */
    @Deprecated
    public double getDeltaZ() {
        return getPositionChange().getDeltaZ();
    }

    /** @deprecated Use {@link #setPositionChange(PositionChange)} instead */
    @Deprecated
    public void setDeltaX(double dx) {
        PositionChange change = getPositionChange();
        setPositionChange(PositionChange.encodeLinearChange(dx, change.getDeltaY(), change.getDeltaZ()));
    }

    /** @deprecated Use {@link #setPositionChange(PositionChange)} instead */
    @Deprecated
    public void setDeltaY(double dy) {
        PositionChange change = getPositionChange();
        setPositionChange(PositionChange.encodeLinearChange(change.getDeltaX(), dy, change.getDeltaZ()));
    }

    /** @deprecated Use {@link #setPositionChange(PositionChange)} instead */
    @Deprecated
    public void setDeltaZ(double dz) {
        PositionChange change = getPositionChange();
        setPositionChange(PositionChange.encodeLinearChange(change.getDeltaX(), change.getDeltaY(), dz));
    }

    /**
     * Represents a change in position of an entity, encoded in the way that the Minecraft protocol expects.
     * The encoding used depends on the Minecraft version.
     */
    public interface PositionChange {
        /**
         * Gets the X-component of the movement change the client will perform with this position change
         *
         * @return delta movement X
         */
        double getDeltaX();

        /**
         * Gets the Y-component of the movement change the client will perform with this position change
         *
         * @return delta movement Y
         */
        double getDeltaY();

        /**
         * Gets the Z-component of the movement change the client will perform with this position change
         *
         * @return delta movement Z
         */
        double getDeltaZ();

        /**
         * Creates a new, initially-empty, stepped position change. Add steps to it (chaining), then assign it
         * to a ClientboundMoveEntityPacket packet change field.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @return stepped position change
         */
        static SteppedPositionChange steppedBuilder() {
            return new SteppedPositionChange();
        }

        /**
         * Creates a new Change instance that encodes the given delta values.
         * The encoding used depends on the Minecraft version.
         *
         * @param deltaX Delta movement to encode, X-coordinate
         * @param deltaY Delta movement to encode, Y-coordinate
         * @param deltaZ Delta movement to encode, Z-coordinate
         * @return change instance that can be assigned to a ClientboundMoveEntityPacket packet change field
         */
        static PositionChange encodeLinearChange(double deltaX, double deltaY, double deltaZ) {
            if (com.bergerkiller.bukkit.common.internal.CommonCapabilities.PROTOCOL_MOVEMENT_IS_SHORT_DELTA) {
                short encodedDeltaX = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaX);
                short encodedDeltaY = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaY);
                short encodedDeltaZ = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaZ);
                return new LinearPositionChange(encodedDeltaX, encodedDeltaY, encodedDeltaZ);
            } else {
                byte encodedDeltaX = (byte) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(deltaX);
                byte encodedDeltaY = (byte) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(deltaY);
                byte encodedDeltaZ = (byte) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_8_8(deltaZ);
                return new LinearPositionChangeLegacy(encodedDeltaX, encodedDeltaY, encodedDeltaZ);
            }
        }
    }

    /**
     * Encoding used since 1.9, where the delta is encoded as a short.
     * Interpolates the movement over the standard entity interpolation tick time (usually 3 ticks),
     * and moves the entity linearly.
     */
    public static class LinearPositionChange implements PositionChange {
        public final short encodedDeltaX;
        public final short encodedDeltaY;
        public final short encodedDeltaZ;

        public LinearPositionChange(short encodedDeltaX, short encodedDeltaY, short encodedDeltaZ) {
            this.encodedDeltaX = encodedDeltaX;
            this.encodedDeltaY = encodedDeltaY;
            this.encodedDeltaZ = encodedDeltaZ;
        }

        @Override
        public double getDeltaX() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_9(encodedDeltaX);
        }

        @Override
        public double getDeltaY() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_9(encodedDeltaY);
        }

        @Override
        public double getDeltaZ() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_9(encodedDeltaZ);
        }
    }

    /**
     * Encoding used before 1.9, where the delta is encoded as a byte.
     * Interpolates the movement over the standard entity interpolation tick time (usually 3 ticks),
     * and moves the entity linearly.
     */
    public static final class LinearPositionChangeLegacy implements PositionChange {
        public final byte encodedDeltaX;
        public final byte encodedDeltaY;
        public final byte encodedDeltaZ;

        public LinearPositionChangeLegacy(byte encodedDeltaX, byte encodedDeltaY, byte encodedDeltaZ) {
            this.encodedDeltaX = encodedDeltaX;
            this.encodedDeltaY = encodedDeltaY;
            this.encodedDeltaZ = encodedDeltaZ;
        }

        @Override
        public double getDeltaX() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedDeltaX);
        }

        @Override
        public double getDeltaY() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedDeltaY);
        }

        @Override
        public double getDeltaZ() {
            return com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.deserializePosition_1_8_8(encodedDeltaZ);
        }
    }

    /**
     * Encoding used since 26.3, where the delta is encoded as a list of steps.
     * Each step has its own delta and duration in ticks.
     * Multi-step changes are only supported on Minecraft 26.3 and later.
     */
    public static final class SteppedPositionChange implements PositionChange {
        public final java.util.List<Step> steps;

        public SteppedPositionChange() {
            this(new java.util.ArrayList<>());
        }

        public SteppedPositionChange(java.util.List<Step> steps) {
            this.steps = steps;
        }

        /**
         * Gets the list of steps that make up this multi-step change.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @return list of steps
         */
        public java.util.List<Step> getSteps() {
            return steps;
        }

        /**
         * Adds a new step to this multi-step change.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @param step Step to add
         * @return this, for chaining
         */
        public SteppedPositionChange addStep(Step step) {
            steps.add(step);
            return this;
        }

        /**
         * Adds a new step to this multi-step change, encoding the given delta values and tick duration as a single step.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @param deltaX Delta movement to encode, X-coordinate
         * @param deltaY Delta movement to encode, Y-coordinate
         * @param deltaZ Delta movement to encode, Z-coordinate
         * @param ticks Duration of this step in ticks
         * @return this, for chaining
         */
        public SteppedPositionChange addStep(double deltaX, double deltaY, double deltaZ, int ticks) {
            steps.add(encodeStep(deltaX, deltaY, deltaZ, ticks));
            return this;
        }

        @Override
        public double getDeltaX() {
            double sum = 0.0;
            for (Step change : steps) {
                sum += change.getDeltaX();
            }
            return sum;
        }

        @Override
        public double getDeltaY() {
            double sum = 0.0;
            for (Step change : steps) {
                sum += change.getDeltaY();
            }
            return sum;
        }

        @Override
        public double getDeltaZ() {
            double sum = 0.0;
            for (Step change : steps) {
                sum += change.getDeltaZ();
            }
            return sum;
        }

        /**
         * Gets the total number of ticks that this multi-step change will take to complete.
         *
         * @return total ticks
         */
        public int getTicks() {
            int sum = 0;
            for (Step change : steps) {
                sum += change.ticks;
            }
            return sum;
        }

        /**
         * Creates a new Step instance that encodes the given delta values and tick duration as a single step.
         * Multi-step changes are only supported on Minecraft 26.3 and later.
         *
         * @param deltaX Delta movement to encode, X-coordinate
         * @param deltaY Delta movement to encode, Y-coordinate
         * @param deltaZ Delta movement to encode, Z-coordinate
         * @param ticks Duration of this step in ticks
         * @return step instance that can be added to MultiPositionChange
         */
        public static Step encodeStep(double deltaX, double deltaY, double deltaZ, int ticks) {
            short encodedDeltaX = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaX);
            short encodedDeltaY = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaY);
            short encodedDeltaZ = (short) com.bergerkiller.bukkit.common.internal.logic.ProtocolMath.serializePosition_1_9(deltaZ);
            return new Step(encodedDeltaX, encodedDeltaY, encodedDeltaZ, ticks);
        }

        /**
         * Represents a single step in a multi-step position change.
         * Each step has its own delta and duration in ticks.
         */
        public static final class Step extends LinearPositionChange {
            public final int ticks;

            public Step(short encodedDeltaX, short encodedDeltaY, short encodedDeltaZ, int ticks) {
                super(encodedDeltaX, encodedDeltaY, encodedDeltaZ);
                this.ticks = ticks;
            }
        }
    }
    </code>

    class ClientboundMoveEntityPacket.Rot extends ClientboundMoveEntityPacket {
        public static (ClientboundMoveEntityPacketHandle.RotHandle) ClientboundMoveEntityPacket.Rot createNew() {
#if version >= 1.20.5
            return new ClientboundMoveEntityPacket$Rot(0, (byte) 0, (byte) 0, false);
#elseif version >= 1.18
            return ClientboundMoveEntityPacket$Rot.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return ClientboundMoveEntityPacket$Rot.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new ClientboundMoveEntityPacket$Rot();
#endif
        }

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

        public static ClientboundMoveEntityPacketHandle.RotHandle createNew(int entityId, float yaw, float pitch, boolean onGround) {
            ClientboundMoveEntityPacketHandle.RotHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setYaw(yaw);
            handle.setPitch(pitch);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }

    class ClientboundMoveEntityPacket.Pos extends ClientboundMoveEntityPacket {
        public static (ClientboundMoveEntityPacketHandle.PosHandle) ClientboundMoveEntityPacket.Pos createNew() {
#if version >= 26.3
            return new ClientboundMoveEntityPacket$Pos(0, new VecDelta$Linear((short) 0, (short) 0, (short) 0), false);
#elseif version >= 1.20.5
            return new ClientboundMoveEntityPacket$Pos(0, (short) 0, (short) 0, (short) 0, false);
#elseif version >= 1.18
            return ClientboundMoveEntityPacket$Pos.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return ClientboundMoveEntityPacket$Pos.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new ClientboundMoveEntityPacket$Pos();
#endif
        }

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

        public static ClientboundMoveEntityPacketHandle.PosHandle createNew(int entityId, double dx, double dy, double dz, boolean onGround) {
            return createNew(entityId, PositionChange.encodeLinearChange(dx, dy, dz), onGround);
        }

        public static ClientboundMoveEntityPacketHandle.PosHandle createNew(int entityId, PositionChange posChange, boolean onGround) {
            ClientboundMoveEntityPacketHandle.PosHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setPositionChange(posChange);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }

    class ClientboundMoveEntityPacket.PosRot extends ClientboundMoveEntityPacket {
        public static (ClientboundMoveEntityPacketHandle.PosRotHandle) ClientboundMoveEntityPacket.PosRot createNew() {
#if version >= 26.3
            return new ClientboundMoveEntityPacket$PosRot(0, new VecDelta$Linear((short) 0, (short) 0, (short) 0), (byte) 0, (byte) 0, false);
#elseif version >= 1.20.5
            return new ClientboundMoveEntityPacket$PosRot(0, (short) 0, (short) 0, (short) 0, (byte) 0, (byte) 0, false);
#elseif version >= 1.18
            return ClientboundMoveEntityPacket$PosRot.read(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
            return ClientboundMoveEntityPacket$PosRot.b(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
            return new ClientboundMoveEntityPacket$PosRot();
#endif
        }

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

        public static ClientboundMoveEntityPacketHandle.PosRotHandle createNew(int entityId, double dx, double dy, double dz, float yaw, float pitch, boolean onGround) {
            return createNew(entityId, PositionChange.encodeLinearChange(dx, dy, dz), yaw, pitch, onGround);
        }

        public static ClientboundMoveEntityPacketHandle.PosRotHandle createNew(int entityId, PositionChange posChange, float yaw, float pitch, boolean onGround) {
            ClientboundMoveEntityPacketHandle.PosRotHandle handle = createNew();
            handle.setEntityId(entityId);
            handle.setPositionChange(posChange);
            handle.setYaw(yaw);
            handle.setPitch(pitch);
            handle.setOnGround(onGround);
            return handle;
        }
        </code>
    }
}

class ClientboundRotateHeadPacket extends Packet {
#if version >= 1.17
    private int entityId:entityId;
#else
    private int entityId:a;
#endif

    public static optional (ClientboundRotateHeadPacketHandle) ClientboundRotateHeadPacket createNewEmpty() {
#if version >= 1.20.5
        #require net.minecraft.network.protocol.game.ClientboundRotateHeadPacket ClientboundRotateHeadPacket createHeadRotationPacket:<init>(net.minecraft.network.FriendlyByteBuf serializer);
        return #createHeadRotationPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#elseif version >= 1.17
        return new ClientboundRotateHeadPacket(com.bergerkiller.bukkit.common.internal.logic.NullPacketDataSerializer.INSTANCE);
#else
        return new ClientboundRotateHeadPacket();
#endif
    }

    <code>
    public static ClientboundRotateHeadPacketHandle createNew() {
        return T.createNewEmpty.invoke();
    }

    public static ClientboundRotateHeadPacketHandle createNew(int entityId, float headYaw) {
        ClientboundRotateHeadPacketHandle packet = createNew();
        packet.setEntityId(entityId);
        packet.setHeadYaw(headYaw);
        return packet;
    }
    </code>

    public static (ClientboundRotateHeadPacketHandle) ClientboundRotateHeadPacket createNew((org.bukkit.entity.Entity) Entity entity, float headYaw) {
        return new ClientboundRotateHeadPacket(entity, (byte) com.bergerkiller.bukkit.common.utils.MathUtil.floor(headYaw * 256.0f / 360.0f));
    }

#if version >= 1.17
    #require net.minecraft.network.protocol.game.ClientboundRotateHeadPacket private byte headYaw:yHeadRot;
#else
    #require net.minecraft.network.protocol.game.ClientboundRotateHeadPacket private byte headYaw:b;
#endif

    public float getHeadYaw() {
        return (float) instance#headYaw * 360.0f / 256.0f;   
    }

    public void setHeadYaw(float headYaw) {
        instance#headYaw = (byte) com.bergerkiller.bukkit.common.utils.MathUtil.floor(headYaw * 256.0f / 360.0f);
    }
}

class ServerboundMovePlayerPacket extends Packet {
    protected double x;
    protected double y;
    protected double z;
#if version >= 1.17
    protected float yaw:yRot;
    protected float pitch:xRot;
    protected boolean onGround;
    protected boolean hasPos;
    protected boolean hasLook:hasRot;
#else
    protected float yaw;
    protected float pitch;
    protected boolean onGround:f;
    protected boolean hasPos;
    protected boolean hasLook;
#endif
}

// Since Minecraft 1.21.2, and only used with the new minecart movement behavior feature
optional class ClientboundMoveMinecartPacket extends Packet {
    public int getEntityId:entityId();
    public (List<NewMinecartBehaviorHandle.MinecartStepHandle>) List<NewMinecartBehavior.MinecartStep> getLerpSteps:lerpSteps();

    public static (ClientboundMoveMinecartPacketHandle) ClientboundMoveMinecartPacket createNew(int entityId, (List<NewMinecartBehaviorHandle.MinecartStepHandle>) List<NewMinecartBehavior.MinecartStep> lerpSteps) {
        return new ClientboundMoveMinecartPacket(entityId, lerpSteps);
    }
}
