diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java index 3cbfcc4..0b14727 100644 --- a/src/main/java/net/minecraft/server/EntityItem.java +++ b/src/main/java/net/minecraft/server/EntityItem.java @@ -4,10 +4,15 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.event.player.PlayerPickupItemEvent; +import java.util.List; import java.util.logging.Level; public class EntityItem extends Entity { + private static final double MERGE_RADIUS = 5.0D; + private static final double MERGE_RADIUS_SQUARED = MERGE_RADIUS * MERGE_RADIUS; + private static final int MERGE_INTERVAL_TICKS = 10; + public ItemStack itemStack; private int e; public int b = 0; @@ -100,6 +105,13 @@ public class EntityItem extends Entity { ++this.e; ++this.b; + + // Merge only settled items. This avoids combining drops while they are still + // being thrown, while keeping large piles from creating excess entities. + if (this.onGround && this.b % MERGE_INTERVAL_TICKS == 0) { + this.mergeNearbyItems(); + } + if (this.b >= 6000) { //Project Poseidon Start if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { @@ -111,6 +123,51 @@ public class EntityItem extends Entity { } } + /** + * Merges compatible, grounded item entities within {@link #MERGE_RADIUS} blocks. + * The item count is never allowed to exceed the item's normal stack limit. + */ + private void mergeNearbyItems() { + if (this.dead || !this.itemStack.isStackable() || this.itemStack.count >= this.itemStack.getMaxStackSize()) { + return; + } + + List nearbyItems = this.world.a(EntityItem.class, this.boundingBox.b(MERGE_RADIUS, MERGE_RADIUS, MERGE_RADIUS)); + + for (int i = 0; i < nearbyItems.size() && this.itemStack.count < this.itemStack.getMaxStackSize(); ++i) { + EntityItem nearbyItem = (EntityItem) nearbyItems.get(i); + + if (nearbyItem == this || nearbyItem.dead || !nearbyItem.onGround + || !this.itemStack.doMaterialsMatch(nearbyItem.itemStack) + || !nearbyItem.itemStack.isStackable()) { + continue; + } + + double deltaX = this.locX - nearbyItem.locX; + double deltaY = this.locY - nearbyItem.locY; + double deltaZ = this.locZ - nearbyItem.locZ; + if (deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ > MERGE_RADIUS_SQUARED) { + continue; + } + + int transfer = Math.min(this.itemStack.getMaxStackSize() - this.itemStack.count, nearbyItem.itemStack.count); + if (transfer <= 0) { + continue; + } + + this.itemStack.count += transfer; + nearbyItem.itemStack.count -= transfer; + + // Preserve the stricter expiry and pickup delay when two piles combine. + this.b = Math.max(this.b, nearbyItem.b); + this.pickupDelay = Math.max(this.pickupDelay, nearbyItem.pickupDelay); + + if (nearbyItem.itemStack.count <= 0) { + nearbyItem.die(); + } + } + } + public boolean f_() { return this.world.a(this.boundingBox, Material.WATER, this); }