mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-23 11:06:29 +01:00
2fa8efce9b
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
609 lines
32 KiB
Diff
609 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 c5f3dc74888919e82e2ffbb8d648b7640aa6b690..80cf4852e4010eeeadaf920ab927a40df0179b40 100644
|
|
--- 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
|
|
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
|
|
worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
|
|
|
|
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
|
|
index cc58df88fc3788dcfb7e429ef899b3d558a931cc..b3c99bb1da54209a21b090d31713167fd9729654 100644
|
|
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
|
|
@@ -723,10 +723,16 @@ public final class ItemStack {
|
|
}
|
|
|
|
public ItemStack copy() {
|
|
- if (this.isEmpty()) {
|
|
+ // Paper start
|
|
+ return this.copy(false);
|
|
+ }
|
|
+
|
|
+ public ItemStack copy(boolean originalItem) {
|
|
+ if (!originalItem && this.isEmpty()) {
|
|
+ // Paper end
|
|
return ItemStack.EMPTY;
|
|
} else {
|
|
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
|
|
+ ItemStack itemstack = new ItemStack(originalItem ? 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 5bdad1866386908b9fef74d15862eb107fabe68f..370a25d2deb54f10a35ee24d9e7e92fbfde60edf 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
|
|
@@ -161,6 +162,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/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
|
index 6907e647ef4d3f5c9c46edb4cf0905844dd1cea9..b11f51762ca289d99eaa49e66e31e58595bcea4e 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
|
|
@@ -193,6 +193,202 @@ 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.copy(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);
|
|
+ container.setChanged(); // original logic always marks source inv as changed even if no move happens.
|
|
+ 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.copy(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 if (container.getOwner() != null) {
|
|
+ sourceInventory = container.getOwner().getInventory();
|
|
+ } else {
|
|
+ sourceInventory = new CraftInventory(container);
|
|
+ }
|
|
+ return sourceInventory;
|
|
+ }
|
|
+
|
|
+ private static void cooldownHopper(final Hopper hopper) {
|
|
+ if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) {
|
|
+ blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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
|
|
+
|
|
private static boolean ejectItems(Level world, BlockPos blockposition, BlockState iblockdata, Container iinventory, HopperBlockEntity hopper) { // CraftBukkit
|
|
Container iinventory1 = HopperBlockEntity.getAttachedContainer(world, blockposition, iblockdata);
|
|
|
|
@@ -204,46 +400,49 @@ 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 if (iinventory1.getOwner() != null) {
|
|
- destinationInventory = iinventory1.getOwner().getInventory();
|
|
- } else {
|
|
- destinationInventory = new CraftInventory(iinventory);
|
|
- }
|
|
-
|
|
- 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 if (iinventory1.getOwner() != null) {
|
|
+ // destinationInventory = iinventory1.getOwner().getInventory();
|
|
+ // } else {
|
|
+ // destinationInventory = new CraftInventory(iinventory);
|
|
+ // }
|
|
+
|
|
+ // 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
|
|
}
|
|
}
|
|
}
|
|
@@ -253,17 +452,11 @@ 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();
|
|
- });
|
|
+ return allMatch(inv, facing, IS_EMPTY_TEST);
|
|
}
|
|
|
|
public static boolean suckInItems(Level world, Hopper hopper) {
|
|
@@ -272,8 +465,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(hopper, iinventory, item, i, enumdirection)) {
|
|
+ return hopperPull(world, hopper, iinventory, item, i);
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ // Paper end
|
|
});
|
|
} else {
|
|
Iterator iterator = HopperBlockEntity.getItemsAtAndAbove(world, hopper).iterator();
|
|
@@ -292,48 +493,52 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
}
|
|
|
|
+ @io.papermc.paper.annotation.DoNotUse // 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(ihopper, 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 if (iinventory.getOwner() != null) {
|
|
- sourceInventory = iinventory.getOwner().getInventory();
|
|
- } else {
|
|
- sourceInventory = new CraftInventory(iinventory);
|
|
- }
|
|
-
|
|
- 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
|
|
+
|
|
+ // 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);
|
|
+ // }
|
|
+
|
|
+ // 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 end
|
|
}
|
|
|
|
return false;
|
|
@@ -342,7 +547,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;
|
|
@@ -442,7 +647,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 = leftover; // Paper
|
|
flag = true;
|
|
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
|
|
@@ -516,19 +723,47 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
// CraftBukkit end
|
|
}
|
|
|
|
+ // 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
|
|
+
|
|
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 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
|
|
}
|
|
|
|
@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) {
|
|
+ // Paper start - add optimizeEntities parameter
|
|
+ return HopperBlockEntity.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 = 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
|
|
}
|
|
}
|
|
|
|
- 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()) {
|
|
@@ -560,7 +795,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
|
}
|
|
|
|
private static boolean canMergeItems(ItemStack first, ItemStack second) {
|
|
- 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
|
|
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 e11618247ad889fa8fadbb2c7addd0de94caf249..081691f9710ff1115e4308f79ed49fbc38941193 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 (final ItemStack itemStack : this.getItems()) {
|
|
+ if (!itemStack.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getItem(int slot) {
|
|
- this.unpackLootTable((Player)null);
|
|
+ if (slot == 0) this.unpackLootTable((Player)null); // Paper
|
|
return this.getItems().get(slot);
|
|
}
|
|
|