--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -1,5 +1,7 @@
 package net.minecraft.world.entity.item;
 
+import java.util.Iterator;
+import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 import javax.annotation.Nullable;
@@ -10,9 +12,6 @@
 import net.minecraft.network.syncher.EntityDataAccessor;
 import net.minecraft.network.syncher.EntityDataSerializers;
 import net.minecraft.network.syncher.SynchedEntityData;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.sounds.SoundSource;
-import net.minecraft.stats.Stats;
 import net.minecraft.tags.DamageTypeTags;
 import net.minecraft.tags.FluidTags;
 import net.minecraft.tags.ItemTags;
@@ -23,7 +22,6 @@
 import net.minecraft.world.entity.MoverType;
 import net.minecraft.world.entity.SlotAccess;
 import net.minecraft.world.entity.TraceableEntity;
-import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.item.Item;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.Items;
@@ -31,8 +29,21 @@
 import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.level.portal.DimensionTransition;
 import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.stats.Stats;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.entity.Player;
+import org.bukkit.event.entity.EntityPickupItemEvent;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.player.PlayerPickupItemEvent;
+// CraftBukkit end
+import org.bukkit.event.player.PlayerAttemptPickupItemEvent; // Paper
 
 public class ItemEntity extends Entity implements TraceableEntity {
+
     private static final EntityDataAccessor<ItemStack> DATA_ITEM = SynchedEntityData.defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK);
     private static final float FLOAT_HEIGHT = 0.1F;
     public static final float EYE_HEIGHT = 0.2125F;
@@ -41,7 +52,7 @@
     private static final int INFINITE_LIFETIME = -32768;
     public int age;
     public int pickupDelay;
-    public int health = 5;
+    public int health;
     @Nullable
     public UUID thrower;
     @Nullable
@@ -49,15 +60,25 @@
     @Nullable
     public UUID target;
     public final float bobOffs;
+    private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit
+    public boolean canMobPickup = true; // Paper - Item#canEntityPickup
+    private int despawnRate = -1; // Paper - Alternative item-despawn-rate
+    public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
 
     public ItemEntity(EntityType<? extends ItemEntity> type, Level world) {
         super(type, world);
-        this.bobOffs = this.random.nextFloat() * (float) Math.PI * 2.0F;
+        this.health = 5;
+        this.bobOffs = this.random.nextFloat() * 3.1415927F * 2.0F;
         this.setYRot(this.random.nextFloat() * 360.0F);
     }
 
     public ItemEntity(Level world, double x, double y, double z, ItemStack stack) {
-        this(world, x, y, z, stack, world.random.nextDouble() * 0.2 - 0.1, 0.2, world.random.nextDouble() * 0.2 - 0.1);
+        // Paper start - Don't use level random in entity constructors (to make them thread-safe)
+        this(EntityType.ITEM, world);
+        this.setPos(x, y, z);
+        this.setDeltaMovement(this.random.nextDouble() * 0.2D - 0.1D, 0.2D, this.random.nextDouble() * 0.2D - 0.1D);
+        this.setItem(stack);
+        // Paper end - Don't use level random in entity constructors
     }
 
     public ItemEntity(Level world, double x, double y, double z, ItemStack stack, double velocityX, double velocityY, double velocityZ) {
@@ -69,6 +90,7 @@
 
     private ItemEntity(ItemEntity entity) {
         super(entity.getType(), entity.level());
+        this.health = 5;
         this.setItem(entity.getItem().copy());
         this.copyPosition(entity);
         this.age = entity.age;
@@ -85,10 +107,18 @@
     public Entity getOwner() {
         if (this.cachedThrower != null && !this.cachedThrower.isRemoved()) {
             return this.cachedThrower;
-        } else if (this.thrower != null && this.level() instanceof ServerLevel serverLevel) {
-            this.cachedThrower = serverLevel.getEntity(this.thrower);
-            return this.cachedThrower;
         } else {
+            if (this.thrower != null) {
+                Level world = this.level();
+
+                if (world instanceof ServerLevel) {
+                    ServerLevel worldserver = (ServerLevel) world;
+
+                    this.cachedThrower = worldserver.getEntity(this.thrower);
+                    return this.cachedThrower;
+                }
+            }
+
             return null;
         }
     }
@@ -96,9 +126,10 @@
     @Override
     public void restoreFrom(Entity original) {
         super.restoreFrom(original);
-        if (original instanceof ItemEntity itemEntity) {
-            this.cachedThrower = itemEntity.cachedThrower;
+        if (original instanceof ItemEntity entityitem) {
+            this.cachedThrower = entityitem.cachedThrower;
         }
+
     }
 
     @Override
@@ -108,31 +139,36 @@
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
-        builder.define(DATA_ITEM, ItemStack.EMPTY);
+        builder.define(ItemEntity.DATA_ITEM, ItemStack.EMPTY);
     }
 
     @Override
     protected double getDefaultGravity() {
-        return 0.04;
+        return 0.04D;
     }
 
     @Override
     public void tick() {
         if (this.getItem().isEmpty()) {
-            this.discard();
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
         } else {
             super.tick();
-            if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
-                this.pickupDelay--;
-            }
+            // CraftBukkit start - Use wall time for pickup and despawn timers
+            int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
+            if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
+            this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
+            if (this.age != -32768) this.age += elapsedTicks;
+            this.lastTick = MinecraftServer.currentTick;
+            // CraftBukkit end
 
             this.xo = this.getX();
             this.yo = this.getY();
             this.zo = this.getZ();
-            Vec3 vec3 = this.getDeltaMovement();
-            if (this.isInWater() && this.getFluidHeight(FluidTags.WATER) > 0.1F) {
+            Vec3 vec3d = this.getDeltaMovement();
+
+            if (this.isInWater() && this.getFluidHeight(FluidTags.WATER) > 0.10000000149011612D) {
                 this.setUnderwaterMovement();
-            } else if (this.isInLava() && this.getFluidHeight(FluidTags.LAVA) > 0.1F) {
+            } else if (this.isInLava() && this.getFluidHeight(FluidTags.LAVA) > 0.10000000149011612D) {
                 this.setUnderLavaMovement();
             } else {
                 this.applyGravity();
@@ -141,125 +177,192 @@
             if (this.level().isClientSide) {
                 this.noPhysics = false;
             } else {
-                this.noPhysics = !this.level().noCollision(this, this.getBoundingBox().deflate(1.0E-7));
+                this.noPhysics = !this.level().noCollision(this, this.getBoundingBox().deflate(1.0E-7D));
                 if (this.noPhysics) {
-                    this.moveTowardsClosestSpace(this.getX(), (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0, this.getZ());
+                    this.moveTowardsClosestSpace(this.getX(), (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D, this.getZ());
                 }
             }
 
-            if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F || (this.tickCount + this.getId()) % 4 == 0) {
+            if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D || (this.tickCount + this.getId()) % 4 == 0) { // Paper - Diff on change
                 this.move(MoverType.SELF, this.getDeltaMovement());
                 float f = 0.98F;
-                if (this.onGround()) {
+
+                // Paper start - Friction API
+                if (frictionState == net.kyori.adventure.util.TriState.FALSE) {
+                    f = 1F;
+                } else if (this.onGround()) {
+                    // Paper end - Friction API
                     f = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98F;
                 }
 
-                this.setDeltaMovement(this.getDeltaMovement().multiply((double)f, 0.98, (double)f));
+                this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 0.98D, (double) f));
                 if (this.onGround()) {
-                    Vec3 vec32 = this.getDeltaMovement();
-                    if (vec32.y < 0.0) {
-                        this.setDeltaMovement(vec32.multiply(1.0, -0.5, 1.0));
+                    Vec3 vec3d1 = this.getDeltaMovement();
+
+                    if (vec3d1.y < 0.0D) {
+                        this.setDeltaMovement(vec3d1.multiply(1.0D, -0.5D, 1.0D));
                     }
                 }
             }
 
-            boolean bl = Mth.floor(this.xo) != Mth.floor(this.getX())
-                || Mth.floor(this.yo) != Mth.floor(this.getY())
-                || Mth.floor(this.zo) != Mth.floor(this.getZ());
-            int i = bl ? 2 : 40;
+            boolean flag = Mth.floor(this.xo) != Mth.floor(this.getX()) || Mth.floor(this.yo) != Mth.floor(this.getY()) || Mth.floor(this.zo) != Mth.floor(this.getZ());
+            int i = flag ? 2 : 40;
+
             if (this.tickCount % i == 0 && !this.level().isClientSide && this.isMergable()) {
                 this.mergeWithNeighbours();
             }
 
+            /* CraftBukkit start - moved up
             if (this.age != -32768) {
-                this.age++;
+                ++this.age;
             }
+            // CraftBukkit end */
 
-            this.hasImpulse = this.hasImpulse | this.updateInWaterStateAndDoFluidPushing();
+            this.hasImpulse |= this.updateInWaterStateAndDoFluidPushing();
             if (!this.level().isClientSide) {
-                double d = this.getDeltaMovement().subtract(vec3).lengthSqr();
-                if (d > 0.01) {
+                double d0 = this.getDeltaMovement().subtract(vec3d).lengthSqr();
+
+                if (d0 > 0.01D) {
                     this.hasImpulse = true;
                 }
             }
 
-            if (!this.level().isClientSide && this.age >= 6000) {
-                this.discard();
+            if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
+                // CraftBukkit start - fire ItemDespawnEvent
+                if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
+                    this.age = 0;
+                    return;
+                }
+                // CraftBukkit end
+                this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
             }
+
         }
     }
 
+    // Spigot start - copied from above
+    @Override
+    public void inactiveTick() {
+        // CraftBukkit start - Use wall time for pickup and despawn timers
+        int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
+        if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
+        this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
+        if (this.age != -32768) this.age += elapsedTicks;
+        this.lastTick = MinecraftServer.currentTick;
+        // CraftBukkit end
+
+        if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
+            // CraftBukkit start - fire ItemDespawnEvent
+            if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
+                this.age = 0;
+                return;
+            }
+            // CraftBukkit end
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+        }
+    }
+    // Spigot end
+
     @Override
     public BlockPos getBlockPosBelowThatAffectsMyMovement() {
         return this.getOnPos(0.999999F);
     }
 
     private void setUnderwaterMovement() {
-        Vec3 vec3 = this.getDeltaMovement();
-        this.setDeltaMovement(vec3.x * 0.99F, vec3.y + (double)(vec3.y < 0.06F ? 5.0E-4F : 0.0F), vec3.z * 0.99F);
+        Vec3 vec3d = this.getDeltaMovement();
+
+        this.setDeltaMovement(vec3d.x * 0.9900000095367432D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.9900000095367432D);
     }
 
     private void setUnderLavaMovement() {
-        Vec3 vec3 = this.getDeltaMovement();
-        this.setDeltaMovement(vec3.x * 0.95F, vec3.y + (double)(vec3.y < 0.06F ? 5.0E-4F : 0.0F), vec3.z * 0.95F);
+        Vec3 vec3d = this.getDeltaMovement();
+
+        this.setDeltaMovement(vec3d.x * 0.949999988079071D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.949999988079071D);
     }
 
     private void mergeWithNeighbours() {
         if (this.isMergable()) {
-            for (ItemEntity itemEntity : this.level()
-                .getEntitiesOfClass(
-                    ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), otherItemEntity -> otherItemEntity != this && otherItemEntity.isMergable()
-                )) {
-                if (itemEntity.isMergable()) {
-                    this.tryToMerge(itemEntity);
+            // Spigot start
+            double radius = this.level().spigotConfig.itemMerge;
+            List<ItemEntity> list = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, this.level().paperConfig().entities.behavior.onlyMergeItemsHorizontally ? 0 : radius - 0.5D, radius), (entityitem) -> { // Paper - configuration to only merge items horizontally
+                // Spigot end
+                return entityitem != this && entityitem.isMergable();
+            });
+            Iterator iterator = list.iterator();
+
+            while (iterator.hasNext()) {
+                ItemEntity entityitem = (ItemEntity) iterator.next();
+
+                if (entityitem.isMergable()) {
+                    // Paper start - Fix items merging through walls
+                    if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) {
+                        if (this.level().clipDirect(this.position(), entityitem.position(),
+                            net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.HitResult.Type.BLOCK) {
+                            continue;
+                        }
+                    }
+                    // Paper end - Fix items merging through walls
+                    this.tryToMerge(entityitem);
                     if (this.isRemoved()) {
                         break;
                     }
                 }
             }
+
         }
     }
 
     private boolean isMergable() {
-        ItemStack itemStack = this.getItem();
-        return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < 6000 && itemStack.getCount() < itemStack.getMaxStackSize();
+        ItemStack itemstack = this.getItem();
+
+        return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && itemstack.getCount() < itemstack.getMaxStackSize(); // Paper - Alternative item-despawn-rate
     }
 
     private void tryToMerge(ItemEntity other) {
-        ItemStack itemStack = this.getItem();
-        ItemStack itemStack2 = other.getItem();
-        if (Objects.equals(this.target, other.target) && areMergable(itemStack, itemStack2)) {
-            if (itemStack2.getCount() < itemStack.getCount()) {
-                merge(this, itemStack, other, itemStack2);
+        ItemStack itemstack = this.getItem();
+        ItemStack itemstack1 = other.getItem();
+
+        if (Objects.equals(this.target, other.target) && ItemEntity.areMergable(itemstack, itemstack1)) {
+            if (true || itemstack1.getCount() < itemstack.getCount()) { // Spigot
+                ItemEntity.merge(this, itemstack, other, itemstack1);
             } else {
-                merge(other, itemStack2, this, itemStack);
+                ItemEntity.merge(other, itemstack1, this, itemstack);
             }
+
         }
     }
 
     public static boolean areMergable(ItemStack stack1, ItemStack stack2) {
-        return stack2.getCount() + stack1.getCount() <= stack2.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2);
+        return stack2.getCount() + stack1.getCount() > stack2.getMaxStackSize() ? false : ItemStack.isSameItemSameComponents(stack1, stack2);
     }
 
     public static ItemStack merge(ItemStack stack1, ItemStack stack2, int maxCount) {
-        int i = Math.min(Math.min(stack1.getMaxStackSize(), maxCount) - stack1.getCount(), stack2.getCount());
-        ItemStack itemStack = stack1.copyWithCount(stack1.getCount() + i);
-        stack2.shrink(i);
-        return itemStack;
+        int j = Math.min(Math.min(stack1.getMaxStackSize(), maxCount) - stack1.getCount(), stack2.getCount());
+        ItemStack itemstack2 = stack1.copyWithCount(stack1.getCount() + j);
+
+        stack2.shrink(j);
+        return itemstack2;
     }
 
     private static void merge(ItemEntity targetEntity, ItemStack stack1, ItemStack stack2) {
-        ItemStack itemStack = merge(stack1, stack2, 64);
-        targetEntity.setItem(itemStack);
+        ItemStack itemstack2 = ItemEntity.merge(stack1, stack2, 64);
+
+        targetEntity.setItem(itemstack2);
     }
 
     private static void merge(ItemEntity targetEntity, ItemStack targetStack, ItemEntity sourceEntity, ItemStack sourceStack) {
-        merge(targetEntity, targetStack, sourceStack);
+        // CraftBukkit start
+        if (!CraftEventFactory.callItemMergeEvent(sourceEntity, targetEntity)) {
+            return;
+        }
+        // CraftBukkit end
+        ItemEntity.merge(targetEntity, targetStack, sourceStack);
         targetEntity.pickupDelay = Math.max(targetEntity.pickupDelay, sourceEntity.pickupDelay);
         targetEntity.age = Math.min(targetEntity.age, sourceEntity.age);
         if (sourceStack.isEmpty()) {
-            sourceEntity.discard();
+            sourceEntity.discard(EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause);
         }
+
     }
 
     @Override
@@ -278,12 +381,17 @@
         } else if (this.level().isClientSide) {
             return true;
         } else {
+            // CraftBukkit start
+            if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount)) {
+                return false;
+            }
+            // CraftBukkit end
             this.markHurt();
-            this.health = (int)((float)this.health - amount);
+            this.health = (int) ((float) this.health - amount);
             this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity());
             if (this.health <= 0) {
                 this.getItem().onDestroyed(this);
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
             }
 
             return true;
@@ -292,9 +400,14 @@
 
     @Override
     public void addAdditionalSaveData(CompoundTag nbt) {
-        nbt.putShort("Health", (short)this.health);
-        nbt.putShort("Age", (short)this.age);
-        nbt.putShort("PickupDelay", (short)this.pickupDelay);
+        // Paper start - Friction API
+        if (this.frictionState != net.kyori.adventure.util.TriState.NOT_SET) {
+            nbt.putString("Paper.FrictionState", this.frictionState.toString());
+        }
+        // Paper end - Friction API
+        nbt.putShort("Health", (short) this.health);
+        nbt.putShort("Age", (short) this.age);
+        nbt.putShort("PickupDelay", (short) this.pickupDelay);
         if (this.thrower != null) {
             nbt.putUUID("Thrower", this.thrower);
         }
@@ -306,6 +419,7 @@
         if (!this.getItem().isEmpty()) {
             nbt.put("Item", this.getItem().save(this.registryAccess()));
         }
+
     }
 
     @Override
@@ -326,40 +440,120 @@
         }
 
         if (nbt.contains("Item", 10)) {
-            CompoundTag compoundTag = nbt.getCompound("Item");
-            this.setItem(ItemStack.parse(this.registryAccess(), compoundTag).orElse(ItemStack.EMPTY));
+            CompoundTag nbttagcompound1 = nbt.getCompound("Item");
+
+            this.setItem((ItemStack) ItemStack.parse(this.registryAccess(), nbttagcompound1).orElse(ItemStack.EMPTY));
         } else {
             this.setItem(ItemStack.EMPTY);
         }
 
+        // Paper start - Friction API
+        if (nbt.contains("Paper.FrictionState")) {
+            String fs = nbt.getString("Paper.FrictionState");
+            try {
+                frictionState = net.kyori.adventure.util.TriState.valueOf(fs);
+            } catch (Exception ignored) {
+                com.mojang.logging.LogUtils.getLogger().error("Unknown friction state " + fs + " for " + this);
+            }
+        }
+        // Paper end - Friction API
+
         if (this.getItem().isEmpty()) {
-            this.discard();
+            this.discard(null); // CraftBukkit - add Bukkit remove cause
         }
+
     }
 
     @Override
-    public void playerTouch(Player player) {
+    public void playerTouch(net.minecraft.world.entity.player.Player player) {
         if (!this.level().isClientSide) {
-            ItemStack itemStack = this.getItem();
-            Item item = itemStack.getItem();
-            int i = itemStack.getCount();
-            if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemStack)) {
+            ItemStack itemstack = this.getItem();
+            Item item = itemstack.getItem();
+            int i = itemstack.getCount();
+
+            // CraftBukkit start - fire PlayerPickupItemEvent
+            int canHold = player.getInventory().canHold(itemstack);
+            int remaining = i - canHold;
+            boolean flyAtPlayer = false; // Paper
+
+            // Paper start - PlayerAttemptPickupItemEvent
+            if (this.pickupDelay <= 0) {
+                PlayerAttemptPickupItemEvent attemptEvent = new PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+                this.level().getCraftServer().getPluginManager().callEvent(attemptEvent);
+
+                flyAtPlayer = attemptEvent.getFlyAtPlayer();
+                if (attemptEvent.isCancelled()) {
+                    if (flyAtPlayer) {
+                        player.take(this, i);
+                    }
+
+                    return;
+                }
+            }
+            // Paper end - PlayerAttemptPickupItemEvent
+
+            if (this.pickupDelay <= 0 && canHold > 0) {
+                itemstack.setCount(canHold);
+                // Call legacy event
+                PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+                playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems());
+                this.level().getCraftServer().getPluginManager().callEvent(playerEvent);
+                flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper
+                if (playerEvent.isCancelled()) {
+                    itemstack.setCount(i); // SPIGOT-5294 - restore count
+                    // Paper start
+                    if (flyAtPlayer) {
+                        player.take(this, i);
+                    }
+                    // Paper end
+                    return;
+                }
+
+                // Call newer event afterwards
+                EntityPickupItemEvent entityEvent = new EntityPickupItemEvent((Player) player.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+                entityEvent.setCancelled(!entityEvent.getEntity().getCanPickupItems());
+                this.level().getCraftServer().getPluginManager().callEvent(entityEvent);
+                if (entityEvent.isCancelled()) {
+                    itemstack.setCount(i); // SPIGOT-5294 - restore count
+                    return;
+                }
+
+                // Update the ItemStack if it was changed in the event
+                ItemStack current = this.getItem();
+                if (!itemstack.equals(current)) {
+                    itemstack = current;
+                } else {
+                    itemstack.setCount(canHold + remaining); // = i
+                }
+
+                // Possibly < 0; fix here so we do not have to modify code below
+                this.pickupDelay = 0;
+            } else if (this.pickupDelay == 0) {
+                // ensure that the code below isn't triggered if canHold says we can't pick the items up
+                this.pickupDelay = -1;
+            }
+            // CraftBukkit end
+
+            if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemstack)) {
+                if (flyAtPlayer) // Paper - PlayerPickupItemEvent
                 player.take(this, i);
-                if (itemStack.isEmpty()) {
-                    this.discard();
-                    itemStack.setCount(i);
+                if (itemstack.isEmpty()) {
+                    this.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
+                    itemstack.setCount(i);
                 }
 
                 player.awardStat(Stats.ITEM_PICKED_UP.get(item), i);
                 player.onItemPickup(this);
             }
+
         }
     }
 
     @Override
     public Component getName() {
-        Component component = this.getCustomName();
-        return (Component)(component != null ? component : Component.translatable(this.getItem().getDescriptionId()));
+        Component ichatbasecomponent = this.getCustomName();
+
+        return (Component) (ichatbasecomponent != null ? ichatbasecomponent : Component.translatable(this.getItem().getDescriptionId()));
     }
 
     @Override
@@ -371,27 +565,30 @@
     @Override
     public Entity changeDimension(DimensionTransition teleportTarget) {
         Entity entity = super.changeDimension(teleportTarget);
-        if (!this.level().isClientSide && entity instanceof ItemEntity itemEntity) {
-            itemEntity.mergeWithNeighbours();
+
+        if (!this.level().isClientSide && entity instanceof ItemEntity entityitem) {
+            entityitem.mergeWithNeighbours();
         }
 
         return entity;
     }
 
     public ItemStack getItem() {
-        return this.getEntityData().get(DATA_ITEM);
+        return (ItemStack) this.getEntityData().get(ItemEntity.DATA_ITEM);
     }
 
     public void setItem(ItemStack stack) {
-        this.getEntityData().set(DATA_ITEM, stack);
+        this.getEntityData().set(ItemEntity.DATA_ITEM, stack);
+        this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate
     }
 
     @Override
     public void onSyncedDataUpdated(EntityDataAccessor<?> data) {
         super.onSyncedDataUpdated(data);
-        if (DATA_ITEM.equals(data)) {
+        if (ItemEntity.DATA_ITEM.equals(data)) {
             this.getItem().setEntityRepresentation(this);
         }
+
     }
 
     public void setTarget(@Nullable UUID owner) {
@@ -437,11 +634,11 @@
 
     public void makeFakeItem() {
         this.setNeverPickUp();
-        this.age = 5999;
+        this.age = this.despawnRate - 1; // Spigot // Paper - Alternative item-despawn-rate
     }
 
     public float getSpin(float tickDelta) {
-        return ((float)this.getAge() + tickDelta) / 20.0F + this.bobOffs;
+        return ((float) this.getAge() + tickDelta) / 20.0F + this.bobOffs;
     }
 
     public ItemEntity copy() {
@@ -455,7 +652,7 @@
 
     @Override
     public float getVisualRotationYInDegrees() {
-        return 180.0F - this.getSpin(0.5F) / (float) (Math.PI * 2) * 360.0F;
+        return 180.0F - this.getSpin(0.5F) / 6.2831855F * 360.0F;
     }
 
     @Override
