From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Sat, 1 Feb 2025 00:55:34 +0300 Subject: [PATCH] Optimize hoppers diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java index 3002ed51a579e81c3266da79a5dd38bac0b4a39c..9fdd4bbbe0e4c75880e9f24741fea7c60c57d09a 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -1789,7 +1789,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - BlockPhysicsEvent serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent serverLevel.updateLagCompensationTick(); // Paper - lag compensation - 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 serverLevel.hasRidableMoveEvent = org.purpurmc.purpur.event.entity.RidableMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Purpur - Ridables profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().location()); diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index a3f363d0c86142e03edc7fc6e2ff6ed81de8ed65..0de0570397a6c6de43b1143b0d722af7a6093c8e 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -195,7 +195,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private final LevelTicks fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded); private final PathTypeCache pathTypesByPosCache = new PathTypeCache(); final Set navigatingMobs = new ObjectOpenHashSet<>(); - volatile boolean isUpdatingNavigations; + final java.util.concurrent.atomic.AtomicBoolean isUpdatingNavigations = new java.util.concurrent.atomic.AtomicBoolean(false); // DivineMC - Optimize Hoppers protected final Raids raids; private final ObjectLinkedOpenHashSet blockEvents = new ObjectLinkedOpenHashSet<>(); private final List blockEventsToReschedule = new ArrayList<>(64); @@ -1770,7 +1770,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @Override public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { - if (this.isUpdatingNavigations) { + if (this.isUpdatingNavigations.get() && false) { // DivineMC String string = "recursive call to sendBlockUpdated"; Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); } @@ -1801,13 +1801,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - catch CME see below why try { - this.isUpdatingNavigations = true; + this.isUpdatingNavigations.set(true); // DivineMC for (PathNavigation pathNavigation : list) { pathNavigation.recomputePath(); } } finally { - this.isUpdatingNavigations = false; + this.isUpdatingNavigations.set(false); // DivineMC } } } // Paper - option to disable pathfinding updates @@ -2699,7 +2699,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } if (entity instanceof Mob mob) { - if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning + if (false && ServerLevel.this.isUpdatingNavigations.get()) { // Paper - Remove unnecessary onTrackingStart during navigation warning // DivineMC String string = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") @@ -2769,7 +2769,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } if (entity instanceof Mob mob) { - if (false && ServerLevel.this.isUpdatingNavigations) { // Paper - Remove unnecessary onTrackingStart during navigation warning + if (false && ServerLevel.this.isUpdatingNavigations.get()) { // Paper - Remove unnecessary onTrackingStart during navigation warning // DivineMC String string = "onTrackingStart called during navigation iteration"; Util.logAndPauseIfInIde( "onTrackingStart called during navigation iteration", new IllegalStateException("onTrackingStart called during navigation iteration") diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java index 5cd1326ad5d046c88b2b3449d610a78fa880b4cd..a0ee6ad6e7a6791605191d20d742e16cc9857a60 100644 --- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -139,56 +139,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } } - // Paper start - Perf: Optimize Hoppers - private static final int HOPPER_EMPTY = 0; - private static final int HOPPER_HAS_ITEMS = 1; - private static final int HOPPER_IS_FULL = 2; - - private static int getFullState(final HopperBlockEntity hopper) { - hopper.unpackLootTable(null); - - final List hopperItems = hopper.items; - - boolean empty = true; - boolean full = true; - - for (int i = 0, len = hopperItems.size(); i < len; ++i) { - final ItemStack stack = hopperItems.get(i); - if (stack.isEmpty()) { - full = false; - continue; - } - - if (!full) { - // can't be full - return HOPPER_HAS_ITEMS; - } - - empty = false; - - if (stack.getCount() != stack.getMaxStackSize()) { - // can't be full or empty - return HOPPER_HAS_ITEMS; - } - } - - return empty ? HOPPER_EMPTY : (full ? HOPPER_IS_FULL : HOPPER_HAS_ITEMS); - } - // Paper end - Perf: Optimize Hoppers - private static boolean tryMoveItems(Level level, BlockPos pos, BlockState state, HopperBlockEntity blockEntity, BooleanSupplier validator) { if (level.isClientSide) { return false; } else { if (!blockEntity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) { boolean flag = false; - final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers - if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers + if (!blockEntity.isEmpty()) { // DivineMC - Optimize hoppers flag = ejectItems(level, pos, blockEntity); } - if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers - flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items + if (!blockEntity.inventoryFull()) { // DivineMC - Optimize hoppers + flag |= validator.getAsBoolean(); // DivineMC - Optimize hoppers } if (flag) { @@ -212,206 +174,6 @@ 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; - - private static boolean hopperPush(final Level level, final Container destination, final Direction direction, final HopperBlockEntity hopper) { - skipPushModeEventFire = skipHopperEvents; - boolean foundItem = false; - for (int i = 0; i < hopper.getContainerSize(); ++i) { - final ItemStack item = hopper.getItem(i); - if (!item.isEmpty()) { - foundItem = true; - ItemStack origItemStack = item; - ItemStack movedItem = origItemStack; - - final int originalItemCount = origItemStack.getCount(); - final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount); - origItemStack.setCount(movedItemCount); - - // We only need to fire the event once to give protection plugins a chance to cancel this event - // Because nothing uses getItem, every event call should end up the same result. - if (!skipPushModeEventFire) { - movedItem = callPushMoveEvent(destination, movedItem, hopper); - if (movedItem == null) { // cancelled - origItemStack.setCount(originalItemCount); - return false; - } - } - - final ItemStack remainingItem = addItem(hopper, destination, movedItem, direction); - final int remainingItemCount = remainingItem.getCount(); - if (remainingItemCount != movedItemCount) { - origItemStack = origItemStack.copy(true); - origItemStack.setCount(originalItemCount); - if (!origItemStack.isEmpty()) { - origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount); - } - hopper.setItem(i, origItemStack); - destination.setChanged(); - return true; - } - origItemStack.setCount(originalItemCount); - } - } - if (foundItem && level.paperConfig().hopper.cooldownWhenFull) { // Inventory was full - cooldown - hopper.setCooldown(level.spigotConfig.hopperTransfer); - } - return false; - } - - private static boolean hopperPull(final Level level, final Hopper hopper, final Container container, ItemStack origItemStack, final int i) { - ItemStack movedItem = origItemStack; - final int originalItemCount = origItemStack.getCount(); - final int movedItemCount = Math.min(level.spigotConfig.hopperAmount, originalItemCount); - container.setChanged(); // original logic always marks source inv as changed even if no move happens. - movedItem.setCount(movedItemCount); - - if (!skipPullModeEventFire) { - movedItem = callPullMoveEvent(hopper, container, movedItem); - if (movedItem == null) { // cancelled - origItemStack.setCount(originalItemCount); - // Drastically improve performance by returning true. - // No plugin could have relied on the behavior of false as the other call - // site for IMIE did not exhibit the same behavior - return true; - } - } - - final ItemStack remainingItem = addItem(container, hopper, movedItem, null); - final int remainingItemCount = remainingItem.getCount(); - if (remainingItemCount != movedItemCount) { - origItemStack = origItemStack.copy(true); - origItemStack.setCount(originalItemCount); - if (!origItemStack.isEmpty()) { - origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount); - } - - ignoreBlockEntityUpdates = true; - container.setItem(i, origItemStack); - ignoreBlockEntityUpdates = false; - container.setChanged(); - return true; - } - origItemStack.setCount(originalItemCount); - - if (level.paperConfig().hopper.cooldownWhenFull) { - applyCooldown(hopper); - } - - return false; - } - - @Nullable - 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) { - applyCooldown(hopper); - return null; - } - - if (event.calledSetItem) { - return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); - } else { - return itemStack; - } - } - - @Nullable - private static ItemStack callPullMoveEvent(final Hopper hopper, final Container container, final ItemStack itemstack) { - 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, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack), destination, false); - final boolean result = event.callEvent(); - if (!event.calledGetItem && !event.calledSetItem) { - skipPullModeEventFire = true; - } - if (!result) { - applyCooldown(hopper); - return null; - } - - if (event.calledSetItem) { - return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); - } else { - return itemstack; - } - } - - 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) { - sourceInventory = blockEntity.getOwner(false).getInventory(); - } else if (container.getOwner() != null) { - sourceInventory = container.getOwner().getInventory(); - } else { - sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); - } - return sourceInventory; - } - - 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 container, Direction direction, java.util.function.BiPredicate test) { - if (container instanceof WorldlyContainer) { - for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) { - if (!test.test(container.getItem(slot), slot)) { - return false; - } - } - } else { - int size = container.getContainerSize(); - for (int slot = 0; slot < size; slot++) { - if (!test.test(container.getItem(slot), slot)) { - return false; - } - } - } - return true; - } - - private static boolean anyMatch(Container container, Direction direction, java.util.function.BiPredicate test) { - if (container instanceof WorldlyContainer) { - for (int slot : ((WorldlyContainer) container).getSlotsForFace(direction)) { - if (test.test(container.getItem(slot), slot)) { - return true; - } - } - } else { - 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 STACK_SIZE_TEST = (itemStack, i) -> itemStack.getCount() >= itemStack.getMaxStackSize(); - private static final java.util.function.BiPredicate IS_EMPTY_TEST = (itemStack, i) -> itemStack.isEmpty(); - // Paper end - Perf: Optimize Hoppers - private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) { Container attachedContainer = getAttachedContainer(level, pos, blockEntity); if (attachedContainer == null) { @@ -421,60 +183,59 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen if (isFullContainer(attachedContainer, opposite)) { return false; } else { - // Paper start - Perf: Optimize Hoppers - 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 = item.copy(); - // org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( - // blockEntity.removeItem(i, level.spigotConfig.hopperAmount) - // ); // Spigot - - // org.bukkit.inventory.Inventory destinationInventory; - // // Have to special case large chests as they work oddly - // 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 org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); - // } - - // 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(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot - // return false; - // } - // int origCount = event.getItem().getAmount(); // Spigot - // ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); - // // CraftBukkit end - - // if (itemStack.isEmpty()) { - // attachedContainer.setChanged(); - // return true; - // } - - // item.setCount(count); - // // Spigot start - // item.shrink(origCount - itemStack.getCount()); - // if (count <= level.spigotConfig.hopperAmount) { - // // Spigot end - // blockEntity.setItem(i, item); - // } - // } - //} - - //return false; - // Paper end - Perf: Optimize Hoppers + // DivineMC start - Optimize hoppers + 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 = item.copy(); + org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( + blockEntity.removeItem(i, level.spigotConfig.hopperAmount) + ); // Spigot + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly + 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 org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); + } + + 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(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); + // CraftBukkit end + + if (itemStack.isEmpty()) { + attachedContainer.setChanged(); + return true; + } + + item.setCount(count); + // Spigot start + item.shrink(origCount - itemStack.getCount()); + if (count <= level.spigotConfig.hopperAmount) { + // Spigot end + blockEntity.setItem(i, item); + } + } + } + + return false; + // DivineMC end - Optimize hoppers } } } @@ -529,7 +290,6 @@ 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 for (int i : getSlots(sourceContainer, direction)) { if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot @@ -555,59 +315,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)) { - // Paper start - Perf: Optimize Hoppers - 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 + // DivineMC start - Optimize hoppers + 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); + } } + // DivineMC end - Optimize hoppers return false; } @@ -615,15 +374,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen public static boolean addItem(Container container, ItemEntity item) { boolean flag = false; // CraftBukkit start - 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( - getInventory(container), (org.bukkit.entity.Item) item.getBukkitEntity() // Paper - Perf: Optimize Hoppers; use getInventory() to avoid snapshot creation + container.getOwner().getInventory(), (org.bukkit.entity.Item) item.getBukkitEntity() // DivineMC - Optimize hoppers ); if (!event.callEvent()) { return false; } // CraftBukkit end - } // Paper - Perf: Optimize Hoppers ItemStack itemStack = item.getItem().copy(); ItemStack itemStack1 = addItem(null, container, itemStack, null); if (itemStack1.isEmpty()) { @@ -678,9 +435,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen stack = stack.split(destination.getMaxStackSize()); } // Spigot end - 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 (canMergeItems(item, stack)) { @@ -768,19 +523,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Nullable 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, true); // Paper - Optimize hoppers + return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); // DivineMC - Optimize hoppers } @Nullable private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z) { - // Paper start - Perf: Optimize Hoppers - return HopperBlockEntity.getContainerAt(level, pos, state, x, y, z, false); - } - @Nullable - 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 blockContainer = getBlockContainer(level, pos, state); - if (blockContainer == null && (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers + if (blockContainer == null) { // DivineMC - Optimize hoppers blockContainer = getEntityContainer(level, x, y, z); } @@ -806,14 +555,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Nullable private static Container getEntityContainer(Level level, double x, double y, double z) { - List 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 - ); + List 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 + ); // DivineMC - Optimize hoppers return !entities.isEmpty() ? (Container)entities.get(level.random.nextInt(entities.size())) : null; } private static boolean canMergeItems(ItemStack stack1, ItemStack stack2) { - return stack1.getCount() < stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); // Paper - Perf: Optimize Hoppers; used to return true for full itemstacks?! + return stack1.getCount() <= stack1.getMaxStackSize() && ItemStack.isSameItemSameComponents(stack1, stack2); // DivineMC - Optimize hoppers } @Override diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java index faf45ac459f7c25309d6ef6dce371d484a0dae7b..6f0d1b28a45b93c51c5476283f1629a86e3420d1 100644 --- a/net/minecraft/world/ticks/LevelChunkTicks.java +++ b/net/minecraft/world/ticks/LevelChunkTicks.java @@ -17,7 +17,8 @@ import net.minecraft.core.BlockPos; import net.minecraft.nbt.ListTag; import net.minecraft.world.level.ChunkPos; -public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // Paper - rewrite chunk system +public class LevelChunkTicks implements SerializableTickContainer, TickContainerAccess, ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks { // DivineMC + private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LevelChunkTicks.class); // Paper - rewrite chunk system private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); @Nullable private List> pendingTicks; @@ -71,10 +72,18 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon @Nullable public ScheduledTick poll() { - ScheduledTick scheduledTick = this.tickQueue.poll(); - if (scheduledTick != null) { - this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system + // DivineMC start - catch exceptions when polling chunk ticks + ScheduledTick scheduledTick = null; + try { + scheduledTick = this.tickQueue.poll(); + if (scheduledTick != null) { + this.ticksPerPosition.remove(scheduledTick); this.dirty = true; // Paper - rewrite chunk system + } + } catch (Exception e) { + log.error("Encountered caught exception when polling chunk ticks, blocking and returning null.", e); + return null; } + // DivineMC end - catch exceptions when polling chunk ticks return scheduledTick; }