aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNassim Jahnke <[email protected]>2024-12-17 11:00:56 +0100
committerNassim Jahnke <[email protected]>2024-12-17 11:00:56 +0100
commit6f26d535826c51d5d3da2ffbeaa7344f581938f6 (patch)
treef212f42711750a8941f2db59b971d2091f4c3ca2
parente7995023f12b5eef09624c79032f26659d498c77 (diff)
downloadPaper-6f26d535826c51d5d3da2ffbeaa7344f581938f6.tar.gz
Paper-6f26d535826c51d5d3da2ffbeaa7344f581938f6.zip
Update hopper optimization patch
-rw-r--r--feature-patches/1048-Optimize-Hoppers.patch594
-rw-r--r--feature-patches/1051-Remove-streams-from-hot-code.patch168
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());
}