--- a/net/minecraft/world/entity/item/EntityItem.java +++ b/net/minecraft/world/entity/item/EntityItem.java @@ -33,6 +33,15 @@ import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.Vec3D; +// CraftBukkit start +import net.minecraft.server.MinecraftServer; +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 + public class EntityItem extends Entity implements TraceableEntity { private static final DataWatcherObject DATA_ITEM = DataWatcher.defineId(EntityItem.class, DataWatcherRegistry.ITEM_STACK); @@ -51,6 +60,7 @@ @Nullable public UUID target; public final float bobOffs; + private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit public EntityItem(EntityTypes entitytypes, World world) { super(entitytypes, world); @@ -132,12 +142,15 @@ @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; + if (this.age != -32768) this.age += elapsedTicks; + this.lastTick = MinecraftServer.currentTick; + // CraftBukkit end this.xo = this.getX(); this.yo = this.getY(); @@ -186,9 +199,11 @@ this.mergeWithNeighbours(); } + /* CraftBukkit start - moved up if (this.age != -32768) { ++this.age; } + // CraftBukkit end */ this.hasImpulse |= this.updateInWaterStateAndDoFluidPushing(); if (!this.level().isClientSide) { @@ -200,7 +215,13 @@ } if (!this.level().isClientSide && this.age >= 6000) { - this.discard(); + // 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 } } @@ -283,11 +304,16 @@ } private static void merge(EntityItem entityitem, ItemStack itemstack, EntityItem entityitem1, ItemStack itemstack1) { + // CraftBukkit start + if (!CraftEventFactory.callItemMergeEvent(entityitem1, entityitem)) { + return; + } + // CraftBukkit end merge(entityitem, itemstack, itemstack1); entityitem.pickupDelay = Math.max(entityitem.pickupDelay, entityitem1.pickupDelay); entityitem.age = Math.min(entityitem.age, entityitem1.age); if (itemstack1.isEmpty()) { - entityitem1.discard(); + entityitem1.discard(EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause); } } @@ -308,12 +334,17 @@ } else if (this.level().isClientSide) { return true; } else { + // CraftBukkit start + if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { + return false; + } + // CraftBukkit end this.markHurt(); this.health = (int) ((float) this.health - f); this.gameEvent(GameEvent.ENTITY_DAMAGE, damagesource.getEntity()); if (this.health <= 0) { this.getItem().onDestroyed(this); - this.discard(); + this.discard(EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause } return true; @@ -365,7 +396,7 @@ } if (this.getItem().isEmpty()) { - this.discard(); + this.discard(null); // CraftBukkit - add Bukkit remove cause } } @@ -377,10 +408,50 @@ Item item = itemstack.getItem(); int i = itemstack.getCount(); + // CraftBukkit start - fire PlayerPickupItemEvent + int canHold = entityhuman.getInventory().canHold(itemstack); + int remaining = i - canHold; + + if (this.pickupDelay <= 0 && canHold > 0) { + itemstack.setCount(canHold); + // Call legacy event + PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); + playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems()); + this.level().getCraftServer().getPluginManager().callEvent(playerEvent); + if (playerEvent.isCancelled()) { + itemstack.setCount(i); // SPIGOT-5294 - restore count + return; + } + + // Call newer event afterwards + EntityPickupItemEvent entityEvent = new EntityPickupItemEvent((Player) entityhuman.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(entityhuman.getUUID())) && entityhuman.getInventory().add(itemstack)) { entityhuman.take(this, i); if (itemstack.isEmpty()) { - this.discard(); + this.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause itemstack.setCount(i); }