Paper/patches/server/0941-Optimize-Hoppers.patch

609 lines
32 KiB
Diff
Raw Normal View History

2021-06-11 14:02:28 +02:00
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 365bcfcf667ffa76253b1db5cd06a96e0f3679d5..ae9b78c4e321eb811ee87e1827d3684371977609 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1522,6 +1522,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2021-06-11 14:02:28 +02:00
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
2023-03-18 20:03:42 +01:00
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
2021-06-11 14:02:28 +02:00
this.profiler.push(() -> {
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
Updated Upstream (Bukkit/CraftBukkit) (#9485) Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 82af5dc6 SPIGOT-7396: Add PlayerSignOpenEvent 3f0281ca SPIGOT-7063, PR-763: Add DragonBattle#initiateRespawn with custom EnderCrystals f83c8df4 PR-873: Add PlayerRecipeBookClickEvent 14560d39 SPIGOT-7435: Add TeleportCause#EXIT_BED 2cc6db92 SPIGOT-7422, PR-887: Add API to set sherds on decorated pots 36022f02 PR-883: Add ItemFactory#getSpawnEgg 12eb5c46 PR-881: Update Scoreboard Javadocs, remove explicit exception throwing f6d8d44a PR-882: Add modern time API methods to ban API 21a7b710 Upgrade some Maven plugins to reduce warnings 11fd1225 PR-886: Deprecate the SmithingRecipe constructor as it now does nothing dbd1761d SPIGOT-7406: Improve documentation for getDragonBattle CraftBukkit Changes: d548daac2 SPIGOT-7446: BlockState#update not updating a spawner's type to null 70e0bc050 SPIGOT-7447: Fix --forceUpgrade 6752f1d63 SPIGOT-7396: Add PlayerSignOpenEvent 847b4cad5 SPIGOT-7063, PR-1071: Add DragonBattle#initiateRespawn with custom EnderCrystals c335a555f PR-1212: Add PlayerRecipeBookClickEvent 4be756ecb SPIGOT-7445: Fix opening smithing inventory db70bd6ed SPIGOT-7441: Fix issue placing certain items in creative/op f7fa6d993 SPIGOT-7435: Add TeleportCause#EXIT_BED b435e8e8d SPIGOT-7349: Player#setDisplayName not working when message/format unmodified a2fafdd1d PR-1232: Re-add fix for player rotation 7cf863de1 PR-1233: Remove some old MC bug fixes now fixed in vanilla 08ec344ad Fix ChunkGenerator#generateCaves never being called 5daeb502a SPIGOT-7422, PR-1228: Add API to set sherds on decorated pots 52faa6b32 PR-1224: Add ItemFactory#getSpawnEgg 01cae71b7 SPIGOT-7429: Fix LEFT_CLICK_AIR not working for passable entities and spectators a94277a18 PR-1223: Remove non-existent scoreboard display name/prefix/suffix limits 36b107660 PR-1225: Add modern time API methods to ban API 59ead25bc Upgrade some Maven plugins to reduce warnings 202fc5c4e Increase outdated build delay ce545de57 SPIGOT-7398: TextDisplay#setInterpolationDuration incorrectly updates the line width Spigot Changes: b41c46db Rebuild patches 3374045a SPIGOT-7431: Fix EntityMountEvent returning opposite entities 0ca4eb66 Rebuild patches
2023-08-06 02:21:59 +02:00
index cc58df88fc3788dcfb7e429ef899b3d558a931cc..b3c99bb1da54209a21b090d31713167fd9729654 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
Updated Upstream (Bukkit/CraftBukkit) (#9485) Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 82af5dc6 SPIGOT-7396: Add PlayerSignOpenEvent 3f0281ca SPIGOT-7063, PR-763: Add DragonBattle#initiateRespawn with custom EnderCrystals f83c8df4 PR-873: Add PlayerRecipeBookClickEvent 14560d39 SPIGOT-7435: Add TeleportCause#EXIT_BED 2cc6db92 SPIGOT-7422, PR-887: Add API to set sherds on decorated pots 36022f02 PR-883: Add ItemFactory#getSpawnEgg 12eb5c46 PR-881: Update Scoreboard Javadocs, remove explicit exception throwing f6d8d44a PR-882: Add modern time API methods to ban API 21a7b710 Upgrade some Maven plugins to reduce warnings 11fd1225 PR-886: Deprecate the SmithingRecipe constructor as it now does nothing dbd1761d SPIGOT-7406: Improve documentation for getDragonBattle CraftBukkit Changes: d548daac2 SPIGOT-7446: BlockState#update not updating a spawner's type to null 70e0bc050 SPIGOT-7447: Fix --forceUpgrade 6752f1d63 SPIGOT-7396: Add PlayerSignOpenEvent 847b4cad5 SPIGOT-7063, PR-1071: Add DragonBattle#initiateRespawn with custom EnderCrystals c335a555f PR-1212: Add PlayerRecipeBookClickEvent 4be756ecb SPIGOT-7445: Fix opening smithing inventory db70bd6ed SPIGOT-7441: Fix issue placing certain items in creative/op f7fa6d993 SPIGOT-7435: Add TeleportCause#EXIT_BED b435e8e8d SPIGOT-7349: Player#setDisplayName not working when message/format unmodified a2fafdd1d PR-1232: Re-add fix for player rotation 7cf863de1 PR-1233: Remove some old MC bug fixes now fixed in vanilla 08ec344ad Fix ChunkGenerator#generateCaves never being called 5daeb502a SPIGOT-7422, PR-1228: Add API to set sherds on decorated pots 52faa6b32 PR-1224: Add ItemFactory#getSpawnEgg 01cae71b7 SPIGOT-7429: Fix LEFT_CLICK_AIR not working for passable entities and spectators a94277a18 PR-1223: Remove non-existent scoreboard display name/prefix/suffix limits 36b107660 PR-1225: Add modern time API methods to ban API 59ead25bc Upgrade some Maven plugins to reduce warnings 202fc5c4e Increase outdated build delay ce545de57 SPIGOT-7398: TextDisplay#setInterpolationDuration incorrectly updates the line width Spigot Changes: b41c46db Rebuild patches 3374045a SPIGOT-7431: Fix EntityMountEvent returning opposite entities 0ca4eb66 Rebuild patches
2023-08-06 02:21:59 +02:00
@@ -723,10 +723,16 @@ public final class ItemStack {
2021-06-11 14:02:28 +02:00
}
2023-03-18 20:03:42 +01:00
public ItemStack copy() {
2021-06-11 14:02:28 +02:00
- if (this.isEmpty()) {
2023-03-18 20:03:42 +01:00
+ // Paper start
+ return this.copy(false);
+ }
+
+ public ItemStack copy(boolean originalItem) {
+ if (!originalItem && this.isEmpty()) {
+ // Paper end
2021-06-11 14:02:28 +02:00
return ItemStack.EMPTY;
} else {
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
2023-03-18 20:03:42 +01:00
+ ItemStack itemstack = new ItemStack(originalItem ? this.item : this.getItem(), this.count); // Paper
2021-06-11 14:02:28 +02:00
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
2023-06-08 10:47:19 +02:00
index 5bdad1866386908b9fef74d15862eb107fabe68f..370a25d2deb54f10a35ee24d9e7e92fbfde60edf 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
2022-03-30 22:28:38 +02:00
@@ -26,6 +26,7 @@ import co.aikar.timings.MinecraftTimings; // Paper
import co.aikar.timings.Timing; // Paper
public abstract class BlockEntity {
2023-02-23 17:37:56 +01:00
+ static boolean ignoreTileUpdates; // Paper
2021-06-11 14:02:28 +02:00
2022-03-30 22:28:38 +02:00
public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper
// CraftBukkit start - data containers
2023-06-08 10:47:19 +02:00
@@ -161,6 +162,7 @@ public abstract class BlockEntity {
2021-06-11 14:02:28 +02:00
public void setChanged() {
if (this.level != null) {
2023-02-23 17:37:56 +01:00
+ if (ignoreTileUpdates) return; // Paper
2021-06-13 21:29:58 +02:00
BlockEntity.setChanged(this.level, this.worldPosition, this.blockState);
}
2021-06-11 14:02:28 +02:00
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 6907e647ef4d3f5c9c46edb4cf0905844dd1cea9..b11f51762ca289d99eaa49e66e31e58595bcea4e 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -193,6 +193,202 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
return false;
}
2023-03-18 20:03:42 +01:00
2021-06-11 14:02:28 +02:00
+ // Paper start - Optimize Hoppers
2023-02-23 17:37:56 +01:00
+ private static boolean skipPullModeEventFire;
+ private static boolean skipPushModeEventFire;
+ public static boolean skipHopperEvents;
2021-06-11 14:02:28 +02:00
+
2023-02-23 17:37:56 +01:00
+ private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) {
2021-06-11 14:02:28 +02:00
+ skipPushModeEventFire = skipHopperEvents;
+ boolean foundItem = false;
2021-06-14 12:18:42 +02:00
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
2023-02-23 17:37:56 +01:00
+ final ItemStack item = hopper.getItem(i);
2021-06-11 14:02:28 +02:00
+ if (!item.isEmpty()) {
+ foundItem = true;
+ ItemStack origItemStack = item;
2023-02-23 17:37:56 +01:00
+ ItemStack movedItem = origItemStack;
2021-06-11 14:02:28 +02:00
+
2023-02-23 17:37:56 +01:00
+ final int originalItemCount = origItemStack.getCount();
+ final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount);
+ origItemStack.setCount(movedItemCount);
2021-06-11 14:02:28 +02:00
+
+ // 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) {
2023-02-23 17:37:56 +01:00
+ movedItem = callPushMoveEvent(destination, movedItem, hopper);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+ return false;
+ }
+ }
2023-02-23 17:37:56 +01:00
+
+ final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
2023-03-18 20:03:42 +01:00
+ origItemStack = origItemStack.copy(true);
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+ if (!origItemStack.isEmpty()) {
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
2021-06-11 14:02:28 +02:00
+ }
2021-06-13 21:29:58 +02:00
+ hopper.setItem(i, origItemStack);
2021-06-14 12:18:42 +02:00
+ destination.setChanged();
2021-06-11 14:02:28 +02:00
+ return true;
+ }
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+ }
+ }
+ if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown
2021-06-13 21:29:58 +02:00
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
2021-06-11 14:02:28 +02:00
+ }
+ return false;
+ }
+
2023-02-23 17:37:56 +01:00
+ 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);
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
2023-02-23 17:37:56 +01:00
+ movedItem.setCount(movedItemCount);
2021-06-11 14:02:28 +02:00
+
+ if (!skipPullModeEventFire) {
2023-02-23 17:37:56 +01:00
+ movedItem = callPullMoveEvent(hopper, container, movedItem);
+ if (movedItem == null) { // cancelled
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+ // 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;
+ }
+ }
+
2023-02-23 17:37:56 +01:00
+ final ItemStack remainingItem = addItem(container, hopper, movedItem, null);
+ final int remainingItemCount = remainingItem.getCount();
+ if (remainingItemCount != movedItemCount) {
2023-03-18 20:03:42 +01:00
+ origItemStack = origItemStack.copy(true);
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+ if (!origItemStack.isEmpty()) {
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount);
2021-06-11 14:02:28 +02:00
+ }
2023-02-23 17:37:56 +01:00
+
+ ignoreTileUpdates = true;
+ container.setItem(i, origItemStack);
+ ignoreTileUpdates = false;
+ container.setChanged();
2021-06-11 14:02:28 +02:00
+ return true;
+ }
2023-02-23 17:37:56 +01:00
+ origItemStack.setCount(originalItemCount);
2021-06-11 14:02:28 +02:00
+
+ if (level.paperConfig().hopper.cooldownWhenFull) {
2023-02-23 17:37:56 +01:00
+ cooldownHopper(hopper);
2021-06-11 14:02:28 +02:00
+ }
+
+ return false;
+ }
+
2023-02-23 17:37:56 +01:00
+ @Nullable
2021-06-13 21:29:58 +02:00
+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) {
2023-02-23 17:37:56 +01:00
+ final Inventory destinationInventory = getInventory(iinventory);
+ final InventoryMoveItemEvent event = new InventoryMoveItemEvent(hopper.getOwner(false).getInventory(),
2021-06-11 14:02:28 +02:00
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
2023-02-23 17:37:56 +01:00
+ final boolean result = event.callEvent();
2021-06-11 14:02:28 +02:00
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPushModeEventFire = true;
+ }
+ if (!result) {
2021-06-13 21:29:58 +02:00
+ cooldownHopper(hopper);
2021-06-11 14:02:28 +02:00
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
2023-02-23 17:37:56 +01:00
+ @Nullable
+ private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) {
+ final Inventory sourceInventory = getInventory(container);
+ final Inventory destination = getInventory(hopper);
2021-06-11 14:02:28 +02:00
+
2023-02-23 17:37:56 +01:00
+ // 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();
2021-06-11 14:02:28 +02:00
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPullModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
2023-02-23 17:37:56 +01:00
+ 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();
2023-03-18 20:03:42 +01:00
+ } else if (container.getOwner() != null) {
2023-02-23 17:37:56 +01:00
+ sourceInventory = container.getOwner().getInventory();
2023-03-18 20:03:42 +01:00
+ } else {
+ sourceInventory = new CraftInventory(container);
2021-06-11 14:02:28 +02:00
+ }
+ return sourceInventory;
+ }
+
2023-02-23 17:37:56 +01:00
+ private static void cooldownHopper(final Hopper hopper) {
2023-03-18 20:03:42 +01:00
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
2021-06-11 14:02:28 +02:00
+ }
+ }
2023-03-18 20:03:42 +01:00
+
+ 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();
2021-06-11 14:02:28 +02:00
+ // Paper end
2023-03-18 20:03:42 +01:00
+
2021-11-24 06:44:21 +01:00
private static boolean ejectItems(Level world, BlockPos blockposition, BlockState iblockdata, Container iinventory, HopperBlockEntity hopper) { // CraftBukkit
2021-06-13 21:29:58 +02:00
Container iinventory1 = HopperBlockEntity.getAttachedContainer(world, blockposition, iblockdata);
2023-03-18 20:03:42 +01:00
@@ -204,46 +400,49 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-13 21:29:58 +02:00
if (HopperBlockEntity.isFullContainer(iinventory1, enumdirection)) {
2021-06-11 14:02:28 +02:00
return false;
} else {
2023-02-23 17:37:56 +01:00
- 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);
2023-03-23 22:57:03 +01:00
-
2023-02-23 17:37:56 +01:00
- // CraftBukkit start - Call event when pushing items into other inventories
- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot
2023-03-23 22:57:03 +01:00
-
2023-02-23 17:37:56 +01:00
- 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);
2023-03-18 20:03:42 +01:00
- } else if (iinventory1.getOwner() != null) {
2023-02-23 17:37:56 +01:00
- destinationInventory = iinventory1.getOwner().getInventory();
2023-03-18 20:03:42 +01:00
- } else {
- destinationInventory = new CraftInventory(iinventory);
2023-02-23 17:37:56 +01:00
- }
2023-03-23 22:57:03 +01:00
-
- 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);
2023-03-23 22:57:03 +01:00
+ // 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 if (iinventory1.getOwner() != null) {
+ // destinationInventory = iinventory1.getOwner().getInventory();
+ // } else {
+ // destinationInventory = new CraftInventory(iinventory);
+ // }
+
2023-03-18 20:03:42 +01:00
+ // 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);
2023-02-23 17:37:56 +01:00
// CraftBukkit end
- if (itemstack1.isEmpty()) {
- iinventory1.setChanged();
- return true;
- }
2023-03-18 20:03:42 +01:00
+ // if (itemstack1.isEmpty()) {
+ // iinventory1.setChanged();
+ // return true;
+ // }
2023-02-23 17:37:56 +01:00
- itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
- iinventory.setItem(i, itemstack);
- }
- }
2023-03-18 20:03:42 +01:00
+ // itemstack.shrink(origCount - itemstack1.getCount()); // Spigot
+ // iinventory.setItem(i, itemstack);
+ // }
+ // }
2021-06-11 14:02:28 +02:00
- return false;
2023-03-18 20:03:42 +01:00
+ // return false;
2023-02-23 17:37:56 +01:00
+ // Paper end
2021-06-11 14:02:28 +02:00
}
}
}
@@ -253,17 +452,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
}
2021-06-13 21:29:58 +02:00
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();
- });
+ return allMatch(inv, facing, IS_EMPTY_TEST);
2023-02-23 17:37:56 +01:00
}
2021-06-13 21:29:58 +02:00
public static boolean suckInItems(Level world, Hopper hopper) {
@@ -272,8 +465,16 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
if (iinventory != null) {
Direction enumdirection = Direction.DOWN;
2021-06-13 21:29:58 +02:00
- return HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) ? false : HopperBlockEntity.getSlots(iinventory, enumdirection).anyMatch((i) -> {
- return HopperBlockEntity.a(hopper, iinventory, i, enumdirection, world); // Spigot
2021-06-11 14:02:28 +02:00
+ // Paper start - optimize hoppers and remove streams
+ skipPullModeEventFire = skipHopperEvents;
2021-06-13 21:29:58 +02:00
+ return !HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) && anyMatch(iinventory, enumdirection, (item, i) -> {
2021-06-11 14:02:28 +02:00
+ // Logic copied from below to avoid extra getItem calls
2023-03-18 20:03:42 +01:00
+ if (!item.isEmpty() && canTakeItemFromContainer(hopper, iinventory, item, i, enumdirection)) {
+ return hopperPull(world, hopper, iinventory, item, i);
2021-06-11 14:02:28 +02:00
+ } else {
+ return false;
+ }
2021-06-13 21:29:58 +02:00
+ // Paper end
2021-06-11 14:02:28 +02:00
});
} else {
2021-06-13 21:29:58 +02:00
Iterator iterator = HopperBlockEntity.getItemsAtAndAbove(world, hopper).iterator();
@@ -292,48 +493,52 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
}
}
2023-03-18 20:03:42 +01:00
+ @io.papermc.paper.annotation.DoNotUse // Paper - method unused as logic is inlined above
2021-06-13 21:29:58 +02:00
private static boolean a(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
ItemStack itemstack = iinventory.getItem(i);
2021-06-11 14:02:28 +02:00
2023-03-18 20:03:42 +01:00
- if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) {
2023-02-23 17:37:56 +01:00
- 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
2023-03-23 22:57:03 +01:00
-
2023-02-23 17:37:56 +01:00
- 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);
2023-03-18 20:03:42 +01:00
- } else if (iinventory.getOwner() != null) {
2023-02-23 17:37:56 +01:00
- sourceInventory = iinventory.getOwner().getInventory();
2023-03-18 20:03:42 +01:00
- } else {
- sourceInventory = new CraftInventory(iinventory);
2023-02-23 17:37:56 +01:00
- }
2023-03-23 22:57:03 +01:00
-
- 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
- }
-
- 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 start - replace pull logic; MAKE SURE TO CHECK FOR DIFFS WHEN UPDATING
+ if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left unused incase reflective plugins
+ 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
+
2023-03-18 20:03:42 +01:00
+ // 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 if (iinventory.getOwner() != null) {
+ // sourceInventory = iinventory.getOwner().getInventory();
+ // } else {
+ // sourceInventory = new CraftInventory(iinventory);
+ // }
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // Bukkit.getServer().getPluginManager().callEvent(event);
+ // if (event.isCancelled()) {
+ // iinventory.setItem(i, itemstack1);
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // if (ihopper instanceof HopperBlockEntity) {
+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot
+ // }
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // return false;
+ // }
+ // int origCount = event.getItem().getAmount(); // Spigot
+ // ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
+ // // CraftBukkit end
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // if (itemstack2.isEmpty()) {
+ // iinventory.setChanged();
+ // return true;
+ // }
2023-03-23 22:57:03 +01:00
+
2023-03-18 20:03:42 +01:00
+ // itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
+ // iinventory.setItem(i, itemstack1);
2023-02-23 17:37:56 +01:00
+ // Paper end
2021-06-11 14:02:28 +02:00
}
return false;
@@ -342,7 +547,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
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
2023-06-08 10:47:19 +02:00
itemEntity.level().getCraftServer().getPluginManager().callEvent(event);
2021-06-11 14:02:28 +02:00
if (event.isCancelled()) {
return false;
@@ -442,7 +647,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
stack = stack.split(to.getMaxStackSize());
}
// Spigot end
2023-02-23 17:37:56 +01:00
+ ignoreTileUpdates = true; // Paper
2021-06-11 14:02:28 +02:00
to.setItem(slot, stack);
2023-02-23 17:37:56 +01:00
+ ignoreTileUpdates = false; // Paper
2023-03-18 20:03:42 +01:00
stack = leftover; // Paper
2021-06-11 14:02:28 +02:00
flag = true;
2021-06-13 21:29:58 +02:00
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
@@ -516,19 +723,47 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// CraftBukkit end
2021-06-11 14:02:28 +02:00
}
+ // Paper start - optimize hopper item suck in
+ static final AABB HOPPER_ITEM_SUCK_OVERALL = Hopper.SUCK.bounds();
+ static final AABB[] HOPPER_ITEM_SUCK_INDIVIDUAL = Hopper.SUCK.toAabbs().toArray(new AABB[0]);
+ // Paper end - optimize hopper item suck in
+
2021-06-13 21:29:58 +02:00
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();
2021-06-11 14:02:28 +02:00
- }).collect(Collectors.toList());
+ // Paper start - optimize hopper item suck in
+ // eliminate multiple getEntitiesOfClass() but maintain the voxelshape collision by moving
+ // the individual AABB checks into the predicate
+ final double shiftX = hopper.getLevelX() - 0.5D;
+ final double shiftY = hopper.getLevelY() - 0.5D;
+ final double shiftZ = hopper.getLevelZ() - 0.5D;
+ return world.getEntitiesOfClass(ItemEntity.class, HOPPER_ITEM_SUCK_OVERALL.move(shiftX, shiftY, shiftZ), (final Entity entity) -> {
+ if (!entity.isAlive()) { // EntitySelector.ENTITY_STILL_ALIVE
+ return false;
+ }
+
+ for (final AABB aabb : HOPPER_ITEM_SUCK_INDIVIDUAL) {
+ if (aabb.move(shiftX, shiftY, shiftZ).intersects(entity.getBoundingBox())) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+ // Paper end - optimize hopper item suck in
2021-06-11 14:02:28 +02:00
}
@Nullable
2021-06-13 21:29:58 +02:00
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
2021-06-11 14:02:28 +02:00
}
@Nullable
2023-03-18 20:03:42 +01:00
private static Container getContainerAt(Level world, double x, double y, double z) {
2023-02-23 17:37:56 +01:00
+ // Paper start - add optimizeEntities parameter
2023-03-18 20:03:42 +01:00
+ return HopperBlockEntity.getContainerAt(world, x, y, z, false);
2023-02-23 17:37:56 +01:00
+ }
+ @Nullable
+ private static Container getContainerAt(Level world, double x, double y, double z, final boolean optimizeEntities) {
+ // Paper end - add optimizeEntities parameter
2021-06-11 14:02:28 +02:00
Object object = null;
2023-03-18 20:03:42 +01:00
BlockPos blockposition = BlockPos.containing(x, y, z);
if ( !world.spigotConfig.hopperCanLoadChunks && !world.hasChunkAt( blockposition ) ) return null; // Spigot
@@ -548,7 +783,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
2021-06-11 14:02:28 +02:00
}
}
- if (object == null) {
+ if (object == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding())) { // Paper
2021-06-13 21:29:58 +02:00
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);
2021-06-11 14:02:28 +02:00
if (!list.isEmpty()) {
@@ -560,7 +795,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static boolean canMergeItems(ItemStack first, ItemStack second) {
2023-06-08 10:47:19 +02:00
- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameTags(first, second);
+ return first.getCount() < first.getMaxStackSize() && first.is(second.getItem()) && first.getDamageValue() == second.getDamageValue() && ((first.isEmpty() && second.isEmpty()) || java.util.Objects.equals(first.getTag(), second.getTag())); // Paper - used to return true for full itemstacks?!
}
@Override
2021-06-11 14:02:28 +02:00
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
2023-06-08 10:47:19 +02:00
index e11618247ad889fa8fadbb2c7addd0de94caf249..081691f9710ff1115e4308f79ed49fbc38941193 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
2023-06-08 10:47:19 +02:00
@@ -95,12 +95,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
2021-06-11 14:02:28 +02:00
@Override
public boolean isEmpty() {
2021-06-13 21:29:58 +02:00
this.unpackLootTable((Player)null);
2021-06-11 14:02:28 +02:00
- return this.getItems().stream().allMatch(ItemStack::isEmpty);
+ // Paper start
2023-03-18 20:03:42 +01:00
+ for (final ItemStack itemStack : this.getItems()) {
2021-06-11 14:02:28 +02:00
+ if (!itemStack.isEmpty()) {
+ return false;
+ }
+ }
+ return true;
2023-03-18 20:03:42 +01:00
+ // Paper end
2021-06-11 14:02:28 +02:00
}
@Override
public ItemStack getItem(int slot) {
2021-06-13 21:29:58 +02:00
- this.unpackLootTable((Player)null);
2023-03-18 20:03:42 +01:00
+ if (slot == 0) this.unpackLootTable((Player)null); // Paper
2021-06-13 21:29:58 +02:00
return this.getItems().get(slot);
2021-06-11 14:02:28 +02:00
}