papermc/Spigot-Server-Patches/0139-Make-entities-look-for-hoppers.patch
Aikar 094bb03a37
Optimize Hoppers
- Lots of itemstack cloning removed. Only clone if the item is actually moved
- Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
  However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
- Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
- Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration
2018-02-12 23:26:02 -05:00

383 lines
17 KiB
Diff

From 1cbfa6cbaddedefe15fccdf12c0427b1727ac4dc Mon Sep 17 00:00:00 2001
From: Techcable <Techcable@outlook.com>
Date: Sat, 18 Jun 2016 01:01:37 -0500
Subject: [PATCH] Make entities look for hoppers
Every tick hoppers try and find an block-inventory to extract from.
If no tile entity is above the hopper (which there often isn't) it will do a bounding box search for minecart chests and minecart hoppers.
If it can't find an inventory, it will then look for a dropped item, which is another bounding box search.
This patch eliminates that expensive check by having dropped items and minecart hoppers/chests look for hoppers instead.
Hoppers are tile entities meaning you can do a simple tile entity lookup to find the nearest hopper in range.
Pushing out of hoppers causes a bouding box lookup, which this patch replaces with a tile entity lookup.
This patch may causes a decrease in the performance of dropped items, which is why it can be disabled in the configuration.
diff --git a/src/main/java/com/destroystokyo/paper/HopperPusher.java b/src/main/java/com/destroystokyo/paper/HopperPusher.java
new file mode 100644
index 000000000..52457e3d8
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/HopperPusher.java
@@ -0,0 +1,60 @@
+package com.destroystokyo.paper;
+
+import net.minecraft.server.AxisAlignedBB;
+import net.minecraft.server.BlockPosition;
+import net.minecraft.server.MCUtil;
+import net.minecraft.server.TileEntityHopper;
+import net.minecraft.server.World;
+
+public interface HopperPusher {
+
+ default TileEntityHopper findHopper() {
+ BlockPosition pos = new BlockPosition(getX(), getY(), getZ());
+ int startX = pos.getX() - 1;
+ int endX = pos.getX() + 1;
+ int startY = Math.max(0, pos.getY() - 1);
+ int endY = Math.min(255, pos.getY() + 1);
+ int startZ = pos.getZ() - 1;
+ int endZ = pos.getZ() + 1;
+ BlockPosition.PooledBlockPosition adjacentPos = BlockPosition.PooledBlockPosition.aquire();
+ for (int x = startX; x <= endX; x++) {
+ for (int y = startY; y <= endY; y++) {
+ for (int z = startZ; z <= endZ; z++) {
+ adjacentPos.setValues(x, y, z);
+ TileEntityHopper hopper = MCUtil.getHopper(getWorld(), adjacentPos);
+ if (hopper == null) continue; // Avoid playing with the bounding boxes, if at all possible
+ AxisAlignedBB hopperBoundingBox = hopper.getHopperLookupBoundingBox();
+ /*
+ * Check if the entity's bounding box intersects with the hopper's lookup box.
+ * This operation doesn't work both ways!
+ * Make sure you check if the entity's box intersects the hopper's box, not vice versa!
+ */
+ AxisAlignedBB boundingBox = this.getBoundingBox().shrink(0.1); // Imitate vanilla behavior
+ if (boundingBox.intersects(hopperBoundingBox)) {
+ return hopper;
+ }
+ }
+ }
+ }
+ adjacentPos.free();
+ return null;
+ }
+
+ boolean acceptItem(TileEntityHopper hopper);
+
+ default boolean tryPutInHopper() {
+ if (!getWorld().paperConfig.isHopperPushBased) return false;
+ TileEntityHopper hopper = findHopper();
+ return hopper != null && hopper.canAcceptItems() && acceptItem(hopper);
+ }
+
+ AxisAlignedBB getBoundingBox();
+
+ World getWorld();
+
+ double getX();
+
+ double getY();
+
+ double getZ();
+}
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 600e603bd..ce43e7bb7 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -339,4 +339,9 @@ public class PaperWorldConfig {
private void altFallingBlockOnGround() {
altFallingBlockOnGround = getBoolean("use-alternate-fallingblock-onGround-detection", false);
}
+
+ public boolean isHopperPushBased;
+ private void isHopperPushBased() {
+ isHopperPushBased = getBoolean("hopper.push-based", false);
+ }
}
diff --git a/src/main/java/net/minecraft/server/AxisAlignedBB.java b/src/main/java/net/minecraft/server/AxisAlignedBB.java
index 1eb9c2da8..c88b76a79 100644
--- a/src/main/java/net/minecraft/server/AxisAlignedBB.java
+++ b/src/main/java/net/minecraft/server/AxisAlignedBB.java
@@ -235,6 +235,7 @@ public class AxisAlignedBB {
}
}
+ public final boolean intersects(AxisAlignedBB intersecting) { return this.c(intersecting); } // Paper - OBFHELPER
public boolean c(AxisAlignedBB axisalignedbb) {
return this.a(axisalignedbb.a, axisalignedbb.b, axisalignedbb.c, axisalignedbb.d, axisalignedbb.e, axisalignedbb.f);
}
diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java
index 008ed206d..b3c1f550c 100644
--- a/src/main/java/net/minecraft/server/BlockPosition.java
+++ b/src/main/java/net/minecraft/server/BlockPosition.java
@@ -250,6 +250,7 @@ public class BlockPosition extends BaseBlockPosition {
super(i, j, k);
}
+ public static BlockPosition.PooledBlockPosition aquire() { return s(); } // Paper - OBFHELPER
public static BlockPosition.PooledBlockPosition s() {
return e(0, 0, 0);
}
@@ -276,6 +277,7 @@ public class BlockPosition extends BaseBlockPosition {
return new BlockPosition.PooledBlockPosition(i, j, k);
}
+ public void free() { t(); } // Paper - OBFHELPER
public void t() {
List list = BlockPosition.PooledBlockPosition.g;
@@ -393,6 +395,7 @@ public class BlockPosition extends BaseBlockPosition {
return this.d;
}
+ public void setValues(int x, int y, int z) { c(x, y, z); } // Paper - OBFHELPER
public BlockPosition.MutableBlockPosition c(int i, int j, int k) {
this.b = i;
this.c = j;
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index d46fb1d76..9ab892876 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -80,6 +80,19 @@ public abstract class Entity implements ICommandListener {
public double locX;
public double locY;
public double locZ;
+ // Paper start - getters to implement HopperPusher
+ public double getX() {
+ return locX;
+ }
+
+ public double getY() {
+ return locY;
+ }
+
+ public double getZ() {
+ return locZ;
+ }
+ // Paper end
public double motX;
public double motY;
public double motZ;
diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java
index 4d3aef96b..6593fc633 100644
--- a/src/main/java/net/minecraft/server/EntityItem.java
+++ b/src/main/java/net/minecraft/server/EntityItem.java
@@ -8,8 +8,15 @@ import org.apache.logging.log4j.Logger;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
// CraftBukkit end
+import com.destroystokyo.paper.HopperPusher; // Paper
-public class EntityItem extends Entity {
+// Paper start - implement HopperPusher
+public class EntityItem extends Entity implements HopperPusher {
+ @Override
+ public boolean acceptItem(TileEntityHopper hopper) {
+ return TileEntityHopper.putDropInInventory(null, hopper, this);
+ }
+// Paper end
private static final Logger b = LogManager.getLogger();
private static final DataWatcherObject<ItemStack> c = DataWatcher.a(EntityItem.class, DataWatcherRegistry.f);
@@ -59,6 +66,7 @@ public class EntityItem extends Entity {
this.die();
} else {
super.B_();
+ if (tryPutInHopper()) return; // Paper
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
@@ -146,6 +154,7 @@ public class EntityItem extends Entity {
// Spigot start - copied from above
@Override
public void inactiveTick() {
+ if (tryPutInHopper()) return; // Paper
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
diff --git a/src/main/java/net/minecraft/server/EntityMinecartContainer.java b/src/main/java/net/minecraft/server/EntityMinecartContainer.java
index 50d7d34b8..15f392d23 100644
--- a/src/main/java/net/minecraft/server/EntityMinecartContainer.java
+++ b/src/main/java/net/minecraft/server/EntityMinecartContainer.java
@@ -7,6 +7,7 @@ import javax.annotation.Nullable;
import java.util.List;
import org.bukkit.Location;
+import com.destroystokyo.paper.HopperPusher; // Paper
import com.destroystokyo.paper.loottable.CraftLootableInventoryData; // Paper
import com.destroystokyo.paper.loottable.CraftLootableInventory; // Paper
import com.destroystokyo.paper.loottable.LootableInventory; // Paper
@@ -15,7 +16,25 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
// CraftBukkit end
-public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory, ILootable, CraftLootableInventory { // Paper
+// Paper start - push into hoppers
+public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory, ILootable, CraftLootableInventory, HopperPusher { // Paper - CraftLootableInventory
+ @Override
+ public boolean acceptItem(TileEntityHopper hopper) {
+ return TileEntityHopper.acceptItem(hopper, this);
+ }
+
+ @Override
+ public void B_() {
+ super.B_();
+ tryPutInHopper();
+ }
+
+ @Override
+ public void inactiveTick() {
+ super.inactiveTick();
+ tryPutInHopper();
+ }
+ // Paper end
private NonNullList<ItemStack> items;
private boolean b;
diff --git a/src/main/java/net/minecraft/server/IHopper.java b/src/main/java/net/minecraft/server/IHopper.java
index 804215a1c..e830d8390 100644
--- a/src/main/java/net/minecraft/server/IHopper.java
+++ b/src/main/java/net/minecraft/server/IHopper.java
@@ -4,9 +4,9 @@ public interface IHopper extends IInventory {
World getWorld();
- double E();
+ double E(); default double getX() { return E(); } // Paper - OBFHELPER
- double F();
+ double F(); default double getY() { return F(); } // Paper - OBFHELPER
- double G();
+ double G(); default double getZ() { return G(); } // Paper - OBFHELPER
}
diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java
index 985513511..e9315f2d5 100644
--- a/src/main/java/net/minecraft/server/TileEntityHopper.java
+++ b/src/main/java/net/minecraft/server/TileEntityHopper.java
@@ -126,6 +126,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
}
private boolean o() {
+ mayAcceptItems = false; // Paper - at the beginning of a tick, assume we can't accept items
if (this.world != null && !this.world.isClientSide) {
if (!this.J() && BlockHopper.f(this.v())) {
boolean flag = false;
@@ -135,6 +136,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
}
if (!this.r()) {
+ mayAcceptItems = true; // Paper - flag this hopper to be able to accept items
flag = a((IHopper) this) || flag;
}
@@ -150,6 +152,14 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
}
}
+ // Paper start
+ private boolean mayAcceptItems = false;
+
+ public boolean canAcceptItems() {
+ return mayAcceptItems;
+ }
+ // Paper end
+
private boolean p() {
Iterator iterator = this.items.iterator();
@@ -300,8 +310,15 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
return true;
}
+ // Paper start - split methods, and only do entity lookup if in pull mode
public static boolean a(IHopper ihopper) {
- IInventory iinventory = b(ihopper);
+ IInventory iinventory = getInventory(ihopper, !(ihopper instanceof TileEntityHopper) || !ihopper.getWorld().paperConfig.isHopperPushBased);
+
+ return acceptItem(ihopper, iinventory);
+ }
+
+ public static boolean acceptItem(IHopper ihopper, IInventory iinventory) {
+ // Paper end
if (iinventory != null) {
EnumDirection enumdirection = EnumDirection.DOWN;
@@ -332,8 +349,8 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
}
}
}
- } else {
- Iterator iterator = a(ihopper.getWorld(), ihopper.E(), ihopper.F(), ihopper.G()).iterator();
+ } else if (!ihopper.getWorld().paperConfig.isHopperPushBased || !(ihopper instanceof TileEntityHopper)) { // Paper - only search for entities in 'pull mode'
+ Iterator iterator = a(ihopper.getWorld(), ihopper.E(), ihopper.F(), ihopper.G()).iterator(); // Change getHopperLookupBoundingBox() if this ever changes
while (iterator.hasNext()) {
EntityItem entityitem = (EntityItem) iterator.next();
@@ -397,6 +414,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
return false;
}
+ public static boolean putDropInInventory(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { return a(iinventory, iinventory1, entityitem); } // Paper - OBFHELPER
public static boolean a(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) {
boolean flag = false;
@@ -500,18 +518,44 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
private IInventory I() {
EnumDirection enumdirection = BlockHopper.b(this.v());
- return b(this.getWorld(), this.E() + (double) enumdirection.getAdjacentX(), this.F() + (double) enumdirection.getAdjacentY(), this.G() + (double) enumdirection.getAdjacentZ());
+ // Paper start - don't search for entities in push mode
+ World world = getWorld();
+ return getInventory(world, this.E() + (double) enumdirection.getAdjacentX(), this.F() + (double) enumdirection.getAdjacentY(), this.G() + (double) enumdirection.getAdjacentZ(), !world.paperConfig.isHopperPushBased);
+ // Paper end
}
- public static IInventory b(IHopper ihopper) {
- return b(ihopper.getWorld(), ihopper.E(), ihopper.F() + 1.0D, ihopper.G());
+ // Paper start - add option to search for entities
+ public static IInventory b(IHopper hopper) {
+ return getInventory(hopper, true);
+ }
+
+ public static IInventory getInventory(IHopper ihopper, boolean searchForEntities) {
+ return getInventory(ihopper.getWorld(), ihopper.E(), ihopper.F() + 1.0D, ihopper.G(), searchForEntities);
+ // Paper end
}
public static List<EntityItem> a(World world, double d0, double d1, double d2) {
- return world.a(EntityItem.class, new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D), IEntitySelector.a);
+ return world.a(EntityItem.class, new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D), IEntitySelector.a); // Change getHopperLookupBoundingBox(double, double, double) if the bounding box calculation is ever changed
+ }
+
+ // Paper start
+ public AxisAlignedBB getHopperLookupBoundingBox() {
+ return getHopperLookupBoundingBox(this.getX(), this.getY(), this.getZ());
}
+ private static AxisAlignedBB getHopperLookupBoundingBox(double d0, double d1, double d2) {
+ // Change this if a(World, double, double, double) above ever changes
+ return new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D);
+ }
+ // Paper end
+
+ // Paper start - add option to searchForEntities
public static IInventory b(World world, double d0, double d1, double d2) {
+ return getInventory(world, d0, d1, d2, true);
+ }
+
+ public static IInventory getInventory(World world, double d0, double d1, double d2, boolean searchForEntities) {
+ // Paper end
Object object = null;
int i = MathHelper.floor(d0);
int j = MathHelper.floor(d1);
@@ -531,7 +575,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
}
}
- if (object == null) {
+ if (object == null && searchForEntities) { // Paper - only if searchForEntities
List list = world.getEntities((Entity) null, new AxisAlignedBB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), IEntitySelector.c);
if (!list.isEmpty()) {
--
2.16.1