Paper/Spigot-Server-Patches/0140-Make-entities-look-for-hoppers.patch
Zach Brown f28f199099
Make push based hoppers imitate vanilla bounds
This is vanilla behavior https://i.imgur.com/zMIpRcQ.png
So we have to allow that sort of thing.

Fixes GH-993
2018-01-26 21:28:34 -05:00

384 lines
17 KiB
Diff

From 4f6df6a265dcf623e4c26b538d1e043369e8714f 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 670d3d9a9..71cdb8118 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -333,4 +333,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 53584b257..8ad081316 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();
@@ -302,8 +312,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;
@@ -334,8 +351,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();
@@ -401,6 +418,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;
@@ -506,18 +524,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);
@@ -537,7 +581,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.14.3