package net.minecraft.network.protocol.game;

import net.minecraft.core.Direction;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.Packet;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;

import com.bergerkiller.bukkit.common.wrappers.HumanHand;
import com.bergerkiller.bukkit.common.wrappers.HumanHandRole;
import com.bergerkiller.bukkit.common.bases.IntVector3;

import com.bergerkiller.generated.net.minecraft.world.phys.BlockHitResultHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundInteractPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundInteractPacketHandle.Action;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundAttackPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundUseItemPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundUseItemOnPacketHandle;
import com.bergerkiller.generated.net.minecraft.network.protocol.game.ServerboundPunchPacketHandle;

// Since 26.3, before this is the ServerboundSwingPacket
class ServerboundPunchPacket extends Packet {

    public static (ServerboundPunchPacketHandle) ServerboundPunchPacket createNew((HumanHandRole) InteractionHand handRole) {
#if version >= 26.3
        return new ServerboundPunchPacket();
#elseif version >= 1.9
        return new ServerboundSwingPacket(handRole);
#else
        ServerboundSwingPacket packet = new ServerboundSwingPacket();
        packet.timestamp = System.currentTimeMillis();
        return packet;
#endif
    }

#if version >= 26.3
    public (HumanHandRole) InteractionHand getHandRole() {
        return InteractionHand.MAIN_HAND;
    }
#elseif version >= 1.18
    public (HumanHandRole) InteractionHand getHandRole:getHand();
#elseif version >= 1.13
    public (HumanHandRole) InteractionHand getHandRole:b();
#elseif version >= 1.9
    public (HumanHandRole) InteractionHand getHandRole:a();
#else
    public (HumanHandRole) HumanHandRole getHandRole() {
        return HumanHandRole.MAIN;
    }
#endif

    <code>
    public com.bergerkiller.bukkit.common.wrappers.HumanHand getHand(org.bukkit.entity.HumanEntity humanEntity) {
        return getHandRole().getHandOf(humanEntity);
    }

    public static ServerboundPunchPacketHandle createNew(org.bukkit.entity.HumanEntity humanEntity, com.bergerkiller.bukkit.common.wrappers.HumanHand hand) {
        return createNew(hand.getRoleOf(humanEntity));
    }
    </code>
}

// 1.8 - 26.2 only. Replaced by ServerboundPunchPacket since 26.3
// Deprecated: but was still in use by TC-Coasters for a short time. Keep until that is no longer the case.
optional class ServerboundSwingPacket extends Packet {
#if version >= 26.3
    public (HumanHandRole) InteractionHand getHandRole() {
        throw new UnsupportedOperationException("ServerboundSwingPacket is not supported on this Minecraft version");
    }

    public void setHandRole((HumanHandRole) InteractionHand handRole) {
        throw new UnsupportedOperationException("ServerboundSwingPacket is not supported on this Minecraft version");
    }
#elseif version >= 1.9
  #if version >= 1.17
    #require ServerboundSwingPacket private InteractionHand handRole:hand;
  #else
    #require ServerboundSwingPacket private InteractionHand handRole:a;
  #endif

    public (HumanHandRole) InteractionHand getHandRole() {
        return instance#handRole;
    }

    public void setHandRole((HumanHandRole) InteractionHand handRole) {
        instance#handRole = handRole;
    }
#else
    public (HumanHandRole) HumanHandRole getHandRole() {
        return HumanHandRole.MAIN;
    }

    public void setHandRole((HumanHandRole) HumanHandRole handRole) {
    }
#endif

    <code>
    public com.bergerkiller.bukkit.common.wrappers.HumanHand getHand(org.bukkit.entity.HumanEntity humanEntity) {
        return getHandRole().getHandOf(humanEntity);
    }

    public void setHand(org.bukkit.entity.HumanEntity humanEntity, com.bergerkiller.bukkit.common.wrappers.HumanHand hand) {
        setHandRole(hand.getRoleOf(humanEntity));
    }
    </code>
}

// Since 26.1 this is a standalone packet
// On 1.8 - 1.21.11 this uses the ServerboundInteractPacket (PacketPlayInUseEntity) with Action = ATTACK
class ServerboundAttackPacket extends Packet {
#if version >= 26.1
    public int getEntityId:entityId();

    public static (ServerboundAttackPacketHandle) ServerboundAttackPacket createNew(int attackedEntityId, boolean isUsingSecondaryAction) {
        return new ServerboundAttackPacket(attackedEntityId);
    }

    public static boolean isAttackInteractionPacket((Object) ServerboundInteractPacket interactPacket) {
        return false;
    }

    public static boolean hasSecondaryActionField() {
        return false;
    }

    public boolean isUsingSecondaryAction() {
        return false;
    }
#elseif version >= 1.17
    #require ServerboundInteractPacket static final ServerboundInteractPacket.Action ATTACK_ACTION;

    public int getEntityId() {
        #require ServerboundAttackPacket private readonly int attackedEntityId:entityId;
        return instance#attackedEntityId;
    }

    public static (ServerboundAttackPacketHandle) ServerboundAttackPacket createNew(int attackedEntityId, boolean isUsingSecondaryAction) {
        #require ServerboundInteractPacket private ServerboundInteractPacket createInteractPacket:<init> (int i, boolean flag, ServerboundInteractPacket.Action action);
        return #createInteractPacket( attackedEntityId, isUsingSecondaryAction, ServerboundInteractPacket#ATTACK_ACTION );
    }

    public static boolean isAttackInteractionPacket((Object) ServerboundInteractPacket interactPacket) {
        #require net.minecraft.network.protocol.game.ServerboundInteractPacket private final ServerboundInteractPacket.Action action;
        return interactPacket#action == ServerboundInteractPacket#ATTACK_ACTION;
    }

    public static boolean hasSecondaryActionField() {
        return true;
    }

  #if version >= 1.18
    public boolean isUsingSecondaryAction();
  #else
    public boolean isUsingSecondaryAction:b();
  #endif
#else
    #require ServerboundAttackPacket private int attackedEntityId:a;
    #require ServerboundAttackPacket ServerboundInteractPacket.ActionType action;
  #if version >= 1.16
    #require ServerboundAttackPacket private boolean secondaryActionFlag:e;
  #endif

    public int getEntityId() {
        return instance#attackedEntityId;
    }

    public static (ServerboundAttackPacketHandle) ServerboundAttackPacket createNew(int attackedEntityId, boolean isUsingSecondaryAction) {
        ServerboundAttackPacket packet = new ServerboundInteractPacket();
        packet#attackedEntityId = attackedEntityId;
        packet#action = ServerboundInteractPacket$ActionType.ATTACK;

  #if version >= 1.16
        packet#secondaryActionFlag = isUsingSecondaryAction;
  #endif

  #if version >= 1.9
        #require ServerboundAttackPacket private net.minecraft.world.InteractionHand hand:d;
        packet#hand = InteractionHand.MAIN_HAND;
  #endif

        return packet;
    }

    public static boolean isAttackInteractionPacket((Object) ServerboundInteractPacket interactPacket) {
        return interactPacket#action == ServerboundInteractPacket$ActionType.ATTACK;
    }

  #if version >= 1.16
    public static boolean hasSecondaryActionField() {
        return true;
    }

    public boolean isUsingSecondaryAction:e();
  #else
    public static boolean hasSecondaryActionField() {
        return false;
    }

    public boolean isUsingSecondaryAction() {
        return false;
    }
  #endif
#endif

    <code>
    public static ServerboundAttackPacketHandle createNew(int attackedEntityId) {
        return createNew(attackedEntityId, false);
    }
    </code>
}

class ServerboundInteractPacket extends Packet {
#if version >= 26.1
    public static (ServerboundInteractPacketHandle) ServerboundInteractPacket createNew(int usedEntityId, (HumanHandRole) InteractionHand handRole, boolean isUsingSecondaryAction, (org.bukkit.util.Vector) Vec3 atPosition) {
        if (atPosition == null) {
            throw new IllegalArgumentException("Since Minecraft 26.1 an interact position is required");
        }

        return new ServerboundInteractPacket(usedEntityId, handRole, atPosition, isUsingSecondaryAction);
    }

    public int getUsedEntityId:entityId();

    public (HumanHandRole) InteractionHand getHandRole:hand();

    // Always sends interaction position since 26.1
    public boolean hasInteractAtPosition() {
        return true;
    }

    public (org.bukkit.util.Vector) Vec3 getInteractAtPosition:location();

    public static boolean hasSecondaryActionField() {
        return true;
    }

    public boolean isUsingSecondaryAction:usingSecondaryAction();

#elseif version >= 1.17
    // Since 1.17 the enum is a property of an interface that houses the action (Action -> getType() -> ActionType)
    // ActionType is the internal enum name in mojmap, this handles the situation better
    // After 26.1 this is no longer used
  #if version >= 1.18
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket.Action public abstract (Enum) net.minecraft.network.protocol.game.ServerboundInteractPacket.ActionType getType();
  #else
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket.Action public abstract (Enum) net.minecraft.network.protocol.game.ServerboundInteractPacket.ActionType getType:a();
  #endif

    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private readonly int usedEntityId:entityId;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private final ServerboundInteractPacket.Action action;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket.InteractionAction private final readonly net.minecraft.world.InteractionHand interactHand:hand;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket.InteractionAtLocationAction private final readonly net.minecraft.world.InteractionHand interactAtHand:hand;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket.InteractionAtLocationAction private final readonly net.minecraft.world.phys.Vec3 interactAtLocation:location;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket public String getActionName() {
        return instance#action#getType().name();
    }

    public static (ServerboundInteractPacketHandle) ServerboundInteractPacket createNew(int usedEntityId, (HumanHandRole) InteractionHand handRole, boolean isUsingSecondaryAction, (org.bukkit.util.Vector) Vec3 atPosition) {
        #require ServerboundInteractPacket private ServerboundInteractPacket createInteractPacket:<init> (int i, boolean flag, ServerboundInteractPacket.Action action);

        Object action;
        if (atPosition != null) {
            #require net.minecraft.network.protocol.game.ServerboundInteractPacket.InteractionAtLocationAction ServerboundInteractPacket.InteractionAtLocationAction createActionInteractAt:<init>(net.minecraft.world.InteractionHand hand, net.minecraft.world.phys.Vec3 location);
            action = #createActionInteractAt(handRole, atPosition);
        } else {
            #require net.minecraft.network.protocol.game.ServerboundInteractPacket.InteractionAction ServerboundInteractPacket.InteractionAction createActionInteract:<init>(net.minecraft.world.InteractionHand hand);
            action = #createActionInteract(handRole);
        }

        return #createInteractPacket(usedEntityId, isUsingSecondaryAction, action);
    }

    public int getUsedEntityId() {
        return instance#usedEntityId;
    }

    public (HumanHandRole) InteractionHand getHandRole() {
        Object useAction = instance#action;
        String name = useAction#getType().name();
        if (name.equals("INTERACT")) {
            return useAction#interactHand;
        } else if (name.equals("INTERACT_AT")) {
            return useAction#interactAtHand;
        } else {
            return null;
        }
    }

    public boolean hasInteractAtPosition() {
        return instance#getActionName().equals("INTERACT_AT");
    }

    public (org.bukkit.util.Vector) Vec3 getInteractAtPosition() {
        Object useAction = instance#action;
        if (useAction#getType().name().equals("INTERACT_AT")) {
            return useAction#interactAtLocation;
        } else {
            return null;
        }
    }

    public static boolean hasSecondaryActionField() {
        return true;
    }

  #if version >= 1.18
    public boolean isUsingSecondaryAction();
  #else
    public boolean isUsingSecondaryAction:b();
  #endif

#else
    // 1.8 - 1.16.5
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private readonly int usedEntityId:a;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private ServerboundInteractPacket.Action action;
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private net.minecraft.world.phys.Vec3 offset:c;
  #if version >= 1.9
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private net.minecraft.world.InteractionHand hand:d;
  #endif
  #if version >= 1.13
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket public ServerboundInteractPacket.ActionType getType:b();
  #else
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket public ServerboundInteractPacket.ActionType getType:a();
  #endif
  #if version >= 1.16
    #require net.minecraft.network.protocol.game.ServerboundInteractPacket private boolean secondaryActionFlag:e;
  #endif

  #if version >= 1.9
    public static (ServerboundInteractPacketHandle) ServerboundInteractPacket createNew(int usedEntityId, (HumanHandRole) InteractionHand handRole, boolean isUsingSecondaryAction, (org.bukkit.util.Vector) Vec3 atPosition) {
        ServerboundInteractPacket packet = new ServerboundInteractPacket();
        packet#usedEntityId = usedEntityId;
        packet#hand = handRole;
        if (atPosition != null) {
            packet#action = ServerboundInteractPacket$ActionType.INTERACT_AT;
        } else {
            packet#action = ServerboundInteractPacket$ActionType.INTERACT;
        }
        packet#offset = atPosition;
    #if version >= 1.16
        packet#secondaryActionFlag = isUsingSecondaryAction;
    #endif
        return packet;
    }
  #else
    public static (ServerboundInteractPacketHandle) ServerboundInteractPacket createNew(int usedEntityId, (HumanHandRole) HumanHandRole handRole, boolean isUsingSecondaryAction, (org.bukkit.util.Vector) Vec3 atPosition) {
        ServerboundInteractPacket packet = new ServerboundInteractPacket();
        packet#usedEntityId = usedEntityId;
        if (atPosition != null) {
            packet#action = ServerboundInteractPacket$ActionType.INTERACT_AT;
        } else {
            packet#action = ServerboundInteractPacket$ActionType.INTERACT;
        }
        packet#offset = atPosition;
        return packet;
    }
  #endif

    public int getUsedEntityId() {
        return instance#usedEntityId;
    }

  #if version >= 1.13
    public (HumanHandRole) InteractionHand getHandRole:c();
  #elseif version >= 1.9
    public (HumanHandRole) InteractionHand getHandRole:b();
  #else
    public (HumanHandRole) HumanHandRole getHandRole() {
        return HumanHandRole.MAIN;
    }
  #endif

    public boolean hasInteractAtPosition() {
        return instance#getType() == ServerboundInteractPacket$Action.INTERACT_AT;
    }

    public (org.bukkit.util.Vector) Vec3 getInteractAtPosition() {
        if (instance#getType() == ServerboundInteractPacket$Action.INTERACT_AT) {
  #select version >=
  #case 1.13: return instance.d();
  #case 1.9:  return instance.c();
  #case else: return instance.b();
  #endselect
        } else {
            return null;
        }
    }

  #if version >= 1.16
    public static boolean hasSecondaryActionField() {
        return true;
    }

    public boolean isUsingSecondaryAction:e();
  #else
    public static boolean hasSecondaryActionField() {
        return false;
    }

    public boolean isUsingSecondaryAction() {
        return false;
    }
  #endif
#endif

    <code>
    public com.bergerkiller.bukkit.common.wrappers.HumanHand getHand(org.bukkit.entity.HumanEntity humanEntity) {
        return getHandRole().getHandOf(humanEntity);
    }

    public static ServerboundInteractPacketHandle createNew(int usedEntityId, com.bergerkiller.bukkit.common.wrappers.HumanHandRole handRole, org.bukkit.util.Vector atPosition) {
        return createNew(usedEntityId, handRole, false, atPosition);
    }

    public static ServerboundInteractPacketHandle withUsingSecondaryAction(ServerboundInteractPacketHandle packet, boolean isUsingSecondaryAction) {
        return createNew(packet.getUsedEntityId(), packet.getHandRole(), isUsingSecondaryAction, packet.getInteractAtPosition());
    }

    public static ServerboundInteractPacketHandle withUsedEntityId(ServerboundInteractPacketHandle packet, int usedEntityId) {
        return createNew(usedEntityId, packet.getHandRole(), packet.isUsingSecondaryAction(), packet.getInteractAtPosition());
    }
    </code>
}

// Note:
// - is PacketPlayInBlockPlace on 1.9 - 1.21.11 (spigot mapping)
// - before off-hand on 1.8, this packet was not used (all interactions use ServerboundUseItemOnPacket)
class ServerboundUseItemPacket extends Packet {

    // Sequence value (?). Since 1.19
#if version >= 26.3
    public int getSequence:sequence();
#elseif version >= 1.19
    public int getSequence();
#else
    public int getSequence() { return 0; }
#endif

    // Yaw/pitch of the player at the time of interaction. Since 1.21.
#if version >= 26.3
    public float getYaw:yRot();
    public float getPitch:xRot();
#elseif version >= 1.21
    public float getYaw:getYRot();
    public float getPitch:getXRot();
#else
    public float getYaw() { return 0.0f; }
    public float getPitch() { return 0.0f; }
#endif

    // Hand used, since 1.9
#if version >= 26.3
    public (HumanHandRole) InteractionHand getHandRole:hand();
#elseif version >= 1.18
    public (HumanHandRole) InteractionHand getHandRole:getHand();
#elseif version >= 1.9
    public (HumanHandRole) InteractionHand getHandRole() {
  #if version >= 1.17
        #require ServerboundUseItemPacket private InteractionHand handRole:hand;
  #else
        #require ServerboundUseItemPacket private InteractionHand handRole:a;
  #endif
      return instance#handRole;
    }
#else
    public (HumanHandRole) HumanHandRole getHandRole() {
        return HumanHandRole.MAIN;
    }
#endif

    // Spigot-only timestamp field. Existed for some anti-cheat nonsense.
    public long getTimestamp() {
#if exists net.minecraft.network.protocol.game.ServerboundUseItemPacket public long timestamp;
        return instance.timestamp;
#else
        return 0L;
#endif
    }

    // This version has all parameters this packet ever supported on spigot/paper
    // We use this to clone the packet with a single property changed.
    // Some fields, like timestamp, are a no-op.
    public static (ServerboundUseItemPacketHandle) ServerboundUseItemPacket createNew((HumanHandRole) InteractionHand handRole, long timestamp, int sequence, float yaw, float pitch) {
#if version >= 1.9
  #if version >= 1.21
        ServerboundUseItemPacket packet = new ServerboundUseItemPacket(handRole, sequence, yaw, pitch);
  #elseif version >= 1.19
        ServerboundUseItemPacket packet = new ServerboundUseItemPacket(handRole, sequence);
  #elseif version >= 1.9
        ServerboundUseItemPacket packet = new ServerboundUseItemPacket(handRole);
  #endif

        // Spigot-specific field not present in vanilla
  #if exists net.minecraft.network.protocol.game.ServerboundUseItemPacket public long timestamp;
        packet.timestamp = timestamp;
  #endif

        return packet;
#else
        throw new UnsupportedOperationException("ServerboundUseItemPacket is not supported on this Minecraft version");
#endif
    }

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

    public com.bergerkiller.bukkit.common.wrappers.HumanHand getHand(org.bukkit.entity.HumanEntity humanEntity) {
        return getHandRole().getHandOf(humanEntity);
    }

    public ServerboundUseItemPacketHandle withRotation(float yaw, float pitch) {
        return createNew(getHandRole(), getTimestamp(), getSequence(), yaw, pitch);
    }

    public ServerboundUseItemPacketHandle withHand(org.bukkit.entity.HumanEntity humanEntity, com.bergerkiller.bukkit.common.wrappers.HumanHand hand) {
        return withHandRole(hand.getRoleOf(humanEntity));
    }

    public ServerboundUseItemPacketHandle withHandRole(HumanHandRole handRole) {
        return createNew(handRole, getTimestamp(), getSequence(), getYaw(), getPitch());
    }
    </code>
}

// ServerboundUseItemPacket on MC 1.8.9 and before when int direction == 255
class ServerboundUseItemOnPacket extends Packet {

    // BlockHitResult since 1.14. Before, the properties were spread over multiple fields and we re-combine.
#if version >= 26.3
    public (BlockHitResultHandle) BlockHitResult getHitResult:hitResult();
#elseif version >= 1.18
    public (BlockHitResultHandle) BlockHitResult getHitResult();
#elseif version >= 1.14
    public (BlockHitResultHandle) BlockHitResult getHitResult:c();
#elseif version >= 1.13
    public (BlockHitResultHandle) BlockHitResult getHitResult() {
        BlockPos blockPos = instance.b();
        Direction direction = instance.c();
        net.minecraft.world.phys.Vec3 pos = new net.minecraft.world.phys.Vec3(
            (double) instance.e() + blockPos.getX(),
            (double) instance.f() + blockPos.getY(),
            (double) instance.g() + blockPos.getZ()
        );
        return new BlockHitResult(pos, direction, blockPos);
    }
#elseif version >= 1.9
    public (BlockHitResultHandle) BlockHitResult getHitResult() {
        BlockPos blockPos = instance.a();
        Direction direction = instance.b();
        net.minecraft.world.phys.Vec3 pos = new net.minecraft.world.phys.Vec3(
            (double) instance.d() + blockPos.getX(),
            (double) instance.e() + blockPos.getY(),
            (double) instance.f() + blockPos.getZ()
        );
        return new BlockHitResult(pos, direction, blockPos);
    }
#else
    public (BlockHitResultHandle) BlockHitResult getHitResult() {
        BlockPos blockPos = instance.a();
        int directionIndex = instance.getFace();
        if (directionIndex == 255) {
            throw new IllegalStateException("Cannot get use-item packet BlockHitResult for a block place packet (direction index 255)");
        }
        Direction direction = Direction.fromType1(directionIndex);
        net.minecraft.world.phys.Vec3 pos = new net.minecraft.world.phys.Vec3(
            (double) instance.d() + blockPos.getX(),
            (double) instance.e() + blockPos.getY(),
            (double) instance.f() + blockPos.getZ()
        );
        return new BlockHitResult(pos, direction, blockPos);
    }
#endif

    // Hand since 1.9
#select version >=
#case 26.3: public (HumanHandRole) InteractionHand getHandRole:hand();
#case 1.18:  public (HumanHandRole) InteractionHand getHandRole:getHand();
#case 1.14:  public (HumanHandRole) InteractionHand getHandRole:b();
#case 1.13:  public (HumanHandRole) InteractionHand getHandRole:d();
#case 1.9:    public (HumanHandRole) InteractionHand getHandRole:c();
#case else:  public (HumanHandRole) HumanHandRole getHandRole() { return HumanHandRole.MAIN; }
#endselect

    // Direction index value of 255 was used for the 'block place' packet on 1.8.9 and before
    // No-op on 1.9+. Internal use only!
#if version >= 1.9
    public boolean isBlockPlacePacket() {
        return false;
    }
    public void setBlockPlacePacket() {
    }
#else
    public boolean isBlockPlacePacket() {
        return instance.getFace() == 255;
    }
    public void setBlockPlacePacket() {
        #require net.minecraft.network.protocol.game.ServerboundUseItemOnPacket private int direction_index:c;
        instance#direction_index = 255;
    }
#endif

    // Sequence property. Since 1.19.
#if version >= 26.3
    public int getSequence:sequence();
#elseif version >= 1.19
    public int getSequence();
#else
    public int getSequence() { return 0; }
#endif

    // Timestamp property. Assigned by Spigot (and Paper?) for anti-cheat purposes. Not present in vanilla.
#if version >= 26.3
    public long getTimestamp:timestamp();
#elseif exists net.minecraft.network.protocol.game.ServerboundUseItemOnPacket public long timestamp;
    public long getTimestamp() { return instance.timestamp; }
#else
    public long getTimestamp() { return 0L; }
#endif

    public static (ServerboundUseItemOnPacketHandle) ServerboundUseItemOnPacket createNew((HumanHandRole) InteractionHand handRole, (BlockHitResultHandle) BlockHitResult blockHitResult, int sequence, long timestamp) {
#if version >= 26.3
        // Immutable record class, so we can just call the constructor
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket(handRole, blockHitResult, sequence, timestamp);
#elseif version >= 1.19
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket(handRole, blockHitResult, sequence);
#elseif version >= 1.17
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket(handRole, blockHitResult);
#elseif version >= 1.14
        // Call empty constructor, then assign the individual fields
        #require ServerboundUseItemOnPacket private BlockHitResult hitResult:a;
        #require ServerboundUseItemOnPacket private net.minecraft.world.InteractionHand handRole:b;
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket();
        packet#hitResult = blockHitResult;
        packet#handRole = handRole;
#elseif version >= 1.9
        // On 1.9 - 1.13.2, the block hit result is split into multiple fields, so we need to assign them individually
        #require ServerboundUseItemOnPacket private net.minecraft.core.BlockPos blockPos:a;
        #require ServerboundUseItemOnPacket private net.minecraft.core.Direction direction:b;
        #require ServerboundUseItemOnPacket private net.minecraft.world.InteractionHand handRole:c;
        #require ServerboundUseItemOnPacket private float deltaX:d;
        #require ServerboundUseItemOnPacket private float deltaY:e;
        #require ServerboundUseItemOnPacket private float deltaZ:f;
        BlockPos blockPos = blockHitResult.getBlockPos();
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket();
        packet#blockPos = blockPos;
        packet#direction = blockHitResult.direction;
        packet#handRole = handRole;
        packet#deltaX = ((float)(blockHitResult.pos.x-blockPos.getX()));
        packet#deltaY = ((float)(blockHitResult.pos.y-blockPos.getY()));
        packet#deltaZ = ((float)(blockHitResult.pos.z-blockPos.getZ()));
#else
        // On 1.8 - 1.8.9 we can call a public constructor to assign the individual fields
        // Note: is called PacketPlayInBlockPlace on these versions
        BlockPos blockPos = blockHitResult.getBlockPos();
        ServerboundUseItemOnPacket packet = new ServerboundUseItemOnPacket(
            blockPos,
            blockHitResult.direction.a(),
            (net.minecraft.world.item.ItemStack) null, /* ItemStack, unused */
            (float) (blockHitResult.pos.x - blockPos.getX()),
            (float) (blockHitResult.pos.y - blockPos.getY()),
            (float) (blockHitResult.pos.z - blockPos.getZ())
        );
#endif

        // Assign mutable spigot timestamp if this field exists
  #if version < 26.3 && exists net.minecraft.network.protocol.game.ServerboundUseItemOnPacket public long timestamp;
        packet.timestamp = timestamp;
  #endif

        return packet;
    }

    <code>
    public ServerboundUseItemOnPacketHandle withHandRole(HumanHandRole handRole) {
        return createNew(handRole, getHitResult(), getSequence(), getTimestamp());
    }

    public ServerboundUseItemOnPacketHandle withHand(org.bukkit.entity.HumanEntity humanEntity, com.bergerkiller.bukkit.common.wrappers.HumanHand hand) {
        return withHandRole(hand.getRoleOf(humanEntity));
    }

    public ServerboundUseItemOnPacketHandle withHitResult(BlockHitResultHandle hitResult) {
        return createNew(getHandRole(), hitResult, getSequence(), getTimestamp());
    }

    public com.bergerkiller.bukkit.common.wrappers.HumanHand getHand(org.bukkit.entity.HumanEntity humanEntity) {
        return getHandRole().getHandOf(humanEntity);
    }

    public com.bergerkiller.bukkit.common.bases.IntVector3 getBlockPos() {
        return getHitResult().getBlockPos();
    }

    public org.bukkit.util.Vector getLocation() {
        return getHitResult().getLocation();
    }

    public org.bukkit.block.BlockFace getDirection() {
        return getHitResult().getDirection();
    }

    public org.bukkit.util.Vector getRelativePosition() {
        return getHitResult().getRelativePosition();
    }

    @Deprecated
    public float getDeltaX() {
        return (float) getRelativePosition().getX();
    }

    @Deprecated
    public float getDeltaY() {
        return (float) getRelativePosition().getY();
    }

    @Deprecated
    public float getDeltaZ() {
        return (float) getRelativePosition().getZ();
    }

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