diff --git a/divinemc-server/minecraft-patches/features/0010-Chunk-System-Optimizations.patch b/divinemc-server/minecraft-patches/features/0010-Chunk-System-Optimizations.patch index 8d9ffb9..5c15062 100644 --- a/divinemc-server/minecraft-patches/features/0010-Chunk-System-Optimizations.patch +++ b/divinemc-server/minecraft-patches/features/0010-Chunk-System-Optimizations.patch @@ -3320,3 +3320,134 @@ index ab1dcbe416e2c3c94cfddf04b7ed053425a71806..a37eb2e29b4577ebc711e8ef7b47fbbc private Vec3i size = Vec3i.ZERO; private String author = "?"; // CraftBukkit start - data containers +diff --git a/net/minecraft/world/ticks/LevelChunkTicks.java b/net/minecraft/world/ticks/LevelChunkTicks.java +index faf45ac459f7c25309d6ef6dce371d484a0dae7b..0e862e9c0d9d562e77ce02a35deb761c28fa2f90 100644 +--- a/net/minecraft/world/ticks/LevelChunkTicks.java ++++ b/net/minecraft/world/ticks/LevelChunkTicks.java +@@ -18,10 +18,10 @@ 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 +- private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); ++ private final Queue> tickQueue = new java.util.concurrent.PriorityBlockingQueue<>(11, ScheduledTick.DRAIN_ORDER); // DivineMC - Chunk System Optimizations + @Nullable + private List> pendingTicks; +- private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Set> ticksPerPosition = it.unimi.dsi.fastutil.objects.ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); // DivineMC - Chunk System Optimizations + @Nullable + private BiConsumer, ScheduledTick> onTickAdded; + +@@ -71,10 +71,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 - Chunk System Optimizations ++ 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) { ++ net.minecraft.server.MinecraftServer.LOGGER.error("Encountered caught exception when polling chunk ticks, blocking and returning null.", e); ++ return null; + } ++ // DivineMC end - Chunk System Optimizations + + return scheduledTick; + } +@@ -87,6 +95,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + private void scheduleUnchecked(ScheduledTick tick) { ++ if (tick == null) return; // DivineMC - Chunk System Optimizations + this.tickQueue.add(tick); + if (this.onTickAdded != null) { + this.onTickAdded.accept(this, tick); +@@ -127,6 +136,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon + } + + for (ScheduledTick scheduledTick : this.tickQueue) { ++ if (scheduledTick == null) continue; // DivineMC - Chunk System Optimizations - fix NPE + list.add(scheduledTick.toSavedTick(gametime)); + } + +diff --git a/net/minecraft/world/ticks/LevelTicks.java b/net/minecraft/world/ticks/LevelTicks.java +index 0a9805d42142678ca5213c511235daa6505ddbf3..cd73f1a3ccc80e4ab1766147fccc67d81eeb4cc3 100644 +--- a/net/minecraft/world/ticks/LevelTicks.java ++++ b/net/minecraft/world/ticks/LevelTicks.java +@@ -30,17 +30,18 @@ public class LevelTicks implements LevelTickAccess { + private static final Comparator> CONTAINER_DRAIN_ORDER = (levelChunkTicks, levelChunkTicks1) -> ScheduledTick.INTRA_TICK_DRAIN_ORDER + .compare(levelChunkTicks.peek(), levelChunkTicks1.peek()); + private final LongPredicate tickCheck; +- private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); +- private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), map -> map.defaultReturnValue(Long.MAX_VALUE)); +- private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); +- private final Queue> toRunThisTick = new ArrayDeque<>(); ++ private final Long2ObjectMap> allContainers = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); // DivineMC - Chunk System Optimizations ++ private final java.util.Map nextTickForContainer = new java.util.concurrent.ConcurrentHashMap<>(); // DivineMC - Chunk System Optimizations ++ private final Queue> containersToTick = new java.util.concurrent.PriorityBlockingQueue<>(11, CONTAINER_DRAIN_ORDER); // DivineMC - Chunk System Optimizations ++ private final Queue> toRunThisTick = new java.util.concurrent.ConcurrentLinkedQueue<>(); // DivineMC - Chunk System Optimizations + private final List> alreadyRunThisTick = new ArrayList<>(); +- private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); ++ private final Set> toRunThisTickSet = com.google.common.collect.Sets.newConcurrentHashSet(); // DivineMC - Chunk System Optimizations + private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (levelChunkTicks, scheduledTick) -> { + if (scheduledTick.equals(levelChunkTicks.peek())) { + this.updateContainerScheduling(scheduledTick); + } + }; ++ private final java.util.concurrent.atomic.AtomicInteger toRunThisTickCount = new java.util.concurrent.atomic.AtomicInteger(0); // DivineMC - Chunk System Optimizations + + public LevelTicks(LongPredicate tickCheck) { + this.tickCheck = tickCheck; +@@ -90,12 +91,14 @@ public class LevelTicks implements LevelTickAccess { + } + + private void sortContainersToTick(long gameTime) { +- ObjectIterator objectIterator = Long2LongMaps.fastIterator(this.nextTickForContainer); ++ java.util.Iterator> objectIterator = this.nextTickForContainer.entrySet().iterator(); // DivineMC - Chunk System Optimizations + + while (objectIterator.hasNext()) { +- Entry entry = objectIterator.next(); +- long longKey = entry.getLongKey(); +- long longValue = entry.getLongValue(); ++ // DivineMC start - Chunk System Optimizations ++ java.util.Map.Entry entry = objectIterator.next(); ++ long longKey = entry.getKey(); ++ long longValue = entry.getValue(); ++ // DivineMC end - Chunk System Optimizations + if (longValue <= gameTime) { + LevelChunkTicks levelChunkTicks = this.allContainers.get(longKey); + if (levelChunkTicks == null) { +@@ -162,16 +165,19 @@ public class LevelTicks implements LevelTickAccess { + } + + private void scheduleForThisTick(ScheduledTick tick) { ++ if (tick == null) return; // DivineMC - Chunk System Optimizations + this.toRunThisTick.add(tick); ++ this.toRunThisTickCount.incrementAndGet(); // DivineMC - Chunk System Optimizations + } + + private boolean canScheduleMoreTicks(int maxAllowedTicks) { +- return this.toRunThisTick.size() < maxAllowedTicks; ++ return this.toRunThisTickCount.get() < maxAllowedTicks; // DivineMC - Chunk System Optimizations + } + + private void runCollectedTicks(BiConsumer ticker) { + while (!this.toRunThisTick.isEmpty()) { + ScheduledTick scheduledTick = this.toRunThisTick.poll(); ++ this.toRunThisTickCount.decrementAndGet(); // DivineMC - Chunk System Optimizations + if (!this.toRunThisTickSet.isEmpty()) { + this.toRunThisTickSet.remove(scheduledTick); + } +@@ -183,6 +189,7 @@ public class LevelTicks implements LevelTickAccess { + + private void cleanupAfterTick() { + this.toRunThisTick.clear(); ++ this.toRunThisTickCount.set(0); // DivineMC - Chunk System Optimizations + this.containersToTick.clear(); + this.alreadyRunThisTick.clear(); + this.toRunThisTickSet.clear(); diff --git a/divinemc-server/minecraft-patches/features/0012-Optimize-Fluids.patch b/divinemc-server/minecraft-patches/features/0011-Optimize-Fluids.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0012-Optimize-Fluids.patch rename to divinemc-server/minecraft-patches/features/0011-Optimize-Fluids.patch diff --git a/divinemc-server/minecraft-patches/features/0011-Optimize-hoppers.patch b/divinemc-server/minecraft-patches/features/0011-Optimize-hoppers.patch deleted file mode 100644 index 66d8efc..0000000 --- a/divinemc-server/minecraft-patches/features/0011-Optimize-hoppers.patch +++ /dev/null @@ -1,682 +0,0 @@ -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 9f7698f8ce56d5d89cf86f6ea2d5b4d51b18c9a2..351b035d1f3025af28b5147b95b912e0e2ab9212 100644 ---- a/net/minecraft/server/MinecraftServer.java -+++ b/net/minecraft/server/MinecraftServer.java -@@ -1746,7 +1746,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 - if (org.bxteam.divinemc.DivineConfig.enableParallelWorldTicking) { - serverLevelTickingSemaphore.acquire(); -diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index e17f5ba74295eb6585520ab9e5cc1cb2e3107098..15e6804a5faccb0a4f514d6b94f5856c7c15ab76 100644 ---- a/net/minecraft/server/level/ServerLevel.java -+++ b/net/minecraft/server/level/ServerLevel.java -@@ -194,7 +194,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); -@@ -1730,7 +1730,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")); - } -@@ -1761,13 +1761,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 -@@ -2654,7 +2654,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") -@@ -2724,7 +2724,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; - } diff --git a/divinemc-server/minecraft-patches/features/0013-Optimize-entity-stupid-brain.patch b/divinemc-server/minecraft-patches/features/0012-Optimize-entity-stupid-brain.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0013-Optimize-entity-stupid-brain.patch rename to divinemc-server/minecraft-patches/features/0012-Optimize-entity-stupid-brain.patch diff --git a/divinemc-server/minecraft-patches/features/0014-Clump-experience-orbs.patch b/divinemc-server/minecraft-patches/features/0013-Clump-experience-orbs.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0014-Clump-experience-orbs.patch rename to divinemc-server/minecraft-patches/features/0013-Clump-experience-orbs.patch diff --git a/divinemc-server/minecraft-patches/features/0015-Optimize-explosions.patch b/divinemc-server/minecraft-patches/features/0014-Optimize-explosions.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0015-Optimize-explosions.patch rename to divinemc-server/minecraft-patches/features/0014-Optimize-explosions.patch diff --git a/divinemc-server/minecraft-patches/features/0016-Option-to-allow-weird-movement-and-disable-teleporti.patch b/divinemc-server/minecraft-patches/features/0015-Option-to-allow-weird-movement-and-disable-teleporti.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0016-Option-to-allow-weird-movement-and-disable-teleporti.patch rename to divinemc-server/minecraft-patches/features/0015-Option-to-allow-weird-movement-and-disable-teleporti.patch diff --git a/divinemc-server/minecraft-patches/features/0017-Lag-compensation.patch b/divinemc-server/minecraft-patches/features/0016-Lag-compensation.patch similarity index 98% rename from divinemc-server/minecraft-patches/features/0017-Lag-compensation.patch rename to divinemc-server/minecraft-patches/features/0016-Lag-compensation.patch index 3f05719..78f76a9 100644 --- a/divinemc-server/minecraft-patches/features/0017-Lag-compensation.patch +++ b/divinemc-server/minecraft-patches/features/0016-Lag-compensation.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Lag compensation diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 351b035d1f3025af28b5147b95b912e0e2ab9212..1bcccba4df407ec4d53f49c3c2c7493db87b2240 100644 +index 9f7698f8ce56d5d89cf86f6ea2d5b4d51b18c9a2..11b89a625b942f5f2f882c54dbfc08c16e983425 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -289,6 +289,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop pendingBlockEntityTickers = Lists.newArrayList(); private boolean tickingBlockEntities; -@@ -1528,7 +1528,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1521,14 +1521,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + boolean runsNormally = this.tickRateManager().runsNormally(); + + int tickedEntities = 0; // Paper - rewrite chunk system +- var toRemove = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet(); // Paper - Fix MC-117075; use removeAll +- toRemove.add(null); // Paper - Fix MC-117075 + for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters + this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0; TickingBlockEntity tickingBlockEntity = this.blockEntityTickers.get(this.tileTickPosition); // Spigot end if (tickingBlockEntity.isRemoved()) { @@ -26,11 +33,12 @@ index 9608aa7231e1e19ac34f7fcf6cc4051da01f2f3e..440d890d32a6705aa5ebc84040c42906 } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { tickingBlockEntity.tick(); // DivineMC start - Parallel world ticking -@@ -1541,6 +1541,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl +@@ -1539,7 +1537,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + // DivineMC end - Parallel world ticking + } } - this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 - +- this.blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 + this.blockEntityTickers.removeMarkedEntries(); // DivineMC - optimize block entity removals - Fix MC-117075 + this.tickingBlockEntities = false; this.spigotConfig.currentPrimedTnt = 0; // Spigot - } diff --git a/divinemc-server/minecraft-patches/features/0024-Skip-distanceToSqr-call-in-ServerEntity-sendChanges-.patch b/divinemc-server/minecraft-patches/features/0023-Skip-distanceToSqr-call-in-ServerEntity-sendChanges-.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0024-Skip-distanceToSqr-call-in-ServerEntity-sendChanges-.patch rename to divinemc-server/minecraft-patches/features/0023-Skip-distanceToSqr-call-in-ServerEntity-sendChanges-.patch diff --git a/divinemc-server/minecraft-patches/features/0025-Optimize-canSee-checks.patch b/divinemc-server/minecraft-patches/features/0024-Optimize-canSee-checks.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0025-Optimize-canSee-checks.patch rename to divinemc-server/minecraft-patches/features/0024-Optimize-canSee-checks.patch diff --git a/divinemc-server/minecraft-patches/features/0026-Implement-NoChatReports.patch b/divinemc-server/minecraft-patches/features/0025-Implement-NoChatReports.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0026-Implement-NoChatReports.patch rename to divinemc-server/minecraft-patches/features/0025-Implement-NoChatReports.patch diff --git a/divinemc-server/minecraft-patches/features/0027-Optimize-Structure-Generation.patch b/divinemc-server/minecraft-patches/features/0026-Optimize-Structure-Generation.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0027-Optimize-Structure-Generation.patch rename to divinemc-server/minecraft-patches/features/0026-Optimize-Structure-Generation.patch diff --git a/divinemc-server/minecraft-patches/features/0028-Verify-Minecraft-EULA-earlier.patch b/divinemc-server/minecraft-patches/features/0027-Verify-Minecraft-EULA-earlier.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0028-Verify-Minecraft-EULA-earlier.patch rename to divinemc-server/minecraft-patches/features/0027-Verify-Minecraft-EULA-earlier.patch diff --git a/divinemc-server/minecraft-patches/features/0029-Configurable-MC-67.patch b/divinemc-server/minecraft-patches/features/0028-Configurable-MC-67.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0029-Configurable-MC-67.patch rename to divinemc-server/minecraft-patches/features/0028-Configurable-MC-67.patch diff --git a/divinemc-server/minecraft-patches/features/0030-Option-to-disable-saving-of-snowball-and-firework.patch b/divinemc-server/minecraft-patches/features/0029-Option-to-disable-saving-of-snowball-and-firework.patch similarity index 100% rename from divinemc-server/minecraft-patches/features/0030-Option-to-disable-saving-of-snowball-and-firework.patch rename to divinemc-server/minecraft-patches/features/0029-Option-to-disable-saving-of-snowball-and-firework.patch diff --git a/divinemc-server/minecraft-patches/features/0031-Catch-update-suppressors.patch b/divinemc-server/minecraft-patches/features/0030-Catch-update-suppressors.patch similarity index 98% rename from divinemc-server/minecraft-patches/features/0031-Catch-update-suppressors.patch rename to divinemc-server/minecraft-patches/features/0030-Catch-update-suppressors.patch index 8336629..093c181 100644 --- a/divinemc-server/minecraft-patches/features/0031-Catch-update-suppressors.patch +++ b/divinemc-server/minecraft-patches/features/0030-Catch-update-suppressors.patch @@ -20,7 +20,7 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..aa4dd7517e8be167aef1eaf7aa907e3c if (var4 instanceof ReportedException reportedException && reportedException.getCause() instanceof OutOfMemoryError) { throw makeReportedException(var4, packet, processor); diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 72ab570ca8b532a1f55408515f34a02aa0c79a6a..fe2d9328a7e9365f8c7e24e862038bc94ddfe1ca 100644 +index 804de864da13ae0be6a1caee88e95a19e35d08c0..5a0c8791495aa522e511918ad0a24d9bbe6b5877 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -1694,6 +1694,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop(), new com.google.common.util.concurrent.ThreadFactoryBuilder() diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index fe2d9328a7e9365f8c7e24e862038bc94ddfe1ca..daf6141a6aed6baf7b8de4030324703a0fe872d3 100644 +index 5a0c8791495aa522e511918ad0a24d9bbe6b5877..9d6fa3bced0ba5ab3443bdf4ce306598a8e4a31f 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java -@@ -2709,8 +2709,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop +Date: Sat, 12 Apr 2025 17:40:53 +0300 +Subject: [PATCH] SparklyPaper: Allow throttling hopper checks if the target + container is full + +Original project: https://github.com/SparklyPower/SparklyPaper + +diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +index 5cd1326ad5d046c88b2b3449d610a78fa880b4cd..6ad3388ac45a4d2ef85fc0fafece7de6e387f738 100644 +--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +@@ -419,6 +419,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } else { + Direction opposite = blockEntity.facing.getOpposite(); + if (isFullContainer(attachedContainer, opposite)) { ++ // DivineMC start - SparklyPaper: Allow throttling hopper checks if the target container is full ++ if (org.bxteam.divinemc.DivineConfig.hopperThrottleWhenFull && org.bxteam.divinemc.DivineConfig.hopperThrottleSkipTicks > 0) { ++ blockEntity.setCooldown(org.bxteam.divinemc.DivineConfig.hopperThrottleSkipTicks); ++ } ++ // DivineMC end - SparklyPaper: Allow throttling hopper checks if the target container is full + return false; + } else { + // Paper start - Perf: Optimize Hoppers diff --git a/divinemc-server/paper-patches/features/0006-Chunk-System-Optimizations.patch b/divinemc-server/paper-patches/features/0006-Chunk-System-Optimizations.patch index 3cb412e..e044381 100644 --- a/divinemc-server/paper-patches/features/0006-Chunk-System-Optimizations.patch +++ b/divinemc-server/paper-patches/features/0006-Chunk-System-Optimizations.patch @@ -296,6 +296,216 @@ index 2bae9949ef325d0001aa638150fbbdf968367e75..11bf4ddb298bb39f7f39a9c33c90b48a this.count = (short)0; this.map.clear(); } +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java +index 460e27ab0506c83a28934800ee74ee886d4b025e..49e53bf85352f4d1b9997fa3c30e37e40c0ff01c 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed26WayDistancePropagator3D.java +@@ -15,7 +15,7 @@ public final class Delayed26WayDistancePropagator3D { + + // Generally updates to positions are made close to other updates, so we link to decrease cache misses when + // propagating updates +- protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet(); ++ protected final it.unimi.dsi.fastutil.longs.LongSet updatedSources = it.unimi.dsi.fastutil.longs.LongSets.synchronize(new LongLinkedOpenHashSet()); // DivineMC - Chunk System Optimizations + + @FunctionalInterface + public static interface LevelChangeCallback { +@@ -94,29 +94,29 @@ public final class Delayed26WayDistancePropagator3D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelRemoveWorkQueueBitset |= (1L << level); + } + +- public boolean propagateUpdates() { ++ public synchronized boolean propagateUpdates() { // DivineMC - Chunk System Optimizations + if (this.updatedSources.isEmpty()) { + return false; + } +@@ -157,15 +157,15 @@ public final class Delayed26WayDistancePropagator3D { + return ret; + } + +- protected void propagateIncreases() { ++ protected synchronized void propagateIncreases() { // DivineMC - Chunk System Optimizations + for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset); + this.levelIncreaseWorkQueueBitset != 0L; + this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Chunk System Optimizations ++ byte level = queue.queuedLevels.removeFirst(); // DivineMC - Chunk System Optimizations + + final boolean neighbourCheck = level < 0; + +@@ -226,15 +226,15 @@ public final class Delayed26WayDistancePropagator3D { + } + } + +- protected void propagateDecreases() { ++ protected synchronized void propagateDecreases() { // DivineMC - Chunk System Optimizations + for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset); + this.levelRemoveWorkQueueBitset != 0L; + this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) { + + final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Chunk System Optimizations ++ final byte level = queue.queuedLevels.removeFirst(); // DivineMC - Chunk System Optimizations + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java +index ab2fa1563d5e32a5313dfcc1da411cab45fb5ca0..1bababc2515d8c805e4ee0c943065f451a38b7b8 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java ++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/Delayed8WayDistancePropagator2D.java +@@ -356,24 +356,24 @@ public final class Delayed8WayDistancePropagator2D { + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelIncreaseWorkQueueBitset |= (1L << level); + } + + protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) { + final WorkQueue queue = this.levelIncreaseWorkQueues[index]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelIncreaseWorkQueueBitset |= (1L << index); + } + + protected final void addToRemoveWorkQueue(final long coordinate, final byte level) { + final WorkQueue queue = this.levelRemoveWorkQueues[level]; +- queue.queuedCoordinates.enqueue(coordinate); +- queue.queuedLevels.enqueue(level); ++ queue.queuedCoordinates.add(coordinate); // DivineMC - Chunk System Optimizations ++ queue.queuedLevels.add(level); // DivineMC - Chunk System Optimizations + + this.levelRemoveWorkQueueBitset |= (1L << level); + } +@@ -426,8 +426,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Chunk System Optimizations ++ byte level = queue.queuedLevels.removeFirst(); // DivineMC - Chunk System Optimizations + + final boolean neighbourCheck = level < 0; + +@@ -492,8 +492,8 @@ public final class Delayed8WayDistancePropagator2D { + + final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex]; + while (!queue.queuedLevels.isEmpty()) { +- final long coordinate = queue.queuedCoordinates.removeFirstLong(); +- final byte level = queue.queuedLevels.removeFirstByte(); ++ final long coordinate = queue.queuedCoordinates.removeFirst(); // DivineMC - Chunk System Optimizations ++ final byte level = queue.queuedLevels.removeFirst(); // DivineMC - Chunk System Optimizations + + final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level); + if (currentLevel == 0) { +@@ -561,7 +561,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private int find(final long k) { ++ private synchronized int find(final long k) { // DivineMC - Chunk System Optimizations + if (k == 0L) { + return this.containsNullKey ? this.n : -(this.n + 1); + } else { +@@ -585,7 +585,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void insert(final int pos, final long k, final byte v) { ++ private synchronized void insert(final int pos, final long k, final byte v) { // DivineMC - Chunk System Optimizations + if (pos == this.n) { + this.containsNullKey = true; + } +@@ -598,7 +598,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- public byte putIfGreater(final long key, final byte value) { ++ public synchronized byte putIfGreater(final long key, final byte value) { // DivineMC - Chunk System Optimizations + final int pos = this.find(key); + if (pos < 0) { + if (this.defRetValue < value) { +@@ -616,7 +616,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void removeEntry(final int pos) { ++ private synchronized void removeEntry(final int pos) { // DivineMC - Chunk System Optimizations + --this.size; + this.shiftKeys(pos); + if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { +@@ -625,7 +625,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- private void removeNullEntry() { ++ private synchronized void removeNullEntry() { // DivineMC - Chunk System Optimizations + this.containsNullKey = false; + --this.size; + if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) { +@@ -634,7 +634,7 @@ public final class Delayed8WayDistancePropagator2D { + } + + // copied from superclass +- public byte removeIfGreaterOrEqual(final long key, final byte value) { ++ public synchronized byte removeIfGreaterOrEqual(final long key, final byte value) { // DivineMC - Chunk System Optimizations + if (key == 0L) { + if (!this.containsNullKey) { + return this.defRetValue; +@@ -679,8 +679,8 @@ public final class Delayed8WayDistancePropagator2D { + + protected static final class WorkQueue { + +- public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque(); +- public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque(); ++ public final java.util.concurrent.ConcurrentLinkedDeque queuedCoordinates = new java.util.concurrent.ConcurrentLinkedDeque<>(); ++ public final java.util.concurrent.ConcurrentLinkedDeque queuedLevels = new java.util.concurrent.ConcurrentLinkedDeque<>(); + + } + diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java index 632920e04686d8a0fd0a60e87348be1fe7862a3c..f10c6c156b8dd9acecc8b1ee81bd28260fb6e4d8 100644 --- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java diff --git a/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java b/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java index a143ee8..18d7ec9 100644 --- a/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java +++ b/divinemc-server/src/main/java/org/bxteam/divinemc/DivineConfig.java @@ -279,6 +279,8 @@ public class DivineConfig { public static boolean disableLeafDecay = false; public static boolean commandBlockParseResultsCaching = true; public static boolean enableAsyncSpawning = true; + public static boolean hopperThrottleWhenFull = false; + public static int hopperThrottleSkipTicks = 0; private static void miscSettings() { skipUselessSecondaryPoiSensor = getBoolean("settings.misc.skip-useless-secondary-poi-sensor", skipUselessSecondaryPoiSensor); clumpOrbs = getBoolean("settings.misc.clump-orbs", clumpOrbs, @@ -300,6 +302,11 @@ public class DivineConfig { "Caches the parse results of command blocks, can significantly reduce performance impact."); enableAsyncSpawning = getBoolean("settings.misc.enable-async-spawning", enableAsyncSpawning, "Enables optimization that will offload much of the computational effort involved with spawning new mobs to a different thread."); + + hopperThrottleWhenFull = getBoolean("settings.misc.hopper-throttle-when-full.enabled", hopperThrottleWhenFull, + "When enabled, hoppers will throttle if target container is full."); + hopperThrottleSkipTicks = getInt("settings.misc.hopper-throttle-when-full.skip-ticks", hopperThrottleSkipTicks, + "The amount of ticks to skip when the hopper is throttled."); } public static String sentryDsn = "";