diff options
author | Nassim Jahnke <[email protected]> | 2024-12-17 11:00:56 +0100 |
---|---|---|
committer | Nassim Jahnke <[email protected]> | 2024-12-17 11:00:56 +0100 |
commit | 6f26d535826c51d5d3da2ffbeaa7344f581938f6 (patch) | |
tree | f212f42711750a8941f2db59b971d2091f4c3ca2 | |
parent | e7995023f12b5eef09624c79032f26659d498c77 (diff) | |
download | Paper-6f26d535826c51d5d3da2ffbeaa7344f581938f6.tar.gz Paper-6f26d535826c51d5d3da2ffbeaa7344f581938f6.zip |
Update hopper optimization patch
-rw-r--r-- | feature-patches/1048-Optimize-Hoppers.patch | 594 | ||||
-rw-r--r-- | feature-patches/1051-Remove-streams-from-hot-code.patch | 168 |
2 files changed, 395 insertions, 367 deletions
diff --git a/feature-patches/1048-Optimize-Hoppers.patch b/feature-patches/1048-Optimize-Hoppers.patch index 3b088a1787..a7a16b45a9 100644 --- a/feature-patches/1048-Optimize-Hoppers.patch +++ b/feature-patches/1048-Optimize-Hoppers.patch @@ -12,28 +12,27 @@ Subject: [PATCH] Optimize Hoppers * 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/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java + +diff --git a/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java new file mode 100644 -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +index 0000000000000000000000000000000000000000..24a2090e068ad3c0d08705050944abdfe19136a2 --- /dev/null -+++ b/src/main/java/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java -@@ -0,0 +0,0 @@ ++++ b/io/papermc/paper/event/inventory/PaperInventoryMoveItemEvent.java +@@ -0,0 +1,29 @@ +package io.papermc.paper.event.inventory; + +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; -+import org.checkerframework.checker.nullness.qual.NonNull; -+import org.checkerframework.framework.qual.DefaultQualifier; -+import org.jetbrains.annotations.NotNull; ++import org.jspecify.annotations.NullMarked; + -+@DefaultQualifier(NonNull.class) ++@NullMarked +public class PaperInventoryMoveItemEvent extends InventoryMoveItemEvent { + + public boolean calledSetItem; + public boolean calledGetItem; + -+ public PaperInventoryMoveItemEvent(final @NotNull Inventory sourceInventory, final @NotNull ItemStack itemStack, final @NotNull Inventory destinationInventory, final boolean didSourceInitiate) { ++ public PaperInventoryMoveItemEvent(final Inventory sourceInventory, final ItemStack itemStack, final Inventory destinationInventory, final boolean didSourceInitiate) { + super(sourceInventory, itemStack, destinationInventory, didSourceInitiate); + } + @@ -49,23 +48,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.calledSetItem = true; + } +} -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa - ServerLevel worldserver = (ServerLevel) iterator.next(); - worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent - worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent -+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers - - gameprofilerfiller.push(() -> { - String s = String.valueOf(worldserver); -diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/item/ItemStack.java -+++ b/src/main/java/net/minecraft/world/item/ItemStack.java -@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder { +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index 646c2f2b617ed706021c83c9fc4492860dfdd4e9..415ab7faf371507e278d0da5bf0ffa9972585876 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -1564,6 +1564,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + for (ServerLevel serverLevel : this.getAllLevels()) { + serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent + serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent ++ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers + profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().location()); + /* Drop global time updates + if (this.tickCount % 20 == 0) { +diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java +index 50cd12def88c9449cad8875c553f5ed9ef1cd791..3d93bb1aac5ad4830fc1dceddb6bebacee28f72a 100644 +--- a/net/minecraft/world/item/ItemStack.java ++++ b/net/minecraft/world/item/ItemStack.java +@@ -815,10 +815,16 @@ public final class ItemStack implements DataComponentHolder { } public ItemStack copy() { @@ -74,42 +73,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return this.copy(false); + } + -+ public ItemStack copy(boolean originalItem) { ++ public ItemStack copy(final boolean originalItem) { + if (!originalItem && this.isEmpty()) { + // Paper end - Perf: Optimize Hoppers - return ItemStack.EMPTY; + return EMPTY; } else { -- ItemStack itemstack = new ItemStack(this.getItem(), this.count, this.components.copy()); -+ ItemStack itemstack = new ItemStack(originalItem ? this.item : this.getItem(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers - - itemstack.setPopTime(this.getPopTime()); - return itemstack; -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ import org.bukkit.inventory.InventoryHolder; - // CraftBukkit end +- ItemStack itemStack = new ItemStack(this.getItem(), this.count, this.components.copy()); ++ ItemStack itemStack = new ItemStack(originalItem ? this.item : this.getItem(), this.count, this.components.copy()); // Paper - Perf: Optimize Hoppers + itemStack.setPopTime(this.getPopTime()); + return itemStack; + } +diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java +index 2ebdf1ad323bb53dfe9eed319e25856b35a1443c..77618757c0e678532dbab814aceed83f7f1cd892 100644 +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -26,6 +26,7 @@ import net.minecraft.world.level.block.state.BlockState; + import org.slf4j.Logger; public abstract class BlockEntity { -+ static boolean ignoreTileUpdates; // Paper - Perf: Optimize Hoppers - ++ static boolean ignoreBlockEntityUpdates; // Paper - Perf: Optimize Hoppers // CraftBukkit start - data containers - private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); -@@ -0,0 +0,0 @@ public abstract class BlockEntity { + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); + public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer; +@@ -196,6 +197,7 @@ public abstract class BlockEntity { public void setChanged() { if (this.level != null) { -+ if (ignoreTileUpdates) return; // Paper - Perf: Optimize Hoppers - BlockEntity.setChanged(this.level, this.worldPosition, this.blockState); ++ if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers + setChanged(this.level, this.worldPosition, this.blockState); + } + } +diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +index 60e1e44f328e66d52ebf08476b533fef83bc5eba..8139868201c2eaca29588b840a2bd85778f1b3d5 100644 +--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +@@ -139,18 +139,56 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } - -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - } + // Paper start - Perf: Optimize Hoppers @@ -117,10 +116,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + private static final int HOPPER_HAS_ITEMS = 1; + private static final int HOPPER_IS_FULL = 2; + -+ private static int getFullState(final HopperBlockEntity tileEntity) { -+ tileEntity.unpackLootTable(null); ++ private static int getFullState(final HopperBlockEntity hoper) { ++ hoper.unpackLootTable(null); + -+ final List<ItemStack> hopperItems = tileEntity.getItems(); ++ final List<ItemStack> hopperItems = hoper.items; + + boolean empty = true; + boolean full = true; @@ -149,32 +148,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end - Perf: Optimize Hoppers + - private static boolean tryMoveItems(Level world, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier booleansupplier) { - if (world.isClientSide) { + private static boolean tryMoveItems(Level level, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier validator) { + if (level.isClientSide) { return false; -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - if (!blockEntity.isOnCooldown() && (Boolean) state.getValue(HopperBlock.ENABLED)) { + } else { + if (!blockEntity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) { boolean flag = false; - - if (!blockEntity.isEmpty()) { + final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers + if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers - flag = HopperBlockEntity.ejectItems(world, pos, blockEntity); + flag = ejectItems(level, pos, blockEntity); } - if (!blockEntity.inventoryFull()) { +- flag |= validator.getAsBoolean(); + if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers - flag |= booleansupplier.getAsBoolean(); ++ flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items } -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - return false; + if (flag) { +@@ -174,6 +212,206 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + return true; } + // Paper start - Perf: Optimize Hoppers ++ public static boolean skipHopperEvents; + 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; @@ -233,7 +233,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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 ++ // No plugin could have relied on the behavior of false as the other call + // site for IMIE did not exhibit the same behavior + return true; + } @@ -248,68 +248,72 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount); + } + -+ ignoreTileUpdates = true; ++ ignoreBlockEntityUpdates = true; + container.setItem(i, origItemStack); -+ ignoreTileUpdates = false; ++ ignoreBlockEntityUpdates = false; + container.setChanged(); + return true; + } + origItemStack.setCount(originalItemCount); + + if (level.paperConfig().hopper.cooldownWhenFull) { -+ cooldownHopper(hopper); ++ applyCooldown(hopper); + } + + return false; + } + + @Nullable -+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) { -+ final Inventory destinationInventory = getInventory(iinventory); -+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(hopper.getOwner(false).getInventory(), -+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true); ++ private static ItemStack callPushMoveEvent(Container destination, ItemStack itemStack, HopperBlockEntity hopper) { ++ final org.bukkit.inventory.Inventory destinationInventory = getInventory(destination); ++ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent( ++ hopper.getOwner(false).getInventory(), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), ++ destinationInventory, ++ true ++ ); + final boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPushModeEventFire = true; + } + if (!result) { -+ cooldownHopper(hopper); ++ applyCooldown(hopper); + return null; + } + + if (event.calledSetItem) { -+ return CraftItemStack.asNMSCopy(event.getItem()); ++ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); + } else { -+ return itemstack; ++ 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); ++ final org.bukkit.inventory.Inventory sourceInventory = getInventory(container); ++ final org.bukkit.inventory.Inventory destination = getInventory(hopper); + + // Mirror is safe as no plugins ever use this item -+ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, CraftItemStack.asCraftMirror(itemstack), destination, false); ++ final io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent event = new io.papermc.paper.event.inventory.PaperInventoryMoveItemEvent(sourceInventory, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), destination, false); + final boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPullModeEventFire = true; + } + if (!result) { -+ cooldownHopper(hopper); ++ applyCooldown(hopper); + return null; + } + + if (event.calledSetItem) { -+ return CraftItemStack.asNMSCopy(event.getItem()); ++ return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); + } else { + return itemstack; + } + } + -+ private static Inventory getInventory(final Container container) { -+ final Inventory sourceInventory; -+ if (container instanceof CompoundContainer compoundContainer) { ++ private static org.bukkit.inventory.Inventory getInventory(final Container container) { ++ final org.bukkit.inventory.Inventory sourceInventory; ++ if (container instanceof net.minecraft.world.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) { @@ -317,28 +321,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } else if (container.getOwner() != null) { + sourceInventory = container.getOwner().getInventory(); + } else { -+ sourceInventory = new CraftInventory(container); ++ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); + } + return sourceInventory; + } + -+ private static void cooldownHopper(final Hopper hopper) { ++ private static void applyCooldown(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)) { ++ private static boolean allMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) { ++ if (container instanceof WorldlyContainer) { ++ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) { ++ if (!test.test(container.getItem(slot), slot)) { + return false; + } + } + } else { -+ int size = iinventory.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (!test.test(iinventory.getItem(i), i)) { ++ int size = container.getContainerSize(); ++ for (int slot = 0; slot < size; slot++) { ++ if (!test.test(container.getItem(slot), slot)) { + return false; + } + } @@ -346,319 +350,335 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + 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)) { ++ private static boolean anyMatch(Container container, Direction direction, java.util.function.BiPredicate<ItemStack, Integer> test) { ++ if (container instanceof WorldlyContainer) { ++ for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) { ++ if (test.test(container.getItem(slot), slot)) { + return true; + } + } + } else { -+ int size = iinventory.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (test.test(iinventory.getItem(i), i)) { ++ int size = container.getContainerSize(); ++ for (int slot = 0; slot < size; slot++) { ++ if (test.test(container.getItem(slot), slot)) { + 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(); ++ 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 - Perf: Optimize Hoppers + - private static boolean ejectItems(Level world, BlockPos pos, HopperBlockEntity blockEntity) { - Container iinventory = HopperBlockEntity.getAttachedContainer(world, pos, blockEntity); - -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - if (HopperBlockEntity.isFullContainer(iinventory, enumdirection)) { + private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) { + Container attachedContainer = getAttachedContainer(level, pos, blockEntity); + if (attachedContainer == null) { +@@ -183,57 +421,60 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + if (isFullContainer(attachedContainer, opposite)) { return false; } else { -- for (int i = 0; i < blockEntity.getContainerSize(); ++i) { -- ItemStack itemstack = blockEntity.getItem(i); -- -- if (!itemstack.isEmpty()) { -- int j = itemstack.getCount(); +- for (int i = 0; i < blockEntity.getContainerSize(); i++) { +- ItemStack item = blockEntity.getItem(i); +- if (!item.isEmpty()) { +- int count = item.getCount(); - // CraftBukkit start - Call event when pushing items into other inventories -- ItemStack original = itemstack.copy(); -- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot +- ItemStack original = item.copy(); +- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( +- blockEntity.removeItem(i, level.spigotConfig.hopperAmount) +- ); // Spigot - -- Inventory destinationInventory; +- org.bukkit.inventory.Inventory destinationInventory; - // Have to special case large chests as they work oddly -- if (iinventory instanceof CompoundContainer) { -- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory); -- } else if (iinventory.getOwner() != null) { -- destinationInventory = iinventory.getOwner().getInventory(); +- if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) { +- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); +- } else if (attachedContainer.getOwner() != null) { +- destinationInventory = attachedContainer.getOwner().getInventory(); - } else { -- destinationInventory = new CraftInventory(iinventory); +- destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); - } - -- InventoryMoveItemEvent event = new InventoryMoveItemEvent(blockEntity.getOwner().getInventory(), oitemstack, destinationInventory, true); -- world.getCraftServer().getPluginManager().callEvent(event); -- if (event.isCancelled()) { +- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( +- blockEntity.getOwner().getInventory(), +- oitemstack, +- destinationInventory, +- true +- ); +- if (!event.callEvent()) { - blockEntity.setItem(i, original); -- blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot +- blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot - return false; - } - int origCount = event.getItem().getAmount(); // Spigot -- ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); +- ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); - // CraftBukkit end - -- if (itemstack1.isEmpty()) { -- iinventory.setChanged(); +- if (itemStack.isEmpty()) { +- attachedContainer.setChanged(); - return true; - } - -- itemstack.setCount(j); +- item.setCount(count); - // Spigot start -- itemstack.shrink(origCount - itemstack1.getCount()); -- if (j <= world.spigotConfig.hopperAmount) { +- item.shrink(origCount - itemStack.getCount()); +- if (count <= level.spigotConfig.hopperAmount) { - // Spigot end -- blockEntity.setItem(i, itemstack); +- blockEntity.setItem(i, item); - } - } - } - - return false; + // Paper start - Perf: Optimize Hoppers -+ return hopperPush(world, iinventory, enumdirection, blockEntity); -+ //for (int i = 0; i < blockEntity.getContainerSize(); ++i) { -+ // ItemStack itemstack = blockEntity.getItem(i); -+ -+ // if (!itemstack.isEmpty()) { -+ // int j = itemstack.getCount(); ++ return hopperPush(level, attachedContainer, opposite, blockEntity); ++ //for (int i = 0; i < blockEntity.getContainerSize(); i++) { ++ // ItemStack item = blockEntity.getItem(i); ++ // if (!item.isEmpty()) { ++ // int count = item.getCount(); + // // CraftBukkit start - Call event when pushing items into other inventories -+ // ItemStack original = itemstack.copy(); -+ // CraftItemStack oitemstack = CraftItemStack.asCraftMirror(blockEntity.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot ++ // ItemStack original = item.copy(); ++ // org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( ++ // blockEntity.removeItem(i, level.spigotConfig.hopperAmount) ++ // ); // Spigot + -+ // Inventory destinationInventory; ++ // org.bukkit.inventory.Inventory destinationInventory; + // // Have to special case large chests as they work oddly -+ // if (iinventory instanceof CompoundContainer) { -+ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory); -+ // } else if (iinventory.getOwner() != null) { -+ // destinationInventory = iinventory.getOwner().getInventory(); ++ // if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) { ++ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); ++ // } else if (attachedContainer.getOwner() != null) { ++ // destinationInventory = attachedContainer.getOwner().getInventory(); + // } else { -+ // destinationInventory = new CraftInventory(iinventory); ++ // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); + // } + -+ // InventoryMoveItemEvent event = new InventoryMoveItemEvent(tileentityhopper.getOwner().getInventory(), oitemstack, destinationInventory, true); -+ // world.getCraftServer().getPluginManager().callEvent(event); -+ // if (event.isCancelled()) { ++ // org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( ++ // blockEntity.getOwner().getInventory(), ++ // oitemstack, ++ // destinationInventory, ++ // true ++ // ); ++ // if (!event.callEvent()) { + // blockEntity.setItem(i, original); -+ // blockEntity.setCooldown(world.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot ++ // blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot + // return false; + // } + // int origCount = event.getItem().getAmount(); // Spigot -+ // ItemStack itemstack1 = HopperBlockEntity.addItem(blockEntity, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); ++ // ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); + // // CraftBukkit end + -+ // if (itemstack1.isEmpty()) { -+ // iinventory.setChanged(); ++ // if (itemStack.isEmpty()) { ++ // attachedContainer.setChanged(); + // return true; + // } + -+ // itemstack.setCount(j); ++ // item.setCount(count); + // // Spigot start -+ // itemstack.shrink(origCount - itemstack1.getCount()); -+ // if (j <= world.spigotConfig.hopperAmount) { -+ // // Spigot end -+ // blockEntity.setItem(i, itemstack); ++ // item.shrink(origCount - itemStack.getCount()); ++ // if (count <= level.spigotConfig.hopperAmount) { ++ // // Spigot end ++ // blockEntity.setItem(i, item); + // } + // } + //} + -+ // return false; ++ //return false; + // Paper end - Perf: Optimize Hoppers } } } -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - return false; - } - } -- - return true; - } - -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - if (iinventory != null) { - Direction enumdirection = Direction.DOWN; +@@ -288,6 +529,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState); + if (sourceContainer != null) { + Direction direction = Direction.DOWN; + skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers - int[] aint = HopperBlockEntity.getSlots(iinventory, enumdirection); - int i = aint.length; - -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - ItemStack itemstack = iinventory.getItem(i); - if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(ihopper, iinventory, itemstack, i, enumdirection)) { -- int j = itemstack.getCount(); + for (int i : getSlots(sourceContainer, direction)) { + if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot +@@ -313,55 +555,58 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + private static boolean tryTakeInItemFromSlot(Hopper hopper, Container container, int slot, Direction direction, Level level) { // Spigot + ItemStack item = container.getItem(slot); + if (!item.isEmpty() && canTakeItemFromContainer(hopper, container, item, slot, direction)) { +- int count = item.getCount(); - // CraftBukkit start - Call event on collection of items from inventories into the hopper -- ItemStack original = itemstack.copy(); -- CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.removeItem(i, world.spigotConfig.hopperAmount)); // Spigot +- ItemStack original = item.copy(); +- org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( +- container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot +- ); - -- Inventory sourceInventory; +- org.bukkit.inventory.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(); +- if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) { +- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); +- } else if (container.getOwner() != null) { +- sourceInventory = container.getOwner().getInventory(); - } else { -- sourceInventory = new CraftInventory(iinventory); +- sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); - } - -- InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack, ihopper.getOwner().getInventory(), false); +- org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( +- sourceInventory, +- oitemstack, +- hopper.getOwner().getInventory(), +- false +- ); - -- Bukkit.getServer().getPluginManager().callEvent(event); -- if (event.isCancelled()) { -- iinventory.setItem(i, original); +- if (!event.callEvent()) { +- container.setItem(slot, original); - -- if (ihopper instanceof HopperBlockEntity) { -- ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot +- if (hopper instanceof final HopperBlockEntity hopperBlockEntity) { +- hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot - } - - return false; - } - int origCount = event.getItem().getAmount(); // Spigot -- ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); +- ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); - // CraftBukkit end - -- if (itemstack1.isEmpty()) { -- iinventory.setChanged(); +- if (itemStack.isEmpty()) { +- container.setChanged(); - return true; - } - -- itemstack.setCount(j); +- item.setCount(count); - // Spigot start -- itemstack.shrink(origCount - itemstack1.getCount()); -- if (j <= world.spigotConfig.hopperAmount) { +- item.shrink(origCount - itemStack.getCount()); +- if (count <= level.spigotConfig.hopperAmount) { - // Spigot end -- iinventory.setItem(i, itemstack); +- container.setItem(slot, item); - } + // Paper start - Perf: Optimize Hoppers -+ return hopperPull(world, ihopper, iinventory, itemstack, i); -+ // int j = itemstack.getCount(); -+ // // CraftBukkit start - Call event on collection of items from inventories into the hopper -+ // ItemStack original = itemstack.copy(); -+ // 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, ihopper.getOwner().getInventory(), false); -+ -+ // Bukkit.getServer().getPluginManager().callEvent(event); -+ // if (event.isCancelled()) { -+ // iinventory.setItem(i, original); -+ -+ // if (ihopper instanceof HopperBlockEntity) { -+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot -+ // } -+ -+ // return false; -+ // } -+ // int origCount = event.getItem().getAmount(); // Spigot -+ // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); -+ // // CraftBukkit end -+ -+ // if (itemstack1.isEmpty()) { -+ // iinventory.setChanged(); -+ // return true; -+ // } -+ -+ // itemstack.setCount(j); -+ // // Spigot start -+ // itemstack.shrink(origCount - itemstack1.getCount()); -+ // if (j <= world.spigotConfig.hopperAmount) { -+ // // Spigot end -+ // iinventory.setItem(i, itemstack); -+ // } ++ return hopperPull(level, hopper, container, item, slot); ++ //int count = item.getCount(); ++ //// CraftBukkit start - Call event on collection of items from inventories into the hopper ++ //ItemStack original = item.copy(); ++ //org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( ++ // container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot ++ //); ++ ++ //org.bukkit.inventory.Inventory sourceInventory; ++ //// Have to special case large chests as they work oddly ++ //if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) { ++ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); ++ //} else if (container.getOwner() != null) { ++ // sourceInventory = container.getOwner().getInventory(); ++ //} else { ++ // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); ++ //} ++ ++ //org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( ++ // sourceInventory, ++ // oitemstack, ++ // hopper.getOwner().getInventory(), ++ // false ++ //); ++ ++ //if (!event.callEvent()) { ++ // container.setItem(slot, original); ++ ++ // if (hopper instanceof final HopperBlockEntity hopperBlockEntity) { ++ // hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot ++ // } ++ ++ // return false; ++ //} ++ //int origCount = event.getItem().getAmount(); // Spigot ++ //ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); ++ //// CraftBukkit end ++ ++ //if (itemStack.isEmpty()) { ++ // container.setChanged(); ++ // return true; ++ //} ++ ++ //item.setCount(count); ++ //// Spigot start ++ //item.shrink(origCount - itemStack.getCount()); ++ //if (count <= level.spigotConfig.hopperAmount) { ++ // // Spigot end ++ // container.setItem(slot, item); ++ //} + // Paper end - Perf: Optimize Hoppers } return false; -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - public static boolean addItem(Container inventory, ItemEntity itemEntity) { +@@ -370,13 +615,15 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + public static boolean addItem(Container container, ItemEntity item) { boolean flag = false; // CraftBukkit start -- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); -+ if (InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers -+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation - itemEntity.level().getCraftServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { ++ if (org.bukkit.event.inventory.InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers + org.bukkit.event.inventory.InventoryPickupItemEvent event = new org.bukkit.event.inventory.InventoryPickupItemEvent( +- container.getOwner().getInventory(), (org.bukkit.entity.Item) item.getBukkitEntity() ++ getInventory(container), (org.bukkit.entity.Item) item.getBukkitEntity() // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation + ); + if (!event.callEvent()) { return false; } // CraftBukkit end + } // Paper - Perf: Optimize Hoppers - ItemStack itemstack = itemEntity.getItem().copy(); - ItemStack itemstack1 = HopperBlockEntity.addItem((Container) null, inventory, itemstack, (Direction) null); - -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - stack = stack.split(to.getMaxStackSize()); + ItemStack itemStack = item.getItem().copy(); + ItemStack itemStack1 = addItem(null, container, itemStack, null); + if (itemStack1.isEmpty()) { +@@ -431,7 +678,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + stack = stack.split(destination.getMaxStackSize()); } // Spigot end -+ ignoreTileUpdates = true; // Paper - Perf: Optimize Hoppers - to.setItem(slot, stack); -+ ignoreTileUpdates = false; // Paper - Perf: Optimize Hoppers ++ ignoreBlockEntityUpdates = true; // Paper - Perf: Optimize Hoppers + destination.setItem(slot, stack); ++ ignoreBlockEntityUpdates = false; // Paper - Perf: Optimize Hoppers stack = leftover; // Paper - Make hoppers respect inventory max stack size flag = true; - } else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) { -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } else if (canMergeItems(item, stack)) { +@@ -519,13 +768,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Nullable - public static Container getContainerAt(Level world, BlockPos pos) { -- return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D); -+ return HopperBlockEntity.getContainerAt(world, pos, world.getBlockState(pos), (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true); + public static Container getContainerAt(Level level, BlockPos pos) { +- return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); ++ return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, true); // Paper - Optimize hoppers } @Nullable - private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z) { + private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z) { + // Paper start - Perf: Optimize Hoppers -+ return HopperBlockEntity.getContainerAt(world, pos, state, x, y, z, false); ++ return HopperBlockEntity.getContainerAt(level, pos, state, x, y, z, false); + } + @Nullable -+ private static Container getContainerAt(Level world, BlockPos pos, BlockState state, double x, double y, double z, boolean optimizeEntities) { ++ private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z, final boolean optimizeEntities) { + // Paper end - Perf: Optimize Hoppers - Container iinventory = HopperBlockEntity.getBlockContainer(world, pos, state); - -- if (iinventory == null) { -+ if (iinventory == null && (!optimizeEntities || !world.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers - iinventory = HopperBlockEntity.getEntityContainer(world, x, y, z); + Container blockContainer = getBlockContainer(level, pos, state); +- if (blockContainer == null) { ++ if (blockContainer == null && (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers + blockContainer = getEntityContainer(level, x, y, z); } -@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen +@@ -551,14 +806,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Nullable - private static Container getEntityContainer(Level world, double x, double y, double z) { -- 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); -+ List<Entity> list = world.getEntitiesOfClass((Class) Container.class, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper - Perf: Optimize hoppers - - return !list.isEmpty() ? (Container) list.get(world.random.nextInt(list.size())) : null; + private static Container getEntityContainer(Level level, double x, double y, double z) { +- List<Entity> entities = level.getEntities( +- (Entity)null, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR ++ List<Entity> entities = level.getEntitiesOfClass( ++ (Class) Container.class, new AABB(x - 0.5, y - 0.5, z - 0.5, x + 0.5, y + 0.5, z + 0.5), EntitySelector.CONTAINER_ENTITY_SELECTOR // Paper - Perf: Optimize hoppers + ); + return !entities.isEmpty() ? (Container)entities.get(level.random.nextInt(entities.size())) : null; } - private static boolean canMergeItems(ItemStack first, ItemStack second) { -- return first.getCount() <= first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second); -+ return first.getCount() < first.getMaxStackSize() && ItemStack.isSameItemSameComponents(first, second); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?! + private static boolean canMergeItems(ItemStack stack1, ItemStack stack2) { +- return stack1.getCount() <= stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); ++ return stack1.getCount() < stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); // Paper - Perf: Optimize Hoppers; 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc +diff --git a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +index 73b3ddb120d6b6f89e478960e78bed415baea205..f9c31da81d84033abfc1179fc643bceffe35da17 100644 +--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +@@ -53,7 +53,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc @Override - public ItemStack getItem(int slot) { + public ItemStack getItem(int index) { - this.unpackLootTable(null); -+ if (slot == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers - return super.getItem(slot); ++ if (index == 0) this.unpackLootTable(null); // Paper - Perf: Optimize Hoppers + return super.getItem(index); } diff --git a/feature-patches/1051-Remove-streams-from-hot-code.patch b/feature-patches/1051-Remove-streams-from-hot-code.patch index 815e75e1bd..afbe9f1e4c 100644 --- a/feature-patches/1051-Remove-streams-from-hot-code.patch +++ b/feature-patches/1051-Remove-streams-from-hot-code.patch @@ -6,44 +6,50 @@ Subject: [PATCH] Remove streams from hot code Co-authored-by: Bjarne Koll <[email protected]> Co-authored-by: Spottedleaf <[email protected]> -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java -@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> +diff --git a/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/net/minecraft/world/entity/ai/behavior/GateBehavior.java +index c215d97c24e6501e1a48a76fc08bf48ff4dfe462..bd31d1cac0d022a72bd536c41d1ef811886e7068 100644 +--- a/net/minecraft/world/entity/ai/behavior/GateBehavior.java ++++ b/net/minecraft/world/entity/ai/behavior/GateBehavior.java +@@ -57,7 +57,7 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> if (this.hasRequiredMemories(entity)) { this.status = Behavior.Status.RUNNING; this.orderPolicy.apply(this.behaviors); -- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time); -+ this.runningPolicy.apply(this.behaviors, world, entity, time); // Paper - Perf: Remove streams from hot code +- this.runningPolicy.apply(this.behaviors.stream(), level, entity, gameTime); ++ this.runningPolicy.apply(this.behaviors, level, entity, gameTime); // Paper - Perf: Remove streams from hot code return true; } else { return false; -@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> +@@ -66,10 +66,13 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> @Override - public final void tickOrStop(ServerLevel world, E entity, long time) { -- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.tickOrStop(world, entity, time)); + public final void tickOrStop(ServerLevel level, E entity, long gameTime) { +- this.behaviors +- .stream() +- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING) +- .forEach(behavior -> behavior.tickOrStop(level, entity, gameTime)); + // Paper start - Perf: Remove streams from hot code -+ for (final BehaviorControl<? super E> task : this.behaviors) { -+ if (task.getStatus() == Behavior.Status.RUNNING) { -+ task.tickOrStop(world, entity, time); ++ for (final BehaviorControl<? super E> behavior : this.behaviors) { ++ if (behavior.getStatus() == Behavior.Status.RUNNING) { ++ behavior.tickOrStop(level, entity, gameTime); + } + } + // Paper end - Perf: Remove streams from hot code - if (this.behaviors.stream().noneMatch(task -> task.getStatus() == Behavior.Status.RUNNING)) { - this.doStop(world, entity, time); + if (this.behaviors.stream().noneMatch(behavior -> behavior.getStatus() == Behavior.Status.RUNNING)) { + this.doStop(level, entity, gameTime); } -@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> +@@ -78,11 +81,16 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> @Override - public final void doStop(ServerLevel world, E entity, long time) { + public final void doStop(ServerLevel level, E entity, long gameTime) { this.status = Behavior.Status.STOPPED; -- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.doStop(world, entity, time)); +- this.behaviors +- .stream() +- .filter(behavior -> behavior.getStatus() == Behavior.Status.RUNNING) +- .forEach(behavior -> behavior.doStop(level, entity, gameTime)); - this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory); + // Paper start - Perf: Remove streams from hot code -+ for (final BehaviorControl<? super E> task : this.behaviors) { -+ if (task.getStatus() == Behavior.Status.RUNNING) { -+ task.doStop(world, entity, time); ++ for (final BehaviorControl<? super E> behavior : this.behaviors) { ++ if (behavior.getStatus() == Behavior.Status.RUNNING) { ++ behavior.doStop(level, entity, gameTime); + } + } + for (final MemoryModuleType<?> exitErasedMemory : this.exitErasedMemories) { @@ -53,17 +59,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } @Override -@@ -0,0 +0,0 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> +@@ -116,20 +124,30 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E> public static enum RunningPolicy { RUN_ONE { + // Paper start - Perf: Remove streams from hot code @Override -- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) { -- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).filter(task -> task.tryStart(world, entity, time)).findFirst(); -+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) { -+ for (final BehaviorControl<? super E> task : tasks) { -+ if (task.getStatus() == Behavior.Status.STOPPED && task.tryStart(world, entity, time)) { +- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) { +- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED) +- .filter(behavior -> behavior.tryStart(level, owner, gameTime)) +- .findFirst(); ++ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) { ++ for (final BehaviorControl<? super E> behavior : behaviors) { ++ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStart(level, owner, gameTime)) { + break; + } + } @@ -73,28 +81,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 TRY_ALL { + // Paper start - Perf: Remove streams from hot code @Override -- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) { -- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).forEach(task -> task.tryStart(world, entity, time)); -+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) { -+ for (final BehaviorControl<? super E> task : tasks) { -+ if (task.getStatus() == Behavior.Status.STOPPED) { -+ task.tryStart(world, entity, time); +- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) { +- behaviors.filter(behavior -> behavior.getStatus() == Behavior.Status.STOPPED).forEach(behavior -> behavior.tryStart(level, owner, gameTime)); ++ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime) { ++ for (final BehaviorControl<? super E> behavior : behaviors) { ++ if (behavior.getStatus() == Behavior.Status.STOPPED) { ++ behavior.tryStart(level, owner, gameTime); + } + } + // Paper end - Perf: Remove streams from hot code } }; -- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time); -+ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time); // Paper - Perf: Remove streams from hot code +- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime); ++ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> behaviors, ServerLevel level, E owner, long gameTime); // Paper - Perf: Remove streams from hot code } } -diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java -+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java -@@ -0,0 +0,0 @@ public class GossipContainer { - return this.gossips.entrySet().stream().flatMap(entry -> entry.getValue().unpack(entry.getKey())); +diff --git a/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/net/minecraft/world/entity/ai/gossip/GossipContainer.java +index 2c839dc80f451c83135828a97aced1a531004bab..b74a4ce1b629d440681a1f5c026997ccaf1d0373 100644 +--- a/net/minecraft/world/entity/ai/gossip/GossipContainer.java ++++ b/net/minecraft/world/entity/ai/gossip/GossipContainer.java +@@ -59,8 +59,22 @@ public class GossipContainer { + return this.gossips.entrySet().stream().flatMap(gossip -> gossip.getValue().unpack(gossip.getKey())); } + // Paper start - Perf: Remove streams from hot code @@ -111,35 +119,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end - Perf: Remove streams from hot code + - private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int count) { + private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int amount) { - List<GossipContainer.GossipEntry> list = this.unpack().toList(); + List<GossipContainer.GossipEntry> list = this.decompress(); // Paper - Perf: Remove streams from hot code if (list.isEmpty()) { return Collections.emptyList(); } else { -@@ -0,0 +0,0 @@ public class GossipContainer { +@@ -145,7 +159,7 @@ public class GossipContainer { public <T> T store(DynamicOps<T> ops) { return GossipContainer.GossipEntry.LIST_CODEC - .encodeStart(ops, this.unpack().toList()) + .encodeStart(ops, this.decompress()) // Paper - Perf: Remove streams from hot code - .resultOrPartial(error -> LOGGER.warn("Failed to serialize gossips: {}", error)) + .resultOrPartial(errorMessage -> LOGGER.warn("Failed to serialize gossips: {}", errorMessage)) .orElseGet(ops::emptyList); } -@@ -0,0 +0,0 @@ public class GossipContainer { +@@ -172,12 +186,23 @@ public class GossipContainer { final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>(); - public int weightedValue(Predicate<GossipType> gossipTypeFilter) { + public int weightedValue(Predicate<GossipType> gossipType) { - return this.entries - .object2IntEntrySet() - .stream() -- .filter(entry -> gossipTypeFilter.test(entry.getKey())) -- .mapToInt(entry -> entry.getIntValue() * entry.getKey().weight) +- .filter(gossip -> gossipType.test(gossip.getKey())) +- .mapToInt(gossip -> gossip.getIntValue() * gossip.getKey().weight) - .sum(); + // Paper start - Perf: Remove streams from hot code + int weight = 0; + for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) { -+ if (gossipTypeFilter.test(entry.getKey())) { ++ if (gossipType.test(entry.getKey())) { + weight += entry.getIntValue() * entry.getKey().weight; + } + } @@ -155,29 +163,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end - Perf: Remove streams from hot code } - public Stream<GossipContainer.GossipEntry> unpack(UUID target) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java -@@ -0,0 +0,0 @@ public class NearestItemSensor extends Sensor<Mob> { + public Stream<GossipContainer.GossipEntry> unpack(UUID identifier) { +diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +index 38873e56e95dc772b184e4271f7af1fb411ac9f8..09fd13e2d958da8326276c4dadf25bf488aff5ac 100644 +--- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +@@ -24,13 +24,17 @@ public class NearestItemSensor extends Sensor<Mob> { @Override - protected void doTick(ServerLevel world, Mob entity) { + protected void doTick(ServerLevel level, Mob entity) { Brain<?> brain = entity.getBrain(); -- List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true); -+ List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(world, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities - list.sort(Comparator.comparingDouble(entity::distanceToSqr)); -- Optional<ItemEntity> optional = list.stream() -- .filter(itemEntity -> entity.wantsToPickUp(world, itemEntity.getItem())) -- .filter(itemEntityx -> itemEntityx.closerThan(entity, 32.0)) +- List<ItemEntity> entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true); ++ List<ItemEntity> entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities + entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); +- Optional<ItemEntity> optional = entitiesOfClass.stream() +- .filter(itemEntity -> entity.wantsToPickUp(level, itemEntity.getItem())) +- .filter(itemEntity -> itemEntity.closerThan(entity, 32.0)) - .filter(entity::hasLineOfSight) - .findFirst(); - brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional); + // Paper start - Perf: remove streams from hot code + ItemEntity nearest = null; -+ for (ItemEntity entityItem : list) { -+ if (entity.hasLineOfSight(entityItem)) { // Paper - Perf: Move predicate into getEntities -+ nearest = entityItem; ++ for (final ItemEntity itemEntity : entitiesOfClass) { ++ if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities ++ nearest = itemEntity; + break; + } + } @@ -185,31 +193,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end - Perf: remove streams from hot code } } -diff --git a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -@@ -0,0 +0,0 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { - int j = pos.getMinBlockZ(); - ObjectList<Beardifier.Rigid> objectList = new ObjectArrayList<>(10); - ObjectList<JigsawJunction> objectList2 = new ObjectArrayList<>(32); -- world.startsForStructure(pos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE) +diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java +index 1a09da5aa1ae047a002d6779326c2a29e47d32b5..131923282c9ecbcb1d7f45a826da907c02bd2716 100644 +--- a/net/minecraft/world/level/levelgen/Beardifier.java ++++ b/net/minecraft/world/level/levelgen/Beardifier.java +@@ -35,9 +35,10 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { + int minBlockZ = chunkPos.getMinBlockZ(); + ObjectList<Beardifier.Rigid> list = new ObjectArrayList<>(10); + ObjectList<JigsawJunction> list1 = new ObjectArrayList<>(32); +- structureManager.startsForStructure(chunkPos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE) - .forEach( -- start -> { +- structureStart -> { + // Paper start - Perf: Remove streams from hot code -+ for (net.minecraft.world.level.levelgen.structure.StructureStart start : world.startsForStructure(pos, (structure) -> { ++ for (net.minecraft.world.level.levelgen.structure.StructureStart structureStart : structureManager.startsForStructure(chunkPos, structure -> { + return structure.terrainAdaptation() != TerrainAdjustment.NONE; + })) { // Paper end - Perf: Remove streams from hot code - TerrainAdjustment terrainAdjustment = start.getStructure().terrainAdaptation(); + TerrainAdjustment terrainAdjustment = structureStart.getStructure().terrainAdaptation(); - for (StructurePiece structurePiece : start.getPieces()) { -@@ -0,0 +0,0 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { + for (StructurePiece structurePiece : structureStart.getPieces()) { +@@ -65,8 +66,7 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { } } } - } - ); + } // Paper - Perf: Remove streams from hot code - return new Beardifier(objectList.iterator(), objectList2.iterator()); + return new Beardifier(list.iterator(), list1.iterator()); } |