--- a/net/minecraft/world/entity/projectile/AbstractArrow.java
+++ b/net/minecraft/world/entity/projectile/AbstractArrow.java
@@ -3,6 +3,8 @@
 import com.google.common.collect.Lists;
 import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import javax.annotation.Nullable;
 import net.minecraft.advancements.CriteriaTriggers;
@@ -13,6 +15,7 @@
 import net.minecraft.core.registries.Registries;
 import net.minecraft.nbt.CompoundTag;
 import net.minecraft.nbt.NbtUtils;
+import net.minecraft.network.chat.Component;
 import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
 import net.minecraft.network.syncher.EntityDataAccessor;
 import net.minecraft.network.syncher.EntityDataSerializers;
@@ -31,6 +34,7 @@
 import net.minecraft.world.entity.MoverType;
 import net.minecraft.world.entity.SlotAccess;
 import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.item.ItemEntity;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.enchantment.EnchantmentHelper;
@@ -45,9 +49,14 @@
 import net.minecraft.world.phys.HitResult;
 import net.minecraft.world.phys.Vec3;
 import net.minecraft.world.phys.shapes.VoxelShape;
+import org.bukkit.event.entity.EntityCombustByEntityEvent;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.player.PlayerPickupArrowEvent;
+// CraftBukkit end
 
 public abstract class AbstractArrow extends Projectile {
-    private static final double ARROW_BASE_DAMAGE = 2.0;
+
+    private static final double ARROW_BASE_DAMAGE = 2.0D;
     private static final EntityDataAccessor<Byte> ID_FLAGS = SynchedEntityData.defineId(AbstractArrow.class, EntityDataSerializers.BYTE);
     private static final EntityDataAccessor<Byte> PIERCE_LEVEL = SynchedEntityData.defineId(AbstractArrow.class, EntityDataSerializers.BYTE);
     private static final int FLAG_CRIT = 1;
@@ -57,30 +66,48 @@
     private BlockState lastState;
     public boolean inGround;
     protected int inGroundTime;
-    public AbstractArrow.Pickup pickup = AbstractArrow.Pickup.DISALLOWED;
+    public AbstractArrow.Pickup pickup;
     public int shakeTime;
     public int life;
-    private double baseDamage = 2.0;
+    private double baseDamage;
     public int knockback;
-    public SoundEvent soundEvent = this.getDefaultHitGroundSoundEvent();
+    public SoundEvent soundEvent;
     @Nullable
     private IntOpenHashSet piercingIgnoreEntityIds;
     @Nullable
     private List<Entity> piercedAndKilledEntities;
-    public ItemStack pickupItemStack = this.getDefaultPickupItem();
+    public ItemStack pickupItemStack;
+
+    // Spigot Start
+    @Override
+    public void inactiveTick()
+    {
+        if ( this.inGround )
+        {
+            this.life += 1;
+        }
+        super.inactiveTick();
+    }
+    // Spigot End
 
     protected AbstractArrow(EntityType<? extends AbstractArrow> type, Level world) {
         super(type, world);
+        this.pickup = AbstractArrow.Pickup.DISALLOWED;
+        this.baseDamage = 2.0D;
+        this.soundEvent = this.getDefaultHitGroundSoundEvent();
+        this.pickupItemStack = this.getDefaultPickupItem();
     }
 
     protected AbstractArrow(EntityType<? extends AbstractArrow> type, Level world, ItemStack stack) {
         this(type, world);
         this.pickupItemStack = stack.copy();
-        this.setCustomName(stack.get(DataComponents.CUSTOM_NAME));
-        Unit unit = stack.remove(DataComponents.INTANGIBLE_PROJECTILE);
+        this.setCustomName((Component) stack.get(DataComponents.CUSTOM_NAME));
+        Unit unit = (Unit) stack.remove(DataComponents.INTANGIBLE_PROJECTILE);
+
         if (unit != null) {
             this.pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
         }
+
     }
 
     protected AbstractArrow(EntityType<? extends AbstractArrow> type, double x, double y, double z, Level world, ItemStack stack) {
@@ -89,7 +116,7 @@
     }
 
     protected AbstractArrow(EntityType<? extends AbstractArrow> type, LivingEntity owner, Level world, ItemStack stack) {
-        this(type, owner.getX(), owner.getEyeY() - 0.1F, owner.getZ(), world, stack);
+        this(type, owner.getX(), owner.getEyeY() - 0.10000000149011612D, owner.getZ(), world, stack);
         this.setOwner(owner);
     }
 
@@ -99,19 +126,20 @@
 
     @Override
     public boolean shouldRenderAtSqrDistance(double distance) {
-        double d = this.getBoundingBox().getSize() * 10.0;
-        if (Double.isNaN(d)) {
-            d = 1.0;
+        double d1 = this.getBoundingBox().getSize() * 10.0D;
+
+        if (Double.isNaN(d1)) {
+            d1 = 1.0D;
         }
 
-        d *= 64.0 * getViewScale();
-        return distance < d * d;
+        d1 *= 64.0D * getViewScale();
+        return distance < d1 * d1;
     }
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
-        builder.define(ID_FLAGS, (byte)0);
-        builder.define(PIERCE_LEVEL, (byte)0);
+        builder.define(AbstractArrow.ID_FLAGS, (byte) 0);
+        builder.define(AbstractArrow.PIERCE_LEVEL, (byte) 0);
     }
 
     @Override
@@ -135,25 +163,33 @@
     @Override
     public void tick() {
         super.tick();
-        boolean bl = this.isNoPhysics();
-        Vec3 vec3 = this.getDeltaMovement();
+        boolean flag = this.isNoPhysics();
+        Vec3 vec3d = this.getDeltaMovement();
+
         if (this.xRotO == 0.0F && this.yRotO == 0.0F) {
-            double d = vec3.horizontalDistance();
-            this.setYRot((float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI));
-            this.setXRot((float)(Mth.atan2(vec3.y, d) * 180.0F / (float)Math.PI));
+            double d0 = vec3d.horizontalDistance();
+
+            this.setYRot((float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D));
+            this.setXRot((float) (Mth.atan2(vec3d.y, d0) * 57.2957763671875D));
             this.yRotO = this.getYRot();
             this.xRotO = this.getXRot();
         }
 
-        BlockPos blockPos = this.blockPosition();
-        BlockState blockState = this.level().getBlockState(blockPos);
-        if (!blockState.isAir() && !bl) {
-            VoxelShape voxelShape = blockState.getCollisionShape(this.level(), blockPos);
-            if (!voxelShape.isEmpty()) {
-                Vec3 vec32 = this.position();
+        BlockPos blockposition = this.blockPosition();
+        BlockState iblockdata = this.level().getBlockState(blockposition);
+        Vec3 vec3d1;
 
-                for (AABB aABB : voxelShape.toAabbs()) {
-                    if (aABB.move(blockPos).contains(vec32)) {
+        if (!iblockdata.isAir() && !flag) {
+            VoxelShape voxelshape = iblockdata.getCollisionShape(this.level(), blockposition);
+
+            if (!voxelshape.isEmpty()) {
+                vec3d1 = this.position();
+                Iterator iterator = voxelshape.toAabbs().iterator();
+
+                while (iterator.hasNext()) {
+                    AABB axisalignedbb = (AABB) iterator.next();
+
+                    if (axisalignedbb.move(blockposition).contains(vec3d1)) {
                         this.inGround = true;
                         break;
                     }
@@ -162,127 +198,140 @@
         }
 
         if (this.shakeTime > 0) {
-            this.shakeTime--;
+            --this.shakeTime;
         }
 
-        if (this.isInWaterOrRain() || blockState.is(Blocks.POWDER_SNOW)) {
+        if (this.isInWaterOrRain() || iblockdata.is(Blocks.POWDER_SNOW)) {
             this.clearFire();
         }
 
-        if (this.inGround && !bl) {
-            if (this.lastState != blockState && this.shouldFall()) {
+        if (this.inGround && !flag) {
+            if (this.lastState != iblockdata && this.shouldFall()) {
                 this.startFalling();
             } else if (!this.level().isClientSide) {
                 this.tickDespawn();
             }
 
-            this.inGroundTime++;
+            ++this.inGroundTime;
         } else {
+            if (tickCount > 200) this.tickDespawn(); // Paper - tick despawnCounter regardless after 10 seconds
             this.inGroundTime = 0;
-            Vec3 vec33 = this.position();
-            Vec3 vec34 = vec33.add(vec3);
-            HitResult hitResult = this.level().clip(new ClipContext(vec33, vec34, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
-            if (hitResult.getType() != HitResult.Type.MISS) {
-                vec34 = hitResult.getLocation();
+            Vec3 vec3d2 = this.position();
+
+            vec3d1 = vec3d2.add(vec3d);
+            Object object = this.level().clip(new ClipContext(vec3d2, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
+
+            if (((HitResult) object).getType() != HitResult.Type.MISS) {
+                vec3d1 = ((HitResult) object).getLocation();
             }
 
             while (!this.isRemoved()) {
-                EntityHitResult entityHitResult = this.findHitEntity(vec33, vec34);
-                if (entityHitResult != null) {
-                    hitResult = entityHitResult;
+                EntityHitResult movingobjectpositionentity = this.findHitEntity(vec3d2, vec3d1);
+
+                if (movingobjectpositionentity != null) {
+                    object = movingobjectpositionentity;
                 }
 
-                if (hitResult != null && hitResult.getType() == HitResult.Type.ENTITY) {
-                    Entity entity = ((EntityHitResult)hitResult).getEntity();
-                    Entity entity2 = this.getOwner();
-                    if (entity instanceof Player && entity2 instanceof Player && !((Player)entity2).canHarmPlayer((Player)entity)) {
-                        hitResult = null;
-                        entityHitResult = null;
+                if (object != null && ((HitResult) object).getType() == HitResult.Type.ENTITY) {
+                    Entity entity = ((EntityHitResult) object).getEntity();
+                    Entity entity1 = this.getOwner();
+
+                    if (entity instanceof Player && entity1 instanceof Player && !((Player) entity1).canHarmPlayer((Player) entity)) {
+                        object = null;
+                        movingobjectpositionentity = null;
                     }
                 }
 
-                if (hitResult != null && !bl) {
-                    ProjectileDeflection projectileDeflection = this.hitTargetOrDeflectSelf(hitResult);
+                if (object != null && !flag) {
+                    ProjectileDeflection projectiledeflection = this.preHitTargetOrDeflectSelf((HitResult) object); // CraftBukkit - projectile hit event
+
                     this.hasImpulse = true;
-                    if (projectileDeflection != ProjectileDeflection.NONE) {
+                    if (projectiledeflection != ProjectileDeflection.NONE) {
                         break;
                     }
                 }
 
-                if (entityHitResult == null || this.getPierceLevel() <= 0) {
+                if (movingobjectpositionentity == null || this.getPierceLevel() <= 0) {
                     break;
                 }
 
-                hitResult = null;
+                object = null;
             }
 
-            vec3 = this.getDeltaMovement();
-            double e = vec3.x;
-            double f = vec3.y;
-            double g = vec3.z;
+            vec3d = this.getDeltaMovement();
+            double d1 = vec3d.x;
+            double d2 = vec3d.y;
+            double d3 = vec3d.z;
+
             if (this.isCritArrow()) {
-                for (int i = 0; i < 4; i++) {
-                    this.level()
-                        .addParticle(
-                            ParticleTypes.CRIT,
-                            this.getX() + e * (double)i / 4.0,
-                            this.getY() + f * (double)i / 4.0,
-                            this.getZ() + g * (double)i / 4.0,
-                            -e,
-                            -f + 0.2,
-                            -g
-                        );
+                for (int i = 0; i < 4; ++i) {
+                    this.level().addParticle(ParticleTypes.CRIT, this.getX() + d1 * (double) i / 4.0D, this.getY() + d2 * (double) i / 4.0D, this.getZ() + d3 * (double) i / 4.0D, -d1, -d2 + 0.2D, -d3);
                 }
             }
 
-            double h = this.getX() + e;
-            double j = this.getY() + f;
-            double k = this.getZ() + g;
-            double l = vec3.horizontalDistance();
-            if (bl) {
-                this.setYRot((float)(Mth.atan2(-e, -g) * 180.0F / (float)Math.PI));
+            double d4 = this.getX() + d1;
+            double d5 = this.getY() + d2;
+            double d6 = this.getZ() + d3;
+            double d7 = vec3d.horizontalDistance();
+
+            if (flag) {
+                this.setYRot((float) (Mth.atan2(-d1, -d3) * 57.2957763671875D));
             } else {
-                this.setYRot((float)(Mth.atan2(e, g) * 180.0F / (float)Math.PI));
+                this.setYRot((float) (Mth.atan2(d1, d3) * 57.2957763671875D));
             }
 
-            this.setXRot((float)(Mth.atan2(f, l) * 180.0F / (float)Math.PI));
+            this.setXRot((float) (Mth.atan2(d2, d7) * 57.2957763671875D));
             this.setXRot(lerpRotation(this.xRotO, this.getXRot()));
             this.setYRot(lerpRotation(this.yRotO, this.getYRot()));
-            float m = 0.99F;
+            float f = 0.99F;
+
             if (this.isInWater()) {
-                for (int n = 0; n < 4; n++) {
-                    float o = 0.25F;
-                    this.level().addParticle(ParticleTypes.BUBBLE, h - e * 0.25, j - f * 0.25, k - g * 0.25, e, f, g);
+                for (int j = 0; j < 4; ++j) {
+                    float f1 = 0.25F;
+
+                    this.level().addParticle(ParticleTypes.BUBBLE, d4 - d1 * 0.25D, d5 - d2 * 0.25D, d6 - d3 * 0.25D, d1, d2, d3);
                 }
 
-                m = this.getWaterInertia();
+                f = this.getWaterInertia();
             }
 
-            this.setDeltaMovement(vec3.scale((double)m));
-            if (!bl) {
+            this.setDeltaMovement(vec3d.scale((double) f));
+            if (!flag) {
                 this.applyGravity();
             }
 
-            this.setPos(h, j, k);
+            this.setPos(d4, d5, d6);
             this.checkInsideBlocks();
         }
     }
 
+    // Paper start - Fix cancelling ProjectileHitEvent for piercing arrows
+    @Override
+    public ProjectileDeflection preHitTargetOrDeflectSelf(HitResult hitResult) {
+        if (hitResult instanceof EntityHitResult entityHitResult && this.hitCancelled && this.getPierceLevel() > 0) {
+            if (this.piercingIgnoreEntityIds == null) {
+                this.piercingIgnoreEntityIds = new IntOpenHashSet(5);
+            }
+            this.piercingIgnoreEntityIds.add(entityHitResult.getEntity().getId());
+        }
+        return super.preHitTargetOrDeflectSelf(hitResult);
+    }
+    // Paper end - Fix cancelling ProjectileHitEvent for piercing arrows
+
     @Override
     protected double getDefaultGravity() {
-        return 0.05;
+        return 0.05D;
     }
 
     private boolean shouldFall() {
-        return this.inGround && this.level().noCollision(new AABB(this.position(), this.position()).inflate(0.06));
+        return this.inGround && this.level().noCollision((new AABB(this.position(), this.position())).inflate(0.06D));
     }
 
     private void startFalling() {
         this.inGround = false;
-        Vec3 vec3 = this.getDeltaMovement();
-        this.setDeltaMovement(
-            vec3.multiply((double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F))
-        );
+        Vec3 vec3d = this.getDeltaMovement();
+
+        this.setDeltaMovement(vec3d.multiply((double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F)));
         this.life = 0;
     }
 
@@ -292,13 +341,15 @@
         if (movementType != MoverType.SELF && this.shouldFall()) {
             this.startFalling();
         }
+
     }
 
     protected void tickDespawn() {
-        this.life++;
-        if (this.life >= 1200) {
-            this.discard();
+        ++this.life;
+        if (this.life >= (pickup == Pickup.CREATIVE_ONLY ? this.level().paperConfig().entities.spawning.creativeArrowDespawnRate.value() : (pickup == Pickup.DISALLOWED ? this.level().paperConfig().entities.spawning.nonPlayerArrowDespawnRate.value() : ((this instanceof ThrownTrident) ? this.level().spigotConfig.tridentDespawnRate : this.level().spigotConfig.arrowDespawnRate)))) { // Spigot // Paper - Configurable non-player arrow despawn rate; TODO: Extract this to init?
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
         }
+
     }
 
     private void resetPiercedEntities() {
@@ -309,14 +360,16 @@
         if (this.piercingIgnoreEntityIds != null) {
             this.piercingIgnoreEntityIds.clear();
         }
+
     }
 
     @Override
     protected void onHitEntity(EntityHitResult entityHitResult) {
         super.onHitEntity(entityHitResult);
         Entity entity = entityHitResult.getEntity();
-        float f = (float)this.getDeltaMovement().length();
-        int i = Mth.ceil(Mth.clamp((double)f * this.baseDamage, 0.0, 2.147483647E9));
+        float f = (float) this.getDeltaMovement().length();
+        int i = Mth.ceil(Mth.clamp((double) f * this.baseDamage, 0.0D, 2.147483647E9D));
+
         if (this.getPierceLevel() > 0) {
             if (this.piercingIgnoreEntityIds == null) {
                 this.piercingIgnoreEntityIds = new IntOpenHashSet(5);
@@ -327,7 +380,7 @@
             }
 
             if (this.piercingIgnoreEntityIds.size() >= this.getPierceLevel() + 1) {
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
                 return;
             }
 
@@ -335,99 +388,117 @@
         }
 
         if (this.isCritArrow()) {
-            long l = (long)this.random.nextInt(i / 2 + 2);
-            i = (int)Math.min(l + (long)i, 2147483647L);
+            long j = (long) this.random.nextInt(i / 2 + 2);
+
+            i = (int) Math.min(j + (long) i, 2147483647L);
         }
 
-        Entity entity2 = this.getOwner();
-        DamageSource damageSource;
-        if (entity2 == null) {
-            damageSource = this.damageSources().arrow(this, this);
+        Entity entity1 = this.getOwner();
+        DamageSource damagesource;
+
+        if (entity1 == null) {
+            damagesource = this.damageSources().arrow(this, this);
         } else {
-            damageSource = this.damageSources().arrow(this, entity2);
-            if (entity2 instanceof LivingEntity) {
-                ((LivingEntity)entity2).setLastHurtMob(entity);
+            damagesource = this.damageSources().arrow(this, entity1);
+            if (entity1 instanceof LivingEntity) {
+                ((LivingEntity) entity1).setLastHurtMob(entity);
             }
         }
 
-        boolean bl = entity.getType() == EntityType.ENDERMAN;
-        int j = entity.getRemainingFireTicks();
-        if (this.isOnFire() && !bl) {
-            entity.igniteForSeconds(5);
+        if (this.isCritArrow()) damagesource = damagesource.critical(); // Paper - add critical damage API
+        boolean flag = entity.getType() == EntityType.ENDERMAN;
+        int k = entity.getRemainingFireTicks();
+
+        if (this.isOnFire() && !flag) {
+            // CraftBukkit start
+            EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5);
+            org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent);
+            if (!combustEvent.isCancelled()) {
+                entity.igniteForSeconds(combustEvent.getDuration(), false);
+            }
+            // CraftBukkit end
         }
 
-        if (entity.hurt(damageSource, (float)i)) {
-            if (bl) {
+        if (entity.hurt(damagesource, (float) i)) {
+            if (flag) {
                 return;
             }
 
-            if (entity instanceof LivingEntity livingEntity) {
+            if (entity instanceof LivingEntity) {
+                LivingEntity entityliving = (LivingEntity) entity;
+
                 if (!this.level().isClientSide && this.getPierceLevel() <= 0) {
-                    livingEntity.setArrowCount(livingEntity.getArrowCount() + 1);
+                    entityliving.setArrowCount(entityliving.getArrowCount() + 1);
                 }
 
                 if (this.knockback > 0) {
-                    double d = Math.max(0.0, 1.0 - livingEntity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE));
-                    Vec3 vec3 = this.getDeltaMovement().multiply(1.0, 0.0, 1.0).normalize().scale((double)this.knockback * 0.6 * d);
-                    if (vec3.lengthSqr() > 0.0) {
-                        livingEntity.push(vec3.x, 0.1, vec3.z);
+                    double d0 = Math.max(0.0D, 1.0D - entityliving.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE));
+                    Vec3 vec3d = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D).normalize().scale((double) this.knockback * 0.6D * d0);
+
+                    if (vec3d.lengthSqr() > 0.0D) {
+                        entityliving.push(vec3d.x, 0.1D, vec3d.z, this); // Paper - pass causing entity for knockback events
                     }
                 }
 
-                if (!this.level().isClientSide && entity2 instanceof LivingEntity) {
-                    EnchantmentHelper.doPostHurtEffects(livingEntity, entity2);
-                    EnchantmentHelper.doPostDamageEffects((LivingEntity)entity2, livingEntity);
+                if (!this.level().isClientSide && entity1 instanceof LivingEntity) {
+                    EnchantmentHelper.doPostHurtEffects(entityliving, entity1);
+                    EnchantmentHelper.doPostDamageEffects((LivingEntity) entity1, entityliving);
                 }
 
-                this.doPostHurtEffects(livingEntity);
-                if (entity2 != null && livingEntity != entity2 && livingEntity instanceof Player && entity2 instanceof ServerPlayer && !this.isSilent()) {
-                    ((ServerPlayer)entity2).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F));
+                this.doPostHurtEffects(entityliving);
+                if (entity1 != null && entityliving != entity1 && entityliving instanceof Player && entity1 instanceof ServerPlayer && !this.isSilent()) {
+                    ((ServerPlayer) entity1).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F));
                 }
 
                 if (!entity.isAlive() && this.piercedAndKilledEntities != null) {
-                    this.piercedAndKilledEntities.add(livingEntity);
+                    this.piercedAndKilledEntities.add(entityliving);
                 }
 
-                if (!this.level().isClientSide && entity2 instanceof ServerPlayer serverPlayer) {
+                if (!this.level().isClientSide && entity1 instanceof ServerPlayer) {
+                    ServerPlayer entityplayer = (ServerPlayer) entity1;
+
                     if (this.piercedAndKilledEntities != null && this.shotFromCrossbow()) {
-                        CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverPlayer, this.piercedAndKilledEntities);
+                        CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(entityplayer, (Collection) this.piercedAndKilledEntities);
                     } else if (!entity.isAlive() && this.shotFromCrossbow()) {
-                        CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverPlayer, Arrays.asList(entity));
+                        CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(entityplayer, (Collection) Arrays.asList(entity));
                     }
                 }
             }
 
             this.playSound(this.soundEvent, 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F));
             if (this.getPierceLevel() <= 0) {
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
             }
         } else {
-            entity.setRemainingFireTicks(j);
+            entity.setRemainingFireTicks(k);
             this.deflect(ProjectileDeflection.REVERSE, entity, this.getOwner(), false);
-            this.setDeltaMovement(this.getDeltaMovement().scale(0.2));
-            if (!this.level().isClientSide && this.getDeltaMovement().lengthSqr() < 1.0E-7) {
+            this.setDeltaMovement(this.getDeltaMovement().scale(0.2D));
+            if (!this.level().isClientSide && this.getDeltaMovement().lengthSqr() < 1.0E-7D) {
                 if (this.pickup == AbstractArrow.Pickup.ALLOWED) {
                     this.spawnAtLocation(this.getPickupItem(), 0.1F);
                 }
 
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause
             }
         }
+
     }
 
     @Override
     protected void onHitBlock(BlockHitResult blockHitResult) {
         this.lastState = this.level().getBlockState(blockHitResult.getBlockPos());
         super.onHitBlock(blockHitResult);
-        Vec3 vec3 = blockHitResult.getLocation().subtract(this.getX(), this.getY(), this.getZ());
-        this.setDeltaMovement(vec3);
-        Vec3 vec32 = vec3.normalize().scale(0.05F);
-        this.setPosRaw(this.getX() - vec32.x, this.getY() - vec32.y, this.getZ() - vec32.z);
+        Vec3 vec3d = blockHitResult.getLocation().subtract(this.getX(), this.getY(), this.getZ());
+
+        this.setDeltaMovement(vec3d);
+        Vec3 vec3d1 = vec3d.normalize().scale(0.05000000074505806D);
+
+        this.setPosRaw(this.getX() - vec3d1.x, this.getY() - vec3d1.y, this.getZ() - vec3d1.z);
         this.playSound(this.getHitGroundSoundEvent(), 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F));
         this.inGround = true;
         this.shakeTime = 7;
         this.setCritArrow(false);
-        this.setPierceLevel((byte)0);
+        this.setPierceLevel((byte) 0);
         this.setSoundEvent(SoundEvents.ARROW_HIT);
         this.setShotFromCrossbow(false);
         this.resetPiercedEntities();
@@ -441,14 +512,11 @@
         return this.soundEvent;
     }
 
-    protected void doPostHurtEffects(LivingEntity target) {
-    }
+    protected void doPostHurtEffects(LivingEntity target) {}
 
     @Nullable
     protected EntityHitResult findHitEntity(Vec3 currentPosition, Vec3 nextPosition) {
-        return ProjectileUtil.getEntityHitResult(
-            this.level(), this, currentPosition, nextPosition, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0), this::canHitEntity
-        );
+        return ProjectileUtil.getEntityHitResult(this.level(), this, currentPosition, nextPosition, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0D), this::canHitEntity);
     }
 
     @Override
@@ -459,14 +527,14 @@
     @Override
     public void addAdditionalSaveData(CompoundTag nbt) {
         super.addAdditionalSaveData(nbt);
-        nbt.putShort("life", (short)this.life);
+        nbt.putShort("life", (short) this.life);
         if (this.lastState != null) {
             nbt.put("inBlockState", NbtUtils.writeBlockState(this.lastState));
         }
 
-        nbt.putByte("shake", (byte)this.shakeTime);
+        nbt.putByte("shake", (byte) this.shakeTime);
         nbt.putBoolean("inGround", this.inGround);
-        nbt.putByte("pickup", (byte)this.pickup.ordinal());
+        nbt.putByte("pickup", (byte) this.pickup.ordinal());
         nbt.putDouble("damage", this.baseDamage);
         nbt.putBoolean("crit", this.isCritArrow());
         nbt.putByte("PierceLevel", this.getPierceLevel());
@@ -493,17 +561,16 @@
         this.setCritArrow(nbt.getBoolean("crit"));
         this.setPierceLevel(nbt.getByte("PierceLevel"));
         if (nbt.contains("SoundEvent", 8)) {
-            this.soundEvent = BuiltInRegistries.SOUND_EVENT
-                .getOptional(new ResourceLocation(nbt.getString("SoundEvent")))
-                .orElse(this.getDefaultHitGroundSoundEvent());
+            this.soundEvent = (SoundEvent) BuiltInRegistries.SOUND_EVENT.getOptional(ResourceLocation.tryParse(nbt.getString("SoundEvent"))).orElse(this.getDefaultHitGroundSoundEvent()); // Paper - Validate resource location
         }
 
         this.setShotFromCrossbow(nbt.getBoolean("ShotFromCrossbow"));
         if (nbt.contains("item", 10)) {
-            this.setPickupItemStack(ItemStack.parse(this.registryAccess(), nbt.getCompound("item")).orElse(this.getDefaultPickupItem()));
+            this.setPickupItemStack((ItemStack) ItemStack.parse(this.registryAccess(), nbt.getCompound("item")).orElse(this.getDefaultPickupItem()));
         } else {
             this.setPickupItemStack(this.getDefaultPickupItem());
         }
+
     }
 
     @Override
@@ -512,24 +579,53 @@
         if (entity instanceof Player && this.pickup == AbstractArrow.Pickup.DISALLOWED) {
             this.pickup = AbstractArrow.Pickup.ALLOWED;
         }
+
     }
 
     @Override
     public void playerTouch(Player player) {
         if (!this.level().isClientSide && (this.inGround || this.isNoPhysics()) && this.shakeTime <= 0) {
-            if (this.tryPickup(player)) {
+            // CraftBukkit start
+            ItemStack itemstack = this.getPickupItem();
+            if (this.pickup == Pickup.ALLOWED && !itemstack.isEmpty() && player.getInventory().canHold(itemstack) > 0) {
+                ItemEntity item = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack);
+                PlayerPickupArrowEvent event = new PlayerPickupArrowEvent((org.bukkit.entity.Player) player.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.level().getCraftServer(), item), (org.bukkit.entity.AbstractArrow) this.getBukkitEntity());
+                // event.setCancelled(!entityhuman.canPickUpLoot); TODO
+                this.level().getCraftServer().getPluginManager().callEvent(event);
+
+                if (event.isCancelled()) {
+                    return;
+                }
+                itemstack = item.getItem();
+            }
+
+            if ((this.pickup == AbstractArrow.Pickup.ALLOWED && player.getInventory().add(itemstack)) || (this.pickup == AbstractArrow.Pickup.CREATIVE_ONLY && player.getAbilities().instabuild)) {
+                // CraftBukkit end
                 player.take(this, 1);
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
             }
+
         }
     }
 
     protected boolean tryPickup(Player player) {
-        return switch (this.pickup) {
-            case DISALLOWED -> false;
-            case ALLOWED -> player.getInventory().add(this.getPickupItem());
-            case CREATIVE_ONLY -> player.hasInfiniteMaterials();
-        };
+        boolean flag;
+
+        switch (this.pickup.ordinal()) {
+            case 0:
+                flag = false;
+                break;
+            case 1:
+                flag = player.getInventory().add(this.getPickupItem());
+                break;
+            case 2:
+                flag = player.hasInfiniteMaterials();
+                break;
+            default:
+                throw new MatchException((String) null, (Throwable) null);
+        }
+
+        return flag;
     }
 
     public ItemStack getPickupItem() {
@@ -573,16 +669,18 @@
     }
 
     public void setPierceLevel(byte level) {
-        this.entityData.set(PIERCE_LEVEL, level);
+        this.entityData.set(AbstractArrow.PIERCE_LEVEL, level);
     }
 
     private void setFlag(int index, boolean flag) {
-        byte b = this.entityData.get(ID_FLAGS);
+        byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
+
         if (flag) {
-            this.entityData.set(ID_FLAGS, (byte)(b | index));
+            this.entityData.set(AbstractArrow.ID_FLAGS, (byte) (b0 | index));
         } else {
-            this.entityData.set(ID_FLAGS, (byte)(b & ~index));
+            this.entityData.set(AbstractArrow.ID_FLAGS, (byte) (b0 & ~index));
         }
+
     }
 
     public void setPickupItemStack(ItemStack stack) {
@@ -591,28 +689,32 @@
         } else {
             this.pickupItemStack = this.getDefaultPickupItem();
         }
+
     }
 
     public boolean isCritArrow() {
-        byte b = this.entityData.get(ID_FLAGS);
-        return (b & 1) != 0;
+        byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
+
+        return (b0 & 1) != 0;
     }
 
     public boolean shotFromCrossbow() {
-        byte b = this.entityData.get(ID_FLAGS);
-        return (b & 4) != 0;
+        byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
+
+        return (b0 & 4) != 0;
     }
 
     public byte getPierceLevel() {
-        return this.entityData.get(PIERCE_LEVEL);
+        return (Byte) this.entityData.get(AbstractArrow.PIERCE_LEVEL);
     }
 
     public void setEnchantmentEffectsFromEntity(LivingEntity entity, float damageModifier) {
         int i = EnchantmentHelper.getEnchantmentLevel(Enchantments.POWER, entity);
         int j = EnchantmentHelper.getEnchantmentLevel(Enchantments.PUNCH, entity);
-        this.setBaseDamage((double)(damageModifier * 2.0F) + this.random.triangle((double)this.level().getDifficulty().getId() * 0.11, 0.57425));
+
+        this.setBaseDamage((double) (damageModifier * 2.0F) + this.random.triangle((double) this.level().getDifficulty().getId() * 0.11D, 0.57425D));
         if (i > 0) {
-            this.setBaseDamage(this.getBaseDamage() + (double)i * 0.5 + 0.5);
+            this.setBaseDamage(this.getBaseDamage() + (double) i * 0.5D + 0.5D);
         }
 
         if (j > 0) {
@@ -622,6 +724,7 @@
         if (EnchantmentHelper.getEnchantmentLevel(Enchantments.FLAME, entity) > 0) {
             this.igniteForSeconds(100);
         }
+
     }
 
     protected float getWaterInertia() {
@@ -634,7 +737,7 @@
     }
 
     public boolean isNoPhysics() {
-        return !this.level().isClientSide ? this.noPhysics : (this.entityData.get(ID_FLAGS) & 2) != 0;
+        return !this.level().isClientSide ? this.noPhysics : ((Byte) this.entityData.get(AbstractArrow.ID_FLAGS) & 2) != 0;
     }
 
     public void setShotFromCrossbow(boolean shotFromCrossbow) {
@@ -652,9 +755,10 @@
     }
 
     public static enum Pickup {
-        DISALLOWED,
-        ALLOWED,
-        CREATIVE_ONLY;
+
+        DISALLOWED, ALLOWED, CREATIVE_ONLY;
+
+        private Pickup() {}
 
         public static AbstractArrow.Pickup byOrdinal(int ordinal) {
             if (ordinal < 0 || ordinal > values().length) {
