mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-30 06:24:06 +01:00
586 lines
32 KiB
Diff
586 lines
32 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Wed, 27 Apr 2016 22:09:52 -0400
|
|
Subject: [PATCH] Optimize Hoppers
|
|
|
|
* Removes unnecessary extra calls to .update() that are very expensive
|
|
* 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
|
|
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
|
|
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 80519ddf6302bf0aa8a186bd03aaa6e518e19adc..c277ccc012bd5011e31d746b08ace1ae5238c937 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1412,6 +1412,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
while (iterator.hasNext()) {
|
|
ServerLevel worldserver = (ServerLevel) iterator.next();
|
|
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
|
|
|
|
this.profiler.push(() -> {
|
|
return worldserver + " " + worldserver.dimension().location();
|
|
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
index 00c438de76577e7b869270df16915d1ade088c9f..79023dace09c99587b5100de29b5b0ed3ba3fc57 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -634,11 +634,12 @@ public final class ItemStack {
|
|
return this.getItem().interactLivingEntity(this, user, entity, hand);
|
|
}
|
|
|
|
- public ItemStack copy() {
|
|
- if (this.isEmpty()) {
|
|
+ public ItemStack copy() { return cloneItemStack(false); } // Paper
|
|
+ public ItemStack cloneItemStack(boolean origItem) { // Paper
|
|
+ if (!origItem && this.isEmpty()) { // Paper
|
|
return ItemStack.EMPTY;
|
|
} else {
|
|
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
|
|
+ ItemStack itemstack = new ItemStack(origItem ? this.item : this.getItem(), this.count); // Paper
|
|
|
|
itemstack.setPopTime(this.getPopTime());
|
|
if (this.tag != null) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
index 5768ff2c3e15c038d132c7ad391332fb36251871..e5e10c30fa9020e8dbbad708ef262eb6e1d559a6 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
|
|
@@ -26,6 +26,7 @@ import co.aikar.timings.MinecraftTimings; // Paper
|
|
import co.aikar.timings.Timing; // Paper
|
|
|
|
public abstract class BlockEntity {
|
|
+ static boolean ignoreTileUpdates; // Paper
|
|
|
|
public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper
|
|
// CraftBukkit start - data containers
|
|
@@ -162,6 +163,7 @@ public abstract class BlockEntity {
|
|
|
|
public void setChanged() {
|
|
if (this.level != null) {
|
|
+ if (ignoreTileUpdates) return; // Paper
|
|
BlockEntity.setChanged(this.level, this.worldPosition, this.blockState);
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java
|
|
index 5050a4dc1599c10470d65eb43d412d8926f2027f..528832949172047d34f234d876fa989288916fed 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java
|
|
@@ -146,7 +146,7 @@ public class ChiseledBookShelfBlockEntity extends BlockEntity implements Contain
|
|
|
|
@Override
|
|
public void setItem(int slot, ItemStack stack) {
|
|
- if (stack.is(ItemTags.BOOKSHELF_BOOKS)) {
|
|
+ if (stack.isEmpty() || stack.is(ItemTags.BOOKSHELF_BOOKS)) { // Paper
|
|
this.items.set(slot, stack);
|
|
this.updateState(slot);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
index a507d7f65a94e49ecd18cd18797b156474558390..eedc9fa0bcd30af3b229d74cfdfeffed4b60f221 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
@@ -190,6 +190,162 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
|
|
return false;
|
|
}
|
|
+ // Paper start - Optimize Hoppers
|
|
+ private static boolean skipPullModeEventFire;
|
|
+ private static boolean skipPushModeEventFire;
|
|
+ public static boolean skipHopperEvents;
|
|
+
|
|
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
|
|
+ skipPushModeEventFire = skipHopperEvents;
|
|
+ boolean foundItem = false;
|
|
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
|
|
+ final ItemStack item = hopper.getItem(i);
|
|
+ if (!item.isEmpty()) {
|
|
+ foundItem = true;
|
|
+ ItemStack origItemStack = item;
|
|
+ ItemStack movedItem = origItemStack;
|
|
+
|
|
+ final int originalItemCount = origItemStack.getCount();
|
|
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
|
+ origItemStack.setCount(movedItemCount);
|
|
+
|
|
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
|
|
+ // Because nothing uses getItem, every event call should end up the same result.
|
|
+ if (!skipPushModeEventFire) {
|
|
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
|
|
+ if (movedItem == null) { // cancelled
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
|
|
+ final int remainingItemCount = remainingItem.getCount();
|
|
+ if (remainingItemCount != movedItemCount) {
|
|
+ origItemStack = origItemStack.cloneItemStack(true);
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+ if (!origItemStack.isEmpty()) {
|
|
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
|
+ }
|
|
+ hopper.setItem(i, origItemStack);
|
|
+ destination.setChanged();
|
|
+ return true;
|
|
+ }
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+ }
|
|
+ }
|
|
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
|
|
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) {
|
|
+ ItemStack movedItem = origItemStack;
|
|
+ final int originalItemCount = origItemStack.getCount();
|
|
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
|
|
+ movedItem.setCount(movedItemCount);
|
|
+
|
|
+ if (!skipPullModeEventFire) {
|
|
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
|
|
+ if (movedItem == null) { // cancelled
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+ // Drastically improve performance by returning true.
|
|
+ // No plugin could of relied on the behavior of false as the other call
|
|
+ // site for IMIE did not exhibit the same behavior
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
|
|
+ final int remainingItemCount = remainingItem.getCount();
|
|
+ if (remainingItemCount != movedItemCount) {
|
|
+ origItemStack = origItemStack.cloneItemStack(true);
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+ if (!origItemStack.isEmpty()) {
|
|
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
|
|
+ }
|
|
+
|
|
+ ignoreTileUpdates = true;
|
|
+ container.setItem(i, origItemStack);
|
|
+ ignoreTileUpdates = false;
|
|
+ container.setChanged();
|
|
+ return true;
|
|
+ }
|
|
+ origItemStack.setCount(originalItemCount);
|
|
+
|
|
+ if (level.paperConfig().hopper.cooldownWhenFull) {
|
|
+ cooldownHopper(hopper);
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) {
|
|
+ final Inventory destinationInventory = getInventory(iinventory);
|
|
+ final InventoryMoveItemEvent event = new InventoryMoveItemEvent(hopper.getOwner(false).getInventory(),
|
|
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
|
|
+ final boolean result = event.callEvent();
|
|
+ if (!event.calledGetItem && !event.calledSetItem) {
|
|
+ skipPushModeEventFire = true;
|
|
+ }
|
|
+ if (!result) {
|
|
+ cooldownHopper(hopper);
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if (event.calledSetItem) {
|
|
+ return CraftItemStack.asNMSCopy(event.getItem());
|
|
+ } else {
|
|
+ return itemstack;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Nullable
|
|
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
|
|
+ final Inventory sourceInventory = getInventory(container);
|
|
+ final Inventory destination = getInventory(hopper);
|
|
+
|
|
+ // Mirror is safe as no plugins ever use this item
|
|
+ final InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, CraftItemStack.asCraftMirror(itemstack), destination, false);
|
|
+ final boolean result = event.callEvent();
|
|
+ if (!event.calledGetItem && !event.calledSetItem) {
|
|
+ skipPullModeEventFire = true;
|
|
+ }
|
|
+ if (!result) {
|
|
+ cooldownHopper(hopper);
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ if (event.calledSetItem) {
|
|
+ return CraftItemStack.asNMSCopy(event.getItem());
|
|
+ } else {
|
|
+ return itemstack;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static Inventory getInventory(final Container container) {
|
|
+ final Inventory sourceInventory;
|
|
+ if (container instanceof CompoundContainer compoundContainer) {
|
|
+ // Have to special-case large chests as they work oddly
|
|
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer);
|
|
+ } else if (container instanceof BlockEntity blockEntity) {
|
|
+ sourceInventory = blockEntity.getOwner(false).getInventory();
|
|
+ } else {
|
|
+ sourceInventory = container.getOwner().getInventory();
|
|
+ }
|
|
+ return sourceInventory;
|
|
+ }
|
|
+
|
|
+ private static void cooldownHopper(final Hopper hopper) {
|
|
+ if (hopper instanceof HopperBlockEntity blockEntity) {
|
|
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
|
|
+ } else if (hopper instanceof MinecartHopper blockEntity) {
|
|
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer / 2);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
|
|
private static boolean ejectItems(Level world, BlockPos blockposition, BlockState iblockdata, Container iinventory, HopperBlockEntity hopper) { // CraftBukkit
|
|
Container iinventory1 = HopperBlockEntity.getAttachedContainer(world, blockposition, iblockdata);
|
|
@@ -202,44 +358,47 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
if (HopperBlockEntity.isFullContainer(iinventory1, enumdirection)) {
|
|
return false;
|
|
} else {
|
|
- for (int i = 0; i < iinventory.getContainerSize(); ++i) {
|
|
- if (!iinventory.getItem(i).isEmpty()) {
|
|
- ItemStack itemstack = iinventory.getItem(i).copy();
|
|
- // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
|
|
-
|
|
- // CraftBukkit start - Call event when pushing items into other inventories
|
|
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
|
-
|
|
- Inventory destinationInventory;
|
|
- // Have to special case large chests as they work oddly
|
|
- if (iinventory1 instanceof CompoundContainer) {
|
|
- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
|
|
- } else {
|
|
- destinationInventory = iinventory1.getOwner().getInventory();
|
|
- }
|
|
-
|
|
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
|
|
- world.getCraftServer().getPluginManager().callEvent(event);
|
|
- if (event.isCancelled()) {
|
|
- hopper.setItem(i, itemstack);
|
|
- hopper.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
|
- return false;
|
|
- }
|
|
- int origCount = event.getItem().getAmount(); // Spigot
|
|
- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
|
|
+ // Paper start - replace logic; MAKE SURE TO CHECK FOR DIFFS ON UPDATES
|
|
+ return hopperPush(world, iinventory1, enumdirection, hopper);
|
|
+ //for (int i = 0; i < iinventory.getContainerSize(); ++i) {
|
|
+ // if (!iinventory.getItem(i).isEmpty()) {
|
|
+ // ItemStack itemstack = iinventory.getItem(i).copy();
|
|
+ // // ItemStack itemstack1 = addItem(iinventory, iinventory1, iinventory.removeItem(i, 1), enumdirection);
|
|
+
|
|
+ // // CraftBukkit start - Call event when pushing items into other inventories
|
|
+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
|
+
|
|
+ // Inventory destinationInventory;
|
|
+ // // Have to special case large chests as they work oddly
|
|
+ // if (iinventory1 instanceof CompoundContainer) {
|
|
+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory1);
|
|
+ // } else {
|
|
+ // destinationInventory = iinventory1.getOwner().getInventory();
|
|
+ // }
|
|
+
|
|
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(iinventory.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
|
|
+ // world.getCraftServer().getPluginManager().callEvent(event);
|
|
+ // if (event.isCancelled()) {
|
|
+ // hopper.setItem(i, itemstack);
|
|
+ // hopper.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
|
+ // return false;
|
|
+ // }
|
|
+ // int origCount = event.getItem().getAmount(); // Spigot
|
|
+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
|
|
// CraftBukkit end
|
|
|
|
- if (itemstack1.isEmpty()) {
|
|
- iinventory1.setChanged();
|
|
- return true;
|
|
- }
|
|
+ // if (itemstack1.isEmpty()) {
|
|
+ // iinventory1.setChanged();
|
|
+ // return true;
|
|
+ // }
|
|
|
|
- itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
|
|
- iinventory.setItem(i, itemstack);
|
|
- }
|
|
- }
|
|
+ // itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
|
|
+ // iinventory.setItem(i, itemstack);
|
|
+ // }
|
|
+ //}
|
|
|
|
- return false;
|
|
+ //return false;
|
|
+ // Paper end
|
|
}
|
|
}
|
|
}
|
|
@@ -249,18 +408,51 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
|
|
private static boolean isFullContainer(Container inventory, Direction direction) {
|
|
- return HopperBlockEntity.getSlots(inventory, direction).allMatch((i) -> {
|
|
- ItemStack itemstack = inventory.getItem(i);
|
|
-
|
|
- return itemstack.getCount() >= itemstack.getMaxStackSize();
|
|
- });
|
|
+ return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams
|
|
}
|
|
|
|
private static boolean isEmptyContainer(Container inv, Direction facing) {
|
|
- return HopperBlockEntity.getSlots(inv, facing).allMatch((i) -> {
|
|
- return inv.getItem(i).isEmpty();
|
|
- });
|
|
+ // Paper start
|
|
+ return allMatch(inv, facing, IS_EMPTY_TEST);
|
|
+ }
|
|
+ private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
|
+ if (iinventory instanceof WorldlyContainer) {
|
|
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
|
|
+ if (!test.test(iinventory.getItem(i), i)) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ int size = iinventory.getContainerSize();
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ if (!test.test(iinventory.getItem(i), i)) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ private static boolean anyMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
|
|
+ if (iinventory instanceof WorldlyContainer) {
|
|
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
|
|
+ if (test.test(iinventory.getItem(i), i)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ int size = iinventory.getContainerSize();
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ if (test.test(iinventory.getItem(i), i)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
}
|
|
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize();
|
|
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
|
|
+ // Paper end
|
|
|
|
public static boolean suckInItems(Level world, Hopper hopper) {
|
|
Container iinventory = HopperBlockEntity.getSourceContainer(world, hopper);
|
|
@@ -268,8 +460,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
if (iinventory != null) {
|
|
Direction enumdirection = Direction.DOWN;
|
|
|
|
- return HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) ? false : HopperBlockEntity.getSlots(iinventory, enumdirection).anyMatch((i) -> {
|
|
- return HopperBlockEntity.a(hopper, iinventory, i, enumdirection, world); // Spigot
|
|
+ // Paper start - optimize hoppers and remove streams
|
|
+ skipPullModeEventFire = skipHopperEvents;
|
|
+ return !HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) && anyMatch(iinventory, enumdirection, (item, i) -> {
|
|
+ // Logic copied from below to avoid extra getItem calls
|
|
+ if (!item.isEmpty() && canTakeItemFromContainer(iinventory, item, i, enumdirection)) {
|
|
+ return hopperPull(world, hopper, iinventory, item, i);
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ // Paper end
|
|
});
|
|
} else {
|
|
Iterator iterator = HopperBlockEntity.getItemsAtAndAbove(world, hopper).iterator();
|
|
@@ -288,47 +488,51 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
}
|
|
|
|
+ // Paper - method unused as logic is inlined above
|
|
private static boolean a(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
|
|
ItemStack itemstack = iinventory.getItem(i);
|
|
|
|
- if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(iinventory, itemstack, i, enumdirection)) {
|
|
- ItemStack itemstack1 = itemstack.copy();
|
|
- // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
|
|
- // CraftBukkit start - Call event on collection of items from inventories into the hopper
|
|
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
|
-
|
|
- Inventory sourceInventory;
|
|
- // Have to special case large chests as they work oddly
|
|
- if (iinventory instanceof CompoundContainer) {
|
|
- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
|
- } else {
|
|
- sourceInventory = iinventory.getOwner().getInventory();
|
|
- }
|
|
-
|
|
- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
|
|
-
|
|
- Bukkit.getServer().getPluginManager().callEvent(event);
|
|
- if (event.isCancelled()) {
|
|
- iinventory.setItem(i, itemstack1);
|
|
-
|
|
- if (ihopper instanceof HopperBlockEntity) {
|
|
- ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
|
- } else if (ihopper instanceof MinecartHopper) {
|
|
- ((MinecartHopper) ihopper).setCooldown(world.spigotConfig.hopperTransfer / 2); // Spigot
|
|
- }
|
|
- return false;
|
|
- }
|
|
- int origCount = event.getItem().getAmount(); // Spigot
|
|
- ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
|
|
- // CraftBukkit end
|
|
-
|
|
- if (itemstack2.isEmpty()) {
|
|
- iinventory.setChanged();
|
|
- return true;
|
|
- }
|
|
-
|
|
- itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
|
|
- iinventory.setItem(i, itemstack1);
|
|
+ if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left inused incase reflective plugins
|
|
+ // Paper start - replace pull logic; MAKE SURE TO CHECK FOR DIFFS WHEN UPDATING
|
|
+ return hopperPull(world, ihopper, iinventory, itemstack, i);
|
|
+ //ItemStack itemstack1 = itemstack.copy();
|
|
+ //// ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.removeItem(i, 1), (EnumDirection) null);
|
|
+ //// CraftBukkit start - Call event on collection of items from inventories into the hopper
|
|
+ //CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
|
|
+
|
|
+ //Inventory sourceInventory;
|
|
+ //// Have to special case large chests as they work oddly
|
|
+ //if (iinventory instanceof CompoundContainer) {
|
|
+ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
|
|
+ //} else {
|
|
+ // sourceInventory = iinventory.getOwner().getInventory();
|
|
+ //}
|
|
+
|
|
+ //InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
|
|
+
|
|
+ //Bukkit.getServer().getPluginManager().callEvent(event);
|
|
+ //if (event.isCancelled()) {
|
|
+ // iinventory.setItem(i, itemstack1);
|
|
+
|
|
+ // if (ihopper instanceof HopperBlockEntity) {
|
|
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
|
|
+ // } else if (ihopper instanceof MinecartHopper) {
|
|
+ // ((MinecartHopper) ihopper).setCooldown(world.spigotConfig.hopperTransfer / 2); // Spigot
|
|
+ // }
|
|
+ // return false;
|
|
+ //}
|
|
+ //int origCount = event.getItem().getAmount(); // Spigot
|
|
+ //ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
|
|
+ //// CraftBukkit end
|
|
+
|
|
+ //if (itemstack2.isEmpty()) {
|
|
+ // iinventory.setChanged();
|
|
+ // return true;
|
|
+ //}
|
|
+
|
|
+ //itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
|
|
+ //iinventory.setItem(i, itemstack1);
|
|
+ // Paper end
|
|
}
|
|
|
|
return false;
|
|
@@ -337,7 +541,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
public static boolean addItem(Container inventory, ItemEntity itemEntity) {
|
|
boolean flag = false;
|
|
// CraftBukkit start
|
|
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
|
|
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation
|
|
itemEntity.level.getCraftServer().getPluginManager().callEvent(event);
|
|
if (event.isCancelled()) {
|
|
return false;
|
|
@@ -396,7 +600,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
stack = stack.split(to.getMaxStackSize());
|
|
}
|
|
// Spigot end
|
|
+ ignoreTileUpdates = true; // Paper
|
|
to.setItem(slot, stack);
|
|
+ ignoreTileUpdates = false; // Paper
|
|
stack = ItemStack.EMPTY;
|
|
flag = true;
|
|
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
|
|
@@ -447,18 +653,28 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
|
|
public static List<ItemEntity> getItemsAtAndAbove(Level world, Hopper hopper) {
|
|
- return (List) hopper.getSuckShape().toAabbs().stream().flatMap((axisalignedbb) -> {
|
|
- return world.getEntitiesOfClass(ItemEntity.class, axisalignedbb.move(hopper.getLevelX() - 0.5D, hopper.getLevelY() - 0.5D, hopper.getLevelZ() - 0.5D), EntitySelector.ENTITY_STILL_ALIVE).stream();
|
|
- }).collect(Collectors.toList());
|
|
+ // Paper start - Optimize item suck in. remove streams, restore 1.12 checks. Seriously checking the bowl?!
|
|
+ double d0 = hopper.getLevelX();
|
|
+ double d1 = hopper.getLevelY();
|
|
+ double d2 = hopper.getLevelZ();
|
|
+ AABB bb = new AABB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D);
|
|
+ return world.getEntitiesOfClass(ItemEntity.class, bb, Entity::isAlive);
|
|
+ // Paper end
|
|
}
|
|
|
|
@Nullable
|
|
public static Container getContainerAt(Level world, BlockPos pos) {
|
|
- return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
|
|
+ return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true); // Paper
|
|
}
|
|
|
|
@Nullable
|
|
- private static Container getContainerAt(Level world, double x, double y, double z) {
|
|
+ public static Container getContainerAt(Level world, double x, double y, double z) {
|
|
+ // Paper start - add optimizeEntities parameter
|
|
+ return getContainerAt(world, x, y, z, false);
|
|
+ }
|
|
+ @Nullable
|
|
+ private static Container getContainerAt(Level world, double x, double y, double z, final boolean optimizeEntities) {
|
|
+ // Paper end - add optimizeEntities parameter
|
|
Object object = null;
|
|
BlockPos blockposition = new BlockPos(x, y, z);
|
|
if ( !world.spigotConfig.hopperCanLoadChunks && !world.hasChunkAt( blockposition ) ) return null; // Spigot
|
|
@@ -478,7 +694,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
}
|
|
|
|
- if (object == null) {
|
|
+ if (object == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding())) { // Paper
|
|
List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
|
|
|
|
if (!list.isEmpty()) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
index e3bee2df77d87630e96621470e940d9d9e152e7f..d559f93a9a09bac414dd5d58afccad42c127f09b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
|
@@ -95,12 +95,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
|
|
@Override
|
|
public boolean isEmpty() {
|
|
this.unpackLootTable((Player)null);
|
|
- return this.getItems().stream().allMatch(ItemStack::isEmpty);
|
|
+ // Paper start
|
|
+ for (ItemStack itemStack : this.getItems()) {
|
|
+ if (!itemStack.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+ return true;
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getItem(int slot) {
|
|
- this.unpackLootTable((Player)null);
|
|
+ if (slot == 0) this.unpackLootTable((Player) null); // Paper
|
|
return this.getItems().get(slot);
|
|
}
|
|
|