From f6aa7635f3a969ca833b7771d79bd07169d43bf9 Mon Sep 17 00:00:00 2001 From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Fri, 14 Mar 2025 15:54:02 -0400 Subject: [PATCH] [ci skip] cleanup (14/14) --- ...ty-to-pre-populate-the-size-of-ticki.patch | 24 ++++++ ...pre-filtered-ticking-chunks-list-as.patch} | 18 ++-- ...writeLongArray-during-chunk-loading.patch} | 0 ...ze-AABB.patch => 0130-Optimize-AABB.patch} | 0 ...1-Improve-sorting-in-SortedArraySet.patch} | 0 ... 0132-Make-removeIf-slightly-faster.patch} | 0 ...atch => 0133-Optimize-LinearPalette.patch} | 0 ...er-id-map-with-optimized-collection.patch} | 0 ...135-Slightly-optimized-VarInt-write.patch} | 0 ...te-ClientboundLightUpdatePacketData.patch} | 0 ...imizations-on-SerializableChunkData.patch} | 20 +++-- ...h => 0138-Rework-ChunkHolderManager.patch} | 19 +++-- ....patch => 0139-Optimize-chunkUnload.patch} | 83 +++++++++---------- ...d.patch => 0140-Async-chunk-sending.patch} | 60 +++++--------- ...atch => 0141-Spawner-Configurations.patch} | 0 .../dreeam/leaf/async/AsyncChunkSending.java | 9 ++ 16 files changed, 123 insertions(+), 110 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0127-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch rename leaf-server/minecraft-patches/features/{0127-ensureCapacity-with-collectTickingChunks.patch => 0128-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch} (54%) rename leaf-server/minecraft-patches/features/{0128-Bulk-writes-to-writeLongArray-during-chunk-loading.patch => 0129-Bulk-writes-to-writeLongArray-during-chunk-loading.patch} (100%) rename leaf-server/minecraft-patches/features/{0129-Optimize-AABB.patch => 0130-Optimize-AABB.patch} (100%) rename leaf-server/minecraft-patches/features/{0130-Improve-sorting-in-SortedArraySet.patch => 0131-Improve-sorting-in-SortedArraySet.patch} (100%) rename leaf-server/minecraft-patches/features/{0131-Make-removeIf-slightly-faster.patch => 0132-Make-removeIf-slightly-faster.patch} (100%) rename leaf-server/minecraft-patches/features/{0132-Optimize-LinearPalette.patch => 0133-Optimize-LinearPalette.patch} (100%) rename leaf-server/minecraft-patches/features/{0133-Replace-IdMapper-id-map-with-optimized-collection.patch => 0134-Replace-IdMapper-id-map-with-optimized-collection.patch} (100%) rename leaf-server/minecraft-patches/features/{0134-Slightly-optimized-VarInt-write.patch => 0135-Slightly-optimized-VarInt-write.patch} (100%) rename leaf-server/minecraft-patches/features/{0135-Rewrite-ClientboundLightUpdatePacketData.patch => 0136-Rewrite-ClientboundLightUpdatePacketData.patch} (100%) rename leaf-server/minecraft-patches/features/{0136-Some-Optimizations-on-SerializableChunkData.patch => 0137-Some-Optimizations-on-SerializableChunkData.patch} (82%) rename leaf-server/minecraft-patches/features/{0137-Rework-ChunkHolderManager.patch => 0138-Rework-ChunkHolderManager.patch} (90%) rename leaf-server/minecraft-patches/features/{0138-Optimize-chunkUnload.patch => 0139-Optimize-chunkUnload.patch} (80%) rename leaf-server/minecraft-patches/features/{0139-Async-ChunkSend.patch => 0140-Async-chunk-sending.patch} (69%) rename leaf-server/minecraft-patches/features/{0140-Spawner-Configurations.patch => 0141-Spawner-Configurations.patch} (100%) create mode 100644 leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java diff --git a/leaf-server/minecraft-patches/features/0127-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch b/leaf-server/minecraft-patches/features/0127-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch new file mode 100644 index 00000000..fb95a3e9 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0127-Use-ensureCapacity-to-pre-populate-the-size-of-ticki.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 16 Feb 2025 01:13:04 +0100 +Subject: [PATCH] Use ensureCapacity to pre-populate the size of ticking chunks + list output + + +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 6735f9e23c8972b7cf1438a2f3b49d780c1ff78c..c80464d333bd37a9b8bc7cea2291c8c72e6f9bd6 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -585,7 +585,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked(); + final int size = tickingChunks.size(); + +- final ChunkMap chunkMap = this.chunkMap; ++ // Leaf start - Use ensureCapacity to pre-populate the size of ticking chunks list output ++ if (output instanceof ArrayList arrayList) { ++ arrayList.ensureCapacity(size); ++ } ++ // Leaf end - Use ensureCapacity to pre-populate the size of ticking chunks list output + + for (int i = 0; i < size; ++i) { + final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i]; diff --git a/leaf-server/minecraft-patches/features/0127-ensureCapacity-with-collectTickingChunks.patch b/leaf-server/minecraft-patches/features/0128-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch similarity index 54% rename from leaf-server/minecraft-patches/features/0127-ensureCapacity-with-collectTickingChunks.patch rename to leaf-server/minecraft-patches/features/0128-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch index ae006c26..7dbac5ad 100644 --- a/leaf-server/minecraft-patches/features/0127-ensureCapacity-with-collectTickingChunks.patch +++ b/leaf-server/minecraft-patches/features/0128-Directly-use-the-pre-filtered-ticking-chunks-list-as.patch @@ -1,22 +1,16 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 16 Feb 2025 01:13:04 +0100 -Subject: [PATCH] ensureCapacity with collectTickingChunks +Subject: [PATCH] Directly use the pre-filtered ticking chunks list as the + output diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java -index 6735f9e23c8972b7cf1438a2f3b49d780c1ff78c..a3afd10ef6bdc7439cb92af97f7336d6331e26a0 100644 +index c80464d333bd37a9b8bc7cea2291c8c72e6f9bd6..b1f1b596a597d559aa672a3cb46a03917ad746af 100644 --- a/net/minecraft/server/level/ServerChunkCache.java +++ b/net/minecraft/server/level/ServerChunkCache.java -@@ -585,17 +585,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked(); - final int size = tickingChunks.size(); - -- final ChunkMap chunkMap = this.chunkMap; -+ // Directly add all pre-filtered ticking chunks to output -+ if (output instanceof ArrayList arrayList) { -+ arrayList.ensureCapacity(size); -+ } +@@ -592,14 +592,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + // Leaf end - Use ensureCapacity to pre-populate the size of ticking chunks list output for (int i = 0; i < size; ++i) { - final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i]; @@ -27,7 +21,7 @@ index 6735f9e23c8972b7cf1438a2f3b49d780c1ff78c..a3afd10ef6bdc7439cb92af97f7336d6 - } - - output.add(levelChunk); -+ output.add(raw[i].chunk()); ++ output.add(raw[i].chunk()); // Leaf - Directly use the pre-filtered ticking chunks list as the output } // Paper end - chunk tick iteration optimisation } diff --git a/leaf-server/minecraft-patches/features/0128-Bulk-writes-to-writeLongArray-during-chunk-loading.patch b/leaf-server/minecraft-patches/features/0129-Bulk-writes-to-writeLongArray-during-chunk-loading.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0128-Bulk-writes-to-writeLongArray-during-chunk-loading.patch rename to leaf-server/minecraft-patches/features/0129-Bulk-writes-to-writeLongArray-during-chunk-loading.patch diff --git a/leaf-server/minecraft-patches/features/0129-Optimize-AABB.patch b/leaf-server/minecraft-patches/features/0130-Optimize-AABB.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0129-Optimize-AABB.patch rename to leaf-server/minecraft-patches/features/0130-Optimize-AABB.patch diff --git a/leaf-server/minecraft-patches/features/0130-Improve-sorting-in-SortedArraySet.patch b/leaf-server/minecraft-patches/features/0131-Improve-sorting-in-SortedArraySet.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0130-Improve-sorting-in-SortedArraySet.patch rename to leaf-server/minecraft-patches/features/0131-Improve-sorting-in-SortedArraySet.patch diff --git a/leaf-server/minecraft-patches/features/0131-Make-removeIf-slightly-faster.patch b/leaf-server/minecraft-patches/features/0132-Make-removeIf-slightly-faster.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0131-Make-removeIf-slightly-faster.patch rename to leaf-server/minecraft-patches/features/0132-Make-removeIf-slightly-faster.patch diff --git a/leaf-server/minecraft-patches/features/0132-Optimize-LinearPalette.patch b/leaf-server/minecraft-patches/features/0133-Optimize-LinearPalette.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0132-Optimize-LinearPalette.patch rename to leaf-server/minecraft-patches/features/0133-Optimize-LinearPalette.patch diff --git a/leaf-server/minecraft-patches/features/0133-Replace-IdMapper-id-map-with-optimized-collection.patch b/leaf-server/minecraft-patches/features/0134-Replace-IdMapper-id-map-with-optimized-collection.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0133-Replace-IdMapper-id-map-with-optimized-collection.patch rename to leaf-server/minecraft-patches/features/0134-Replace-IdMapper-id-map-with-optimized-collection.patch diff --git a/leaf-server/minecraft-patches/features/0134-Slightly-optimized-VarInt-write.patch b/leaf-server/minecraft-patches/features/0135-Slightly-optimized-VarInt-write.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0134-Slightly-optimized-VarInt-write.patch rename to leaf-server/minecraft-patches/features/0135-Slightly-optimized-VarInt-write.patch diff --git a/leaf-server/minecraft-patches/features/0135-Rewrite-ClientboundLightUpdatePacketData.patch b/leaf-server/minecraft-patches/features/0136-Rewrite-ClientboundLightUpdatePacketData.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0135-Rewrite-ClientboundLightUpdatePacketData.patch rename to leaf-server/minecraft-patches/features/0136-Rewrite-ClientboundLightUpdatePacketData.patch diff --git a/leaf-server/minecraft-patches/features/0136-Some-Optimizations-on-SerializableChunkData.patch b/leaf-server/minecraft-patches/features/0137-Some-Optimizations-on-SerializableChunkData.patch similarity index 82% rename from leaf-server/minecraft-patches/features/0136-Some-Optimizations-on-SerializableChunkData.patch rename to leaf-server/minecraft-patches/features/0137-Some-Optimizations-on-SerializableChunkData.patch index 10c295fc..126a1d8c 100644 --- a/leaf-server/minecraft-patches/features/0136-Some-Optimizations-on-SerializableChunkData.patch +++ b/leaf-server/minecraft-patches/features/0137-Some-Optimizations-on-SerializableChunkData.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Some Optimizations on SerializableChunkData diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c10ed10dd843bfa12be3f80a244cda94f8c56807 100644 +index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c0939c311c554a4660b80725294663bab7915733 100644 --- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java +++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java -@@ -502,14 +502,14 @@ public record SerializableChunkData( +@@ -502,14 +502,16 @@ public record SerializableChunkData( throw new IllegalArgumentException("Chunk can't be serialized: " + chunk); } else { ChunkPos pos = chunk.getPos(); @@ -20,18 +20,20 @@ index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c10ed10dd843bfa12be3f80a244cda94 final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level); final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level); final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level); ++ // Leaf start - Some Optimizations on SerializableChunkData + // Pre-allocate with correct capacity to avoid resizing + final int expectedSectionCount = maxLightSection - minLightSection + 1; + List list = new ArrayList<>(expectedSectionCount); ++ // Leaf end - Some Optimizations on SerializableChunkData final LevelChunkSection[] chunkSections = chunk.getSections(); final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles(); -@@ -541,10 +541,11 @@ public record SerializableChunkData( +@@ -541,10 +543,11 @@ public record SerializableChunkData( ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state); } - sectionsList.add(sectionData); -+ list.add(sectionData); ++ list.add(sectionData); // Leaf - Some Optimizations on SerializableChunkData } // Paper end - starlight @@ -39,11 +41,12 @@ index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c10ed10dd843bfa12be3f80a244cda94 List list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size()); for (BlockPos blockPos : chunk.getBlockEntitiesPos()) { -@@ -554,7 +555,14 @@ public record SerializableChunkData( +@@ -554,7 +557,16 @@ public record SerializableChunkData( } } - List list2 = new ArrayList<>(); ++ // Leaf start - Some Optimizations on SerializableChunkData + // For entities, use an initial estimated capacity if it's a ProtoChunk + int entityEstimate = 64; // Reasonable default size + if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) { @@ -51,16 +54,17 @@ index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c10ed10dd843bfa12be3f80a244cda94 + entityEstimate = Math.max(16, protoChunk.getEntities().size()); + } + List list2 = new ArrayList<>(entityEstimate); ++ // Leaf end - Some Optimizations on SerializableChunkData + long[] longs = null; if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) { ProtoChunk protoChunk = (ProtoChunk)chunk; -@@ -570,14 +578,16 @@ public record SerializableChunkData( +@@ -570,14 +582,18 @@ public record SerializableChunkData( for (Entry entry : chunk.getHeightmaps()) { if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) { long[] rawData = entry.getValue().getRawData(); - map.put(entry.getKey(), (long[])rawData.clone()); -+ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); ++ map.put(entry.getKey(), Arrays.copyOf(rawData, rawData.length)); // Leaf - Some Optimizations on SerializableChunkData } } @@ -68,11 +72,13 @@ index 6b6aaeca14178b5b709e20ae13552d42217f15c0..c10ed10dd843bfa12be3f80a244cda94 - ShortList[] lists = Arrays.stream(chunk.getPostProcessing()) - .map(list3 -> list3 != null ? new ShortArrayList(list3) : null) - .toArray(ShortList[]::new); ++ // Leaf start - Some Optimizations on SerializableChunkData + ShortList[] postProcessing = chunk.getPostProcessing(); + ShortList[] lists = new ShortList[postProcessing.length]; + for (int i = 0; i < postProcessing.length; i++) { + lists[i] = postProcessing[i] != null ? new ShortArrayList(postProcessing[i]) : null; + } ++ // Leaf end - Some Optimizations on SerializableChunkData CompoundTag compoundTag = packStructureData( StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences() ); diff --git a/leaf-server/minecraft-patches/features/0137-Rework-ChunkHolderManager.patch b/leaf-server/minecraft-patches/features/0138-Rework-ChunkHolderManager.patch similarity index 90% rename from leaf-server/minecraft-patches/features/0137-Rework-ChunkHolderManager.patch rename to leaf-server/minecraft-patches/features/0138-Rework-ChunkHolderManager.patch index 939898f9..a95feb8d 100644 --- a/leaf-server/minecraft-patches/features/0137-Rework-ChunkHolderManager.patch +++ b/leaf-server/minecraft-patches/features/0138-Rework-ChunkHolderManager.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Rework ChunkHolderManager diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -index be820c6093dd2ae7642b9bee11edf65e3a8d7242..29872fa10ec833ff9391fc09df034204f092f8a6 100644 +index be820c6093dd2ae7642b9bee11edf65e3a8d7242..d6a30d6735d24f24a8108b6a5d15725587bb662a 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java -@@ -736,24 +736,19 @@ public final class ChunkHolderManager { +@@ -736,24 +736,20 @@ public final class ChunkHolderManager { final int sectionShift = ((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift(); @@ -22,6 +22,7 @@ index be820c6093dd2ae7642b9bee11edf65e3a8d7242..29872fa10ec833ff9391fc09df034204 - return removeDelay <= 0L; - }; - ++ // Leaf start - Rework ChunkHolderManager + // Collect sections to process first to avoid concurrent modification issues + List sectionKeys = new ArrayList<>(); for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { @@ -40,7 +41,7 @@ index be820c6093dd2ae7642b9bee11edf65e3a8d7242..29872fa10ec833ff9391fc09df034204 final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock( CoordinateUtils.getChunkX(sectionKey) << sectionShift, CoordinateUtils.getChunkZ(sectionKey) << sectionShift -@@ -761,45 +756,66 @@ public final class ChunkHolderManager { +@@ -761,11 +757,15 @@ public final class ChunkHolderManager { try { final Long2IntOpenHashMap chunkToExpireCount = this.sectionToChunkToExpireCount.get(sectionKey); @@ -57,8 +58,8 @@ index be820c6093dd2ae7642b9bee11edf65e3a8d7242..29872fa10ec833ff9391fc09df034204 + // Process each chunk in this section for (final Iterator iterator1 = chunkToExpireCount.long2IntEntrySet().fastIterator(); iterator1.hasNext();) { final Long2IntMap.Entry entry = iterator1.next(); -- - final long chunkKey = entry.getLongKey(); + +@@ -773,33 +773,51 @@ public final class ChunkHolderManager { final int expireCount = entry.getIntValue(); final SortedArraySet> tickets = this.tickets.get(chunkKey); @@ -123,3 +124,11 @@ index be820c6093dd2ae7642b9bee11edf65e3a8d7242..29872fa10ec833ff9391fc09df034204 if (chunkToExpireCount.isEmpty()) { this.sectionToChunkToExpireCount.remove(sectionKey); } +@@ -807,6 +825,7 @@ public final class ChunkHolderManager { + this.ticketLockArea.unlock(ticketLock); + } + } ++ // Leaf end - Rework ChunkHolderManager + + this.processTicketUpdates(); + } diff --git a/leaf-server/minecraft-patches/features/0138-Optimize-chunkUnload.patch b/leaf-server/minecraft-patches/features/0139-Optimize-chunkUnload.patch similarity index 80% rename from leaf-server/minecraft-patches/features/0138-Optimize-chunkUnload.patch rename to leaf-server/minecraft-patches/features/0139-Optimize-chunkUnload.patch index 61e01a88..ce7a01ab 100644 --- a/leaf-server/minecraft-patches/features/0138-Optimize-chunkUnload.patch +++ b/leaf-server/minecraft-patches/features/0139-Optimize-chunkUnload.patch @@ -5,59 +5,51 @@ Subject: [PATCH] Optimize chunkUnload diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java -index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff999df5028 100644 +index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..bed3a64388bb43e47c2ba4e67f7dde5b990d9578 100644 --- a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java +++ b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java -@@ -3,6 +3,8 @@ package ca.spottedleaf.moonrise.patches.starlight.light; - import net.minecraft.world.level.chunk.DataLayer; - import java.util.ArrayDeque; - import java.util.Arrays; -+import java.util.Map; -+import java.util.WeakHashMap; - - // SWMR -> Single Writer Multi Reader Nibble Array - public final class SWMRNibbleArray { -@@ -22,21 +24,35 @@ public final class SWMRNibbleArray { +@@ -22,12 +22,24 @@ public final class SWMRNibbleArray { protected static final int INIT_STATE_INIT = 2; // initialised protected static final int INIT_STATE_HIDDEN = 3; // initialised, but conversion to Vanilla data should be treated as if NULL ++ // Leaf start - Optimize chunkUnload + private volatile boolean cachedIsAllZero = false; + private boolean cachedIsAllZeroValid = false; + -+ private static final ThreadLocal SAVE_STATE_CACHE = -+ ThreadLocal.withInitial(() -> new SaveState[4]); ++ private static final ThreadLocal SAVE_STATE_CACHE = ThreadLocal.withInitial(() -> new SaveState[4]); + public static final int ARRAY_SIZE = 16 * 16 * 16 / (8/4); // blocks / bytes per block // this allows us to maintain only 1 byte array when we're not updating - static final ThreadLocal> WORKING_BYTES_POOL = ThreadLocal.withInitial(ArrayDeque::new); -+ static final ThreadLocal> WORKING_BYTES_POOL = new ThreadLocal>() { -+ @Override -+ protected ArrayDeque initialValue() { -+ return new ArrayDeque(8); // Limit pool size to avoid memory leaks -+ } -+ }; ++ static final ThreadLocal> WORKING_BYTES_POOL = ThreadLocal.withInitial(() -> { ++ return new ArrayDeque<>(8); // Limit pool size to avoid memory leaks ++ }); ++ // Leaf end - Optimize chunkUnload private static byte[] allocateBytes() { - final byte[] inPool = WORKING_BYTES_POOL.get().pollFirst(); ++ // Leaf start - Optimize chunkUnload + final ArrayDeque queue = WORKING_BYTES_POOL.get(); + final byte[] inPool = queue.pollFirst(); ++ // Leaf end - Optimize chunkUnload if (inPool != null) { return inPool; } -- - return new byte[ARRAY_SIZE]; +@@ -36,7 +48,12 @@ public final class SWMRNibbleArray { } private static void freeBytes(final byte[] bytes) { - WORKING_BYTES_POOL.get().addFirst(bytes); ++ // Leaf start - Optimize chunkUnload + final ArrayDeque queue = WORKING_BYTES_POOL.get(); + if (queue.size() < 8) { // Limit pool size to prevent memory leaks + queue.addFirst(bytes); + } ++ // Leaf end - Optimize chunkUnload } public static SWMRNibbleArray fromVanilla(final DataLayer nibble) { -@@ -131,15 +147,44 @@ public final class SWMRNibbleArray { +@@ -131,15 +148,44 @@ public final class SWMRNibbleArray { public SaveState getSaveState() { synchronized (this) { final int state = this.stateVisible; @@ -65,9 +57,9 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff9 if (state == INIT_STATE_NULL) { return null; } -+ if (state == INIT_STATE_UNINIT) { - return new SaveState(null, state); ++ // Leaf start - Optimize chunkUnload + // Use array-based cache instead of WeakHashMap + SaveState[] cache = SAVE_STATE_CACHE.get(); + SaveState cachedState = cache[INIT_STATE_UNINIT]; @@ -89,7 +81,6 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff9 + cachedIsAllZeroValid = true; } - final boolean zero = isAllZero(data); -+ if (zero) { + // Use array-based cache instead of WeakHashMap + SaveState[] cache = SAVE_STATE_CACHE.get(); @@ -102,10 +93,11 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff9 + } + return cachedState; + } ++ // Leaf end - Optimize chunkUnload return state == INIT_STATE_INIT ? new SaveState(null, INIT_STATE_UNINIT) : null; } else { return new SaveState(data.clone(), state); -@@ -148,14 +193,23 @@ public final class SWMRNibbleArray { +@@ -148,17 +194,28 @@ public final class SWMRNibbleArray { } protected static boolean isAllZero(final byte[] data) { @@ -114,14 +106,15 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff9 - - for (int k = 1; k < (1 << 4); ++k) { - whole |= data[(i << 4) | k]; -+ // check in 8-byte chunks ++ // Leaf start - Optimize chunkUnload ++ // Check in 8-byte chunks + final int longLength = ARRAY_SIZE >>> 3; + for (int i = 0; i < longLength; i++) { + long value = 0; + final int baseIndex = i << 3; + // Combine 8 bytes into a long + for (int j = 0; j < 8; j++) { -+ value |= ((long)(data[baseIndex + j] & 0xFF)) << (j << 3); ++ value |= ((long) (data[baseIndex + j] & 0xFF)) << (j << 3); + } + if (value != 0) { + return false; @@ -135,50 +128,45 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..75893974d198b946bdc07b01b4c68ff9 return false; } } -@@ -163,6 +217,10 @@ public final class SWMRNibbleArray { ++ // Leaf end - Optimize chunkUnload + return true; } - -+ private void invalidateCache() { -+ this.cachedIsAllZeroValid = false; -+ } -+ - // operation type: updating on src, updating on other - public void extrudeLower(final SWMRNibbleArray other) { - if (other.stateUpdating == INIT_STATE_NULL) { -@@ -349,6 +407,7 @@ public final class SWMRNibbleArray { +@@ -349,6 +406,7 @@ public final class SWMRNibbleArray { } this.updatingDirty = false; this.stateVisible = this.stateUpdating; -+ this.cachedIsAllZeroValid = false; // Invalidate cache on update ++ this.cachedIsAllZeroValid = false; // Leaf - Optimize chunkUnload - Invalidate cache on update } return true; -@@ -424,7 +483,14 @@ public final class SWMRNibbleArray { +@@ -424,7 +482,16 @@ public final class SWMRNibbleArray { final int shift = (index & 1) << 2; final int i = index >>> 1; - this.storageUpdating[i] = (byte)((this.storageUpdating[i] & (0xF0 >>> shift)) | (value << shift)); ++ // Leaf start - Optimize chunkUnload + byte oldValue = this.storageUpdating[i]; + byte newValue = (byte)((oldValue & (0xF0 >>> shift)) | (value << shift)); + + // Only invalidate cache if the value actually changes + if (oldValue != newValue) { + this.storageUpdating[i] = newValue; -+ this.invalidateCache(); ++ this.cachedIsAllZeroValid = false; + } ++ // Leaf end - Optimize chunkUnload } public static final class SaveState { diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..f6c33a7ca59d00c8967034402be00767a66b6948 100644 +index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..dc158e981199b507531af810ff9ced3ca717e39e 100644 --- a/net/minecraft/world/level/chunk/LevelChunkSection.java +++ b/net/minecraft/world/level/chunk/LevelChunkSection.java @@ -24,6 +24,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status public final PalettedContainer states; private PalettedContainer> biomes; // CraftBukkit - read/write -+ private boolean modified = false; ++ private boolean modified = false; // Leaf - Optimize chunkUnload // Paper start - block counting private static final it.unimi.dsi.fastutil.shorts.ShortArrayList FULL_LIST = new it.unimi.dsi.fastutil.shorts.ShortArrayList(16*16*16); @@ -186,14 +174,15 @@ index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..f6c33a7ca59d00c8967034402be00767 // Paper end - block counting public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { -+ this.modified = true; ++ this.modified = true; // Leaf - Optimize chunkUnload BlockState blockState; if (useLocks) { blockState = this.states.getAndSet(x, y, z, state); -@@ -328,7 +330,29 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ +@@ -328,7 +330,32 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ this.biomes = palettedContainer; } ++ // Leaf start - Optimize chunkUnload + private LevelChunkSection(short nonEmptyBlockCount, short tickingBlockCount, short tickingFluidCount, + PalettedContainer states, PalettedContainer> biomes) { + this.nonEmptyBlockCount = nonEmptyBlockCount; @@ -201,10 +190,12 @@ index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..f6c33a7ca59d00c8967034402be00767 + this.tickingFluidCount = tickingFluidCount; + this.states = states; + this.biomes = biomes; -+ this.isRandomlyTickingBlocksStatus = this.tickingBlockCount > 0; ++ this.isRandomlyTickingBlocksStatus = this.tickingBlockCount > 0; // Leaf - Cache random tick block status + } ++ // Leaf end - Optimize chunkUnload + public LevelChunkSection copy() { ++ // Leaf - Optimize chunkUnload + // If the section hasn't been modified and no random ticking blocks/fluids, + // return a lightweight copy that shares palette data + if (!this.modified && this.tickingBlockCount == 0 && this.tickingFluidCount == 0) { @@ -216,7 +207,7 @@ index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..f6c33a7ca59d00c8967034402be00767 + this.biomes // Share reference instead of copying + ); + } ++ // Leaf end - Optimize chunkUnload return new LevelChunkSection(this); } -+ } diff --git a/leaf-server/minecraft-patches/features/0139-Async-ChunkSend.patch b/leaf-server/minecraft-patches/features/0140-Async-chunk-sending.patch similarity index 69% rename from leaf-server/minecraft-patches/features/0139-Async-ChunkSend.patch rename to leaf-server/minecraft-patches/features/0140-Async-chunk-sending.patch index d5e4cb6d..e8b716e1 100644 --- a/leaf-server/minecraft-patches/features/0139-Async-ChunkSend.patch +++ b/leaf-server/minecraft-patches/features/0140-Async-chunk-sending.patch @@ -1,44 +1,18 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 2 Mar 2025 21:23:20 +0100 -Subject: [PATCH] Async ChunkSend +Subject: [PATCH] Async chunk sending diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078918d52a9 100644 +index a35e9fae8f8da0c42f0616c4f78dc396492673aa..af49117695c0785033b984ff91550e2fccbbc5e6 100644 --- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java +++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java -@@ -22,15 +22,13 @@ import it.unimi.dsi.fastutil.longs.LongComparator; - import it.unimi.dsi.fastutil.longs.LongHeapPriorityQueue; - import it.unimi.dsi.fastutil.longs.LongOpenHashSet; - import net.minecraft.network.protocol.Packet; --import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; --import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; --import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket; --import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket; -+import net.minecraft.network.protocol.game.*; - import net.minecraft.server.level.ChunkTrackingView; - import net.minecraft.server.level.ServerLevel; - import net.minecraft.server.level.ServerPlayer; - import net.minecraft.server.level.TicketType; - import net.minecraft.server.network.PlayerChunkSender; -+import net.minecraft.server.network.ServerGamePacketListenerImpl; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.GameRules; - import net.minecraft.world.level.chunk.ChunkAccess; -@@ -43,6 +41,8 @@ import java.util.concurrent.TimeUnit; - import java.util.concurrent.atomic.AtomicLong; - import java.util.function.Function; - -+import static org.dreeam.leaf.config.LeafConfig.LOGGER; -+ - public final class RegionizedPlayerChunkLoader { - - public static final TicketType PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo); -@@ -411,18 +411,84 @@ public final class RegionizedPlayerChunkLoader { +@@ -411,19 +411,91 @@ public final class RegionizedPlayerChunkLoader { this.delayedTicketOps.addLast(op); } ++ // Leaf start - Async chunk sending + /** + * Sends a chunk to the player. + * If async chunk sending is enabled, this will prepare and send the chunk packet asynchronously. @@ -49,7 +23,8 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078 - ((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager - .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$addReceivedChunk(this.player); + final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ); -+ + +- final LevelChunk chunk = ((ChunkSystemLevel)this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ); + if (!this.sentChunks.add(chunkKey)) { + // Already in our sent list - silently return instead of throwing an exception + return; @@ -62,13 +37,12 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078 + this.sentChunks.remove(chunkKey); + return; + } - -- final LevelChunk chunk = ((ChunkSystemLevel)this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ); ++ + // Try to mark the chunk as received by this player + try { + // This part needs to remain on the main thread as it affects shared state -+ ((ChunkSystemChunkHolder) ((ChunkSystemServerLevel) this.world).moonrise$getChunkTaskScheduler().chunkHolderManager -+ .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$addReceivedChunk(this.player); ++ ((ChunkSystemServerLevel) this.world).moonrise$getChunkTaskScheduler().chunkHolderManager ++ .getChunkHolder(chunkX, chunkZ).vanillaChunkHolder.moonrise$addReceivedChunk(this.player); + // Call onChunkWatch on the main thread as it might affect server state PlatformHooks.get().onChunkWatch(this.world, chunk, this.player); @@ -86,22 +60,26 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078 + // Async implementation + net.minecraft.Util.backgroundExecutor().execute(() -> { + try { -+ final ServerGamePacketListenerImpl connection = this.player.connection; ++ final net.minecraft.server.network.ServerGamePacketListenerImpl connection = this.player.connection; + final ServerLevel serverLevel = this.world; ++ + // Create the packet with anti-xray control flag -+ final ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket( ++ final net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket packet = new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( + chunk, serverLevel.getLightEngine(), null, null, + serverLevel.chunkPacketBlockController.shouldModify(this.player, chunk) + ); ++ + // Let the main thread handle the anti-xray processing + serverLevel.getServer().execute(() -> { + if (this.removed || !this.sentChunks.contains(chunkKey)) { + return; + } ++ + // This will trigger anti-xray processing and mark the packet as ready when done + // The packet automatically handles readiness + // Send the packet (which will be held until ready by the network layer) + connection.send(packet); ++ + // Fire events and send POI packets + if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { + new io.papermc.paper.event.packet.PlayerChunkLoadEvent( @@ -110,11 +88,11 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078 + ).callEvent(); + } + -+ ChunkPos pos = chunk.getPos(); -+ DebugPackets.sendPoiPacketsForChunk(serverLevel, pos); ++ net.minecraft.network.protocol.game.DebugPackets.sendPoiPacketsForChunk(serverLevel, chunk.getPos()); + }); + } catch (Exception e) { -+ LOGGER.error("Failed to send chunk asynchronously", e); ++ org.dreeam.leaf.async.AsyncChunkSending.LOGGER.error("Failed to send chunk asynchronously!", e); ++ + if (!this.removed) { + this.sentChunks.remove(chunkKey); + } @@ -124,5 +102,7 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..4e2e9cbd46c12f5d11dca0ecc0d41078 + PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk); + } } ++ // Leaf end - Async chunk sending private void sendUnloadChunk(final int chunkX, final int chunkZ) { + if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { diff --git a/leaf-server/minecraft-patches/features/0140-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0141-Spawner-Configurations.patch similarity index 100% rename from leaf-server/minecraft-patches/features/0140-Spawner-Configurations.patch rename to leaf-server/minecraft-patches/features/0141-Spawner-Configurations.patch diff --git a/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java new file mode 100644 index 00000000..a54cefb3 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/async/AsyncChunkSending.java @@ -0,0 +1,9 @@ +package org.dreeam.leaf.async; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AsyncChunkSending { + + public static final Logger LOGGER = LogManager.getLogger(AsyncChunkSending.class.getSimpleName()); +}