diff --git a/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch b/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch index 76b36fb2..e8b716e1 100644 --- a/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch +++ b/leaf-server/minecraft-patches/features/0132-Async-chunk-sending.patch @@ -5,10 +5,10 @@ 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..c0f14d2bc30a186511bafddc2e80f29b686887c5 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 -@@ -411,19 +411,98 @@ public final class RegionizedPlayerChunkLoader { +@@ -411,19 +411,91 @@ public final class RegionizedPlayerChunkLoader { this.delayedTicketOps.addLast(op); } @@ -29,6 +29,7 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..c0f14d2bc30a186511bafddc2e80f29b + // Already in our sent list - silently return instead of throwing an exception + return; + } ++ + // Get the chunk now, as we need it for both sync and async paths + final LevelChunk chunk = ((ChunkSystemLevel) this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ); + if (chunk == null) { @@ -36,12 +37,13 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..c0f14d2bc30a186511bafddc2e80f29b + this.sentChunks.remove(chunkKey); + return; + } - ++ + // 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 + ((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); - PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk); @@ -56,13 +58,6 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..c0f14d2bc30a186511bafddc2e80f29b + // Check if async chunk sending is enabled + if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) { + // Async implementation -+ var heightmaps = new net.minecraft.nbt.CompoundTag(); -+ for (var entry : chunk.getHeightmaps()) { -+ if (entry.getKey().sendToClient()) { -+ heightmaps.put(entry.getKey().getSerializationKey(), new net.minecraft.nbt.LongArrayTag(entry.getValue().getRawData())); -+ } -+ } -+ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]); + net.minecraft.Util.backgroundExecutor().execute(() -> { + try { + final net.minecraft.server.network.ServerGamePacketListenerImpl connection = this.player.connection; @@ -71,9 +66,7 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..c0f14d2bc30a186511bafddc2e80f29b + // Create the packet with anti-xray control flag + 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), -+ heightmaps, -+ blockEntities ++ serverLevel.chunkPacketBlockController.shouldModify(this.player, chunk) + ); + + // Let the main thread handle the anti-xray processing @@ -113,138 +106,3 @@ index a35e9fae8f8da0c42f0616c4f78dc396492673aa..c0f14d2bc30a186511bafddc2e80f29b private void sendUnloadChunk(final int chunkX, final int chunkZ) { if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) { -diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -index 9e321ef1c3d5803519b243685f4ee598dc0cf640..c981801171307e571388e6e810840ae2525c5d10 100644 ---- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java -@@ -26,6 +26,7 @@ public class ClientboundLevelChunkPacketData { - private static final int TWO_MEGABYTES = 2097152; - private final CompoundTag heightmaps; - private final byte[] buffer; -+ private final int bufferLength; - private final List blockEntitiesData; - // Paper start - Handle oversized block entities in chunks - private final java.util.List> extraPackets = new java.util.ArrayList<>(); -@@ -52,6 +53,7 @@ public class ClientboundLevelChunkPacketData { - } - - this.buffer = new byte[calculateChunkSize(levelChunk)]; -+ this.bufferLength = this.buffer.length; // Leaf - // Paper start - Anti-Xray - Add chunk packet info - if (chunkPacketInfo != null) { - chunkPacketInfo.setBuffer(this.buffer); -@@ -74,6 +76,51 @@ public class ClientboundLevelChunkPacketData { - } - } - -+ // Leaf start - Async chunk sending -+ public ClientboundLevelChunkPacketData( -+ LevelChunk levelChunk, -+ io.papermc.paper.antixray.ChunkPacketInfo chunkPacketInfo, -+ CompoundTag heightmaps, -+ BlockEntity[] blockEntities -+ ) { -+ this.heightmaps = heightmaps; -+ var buffer = new FriendlyByteBuf(Unpooled.buffer(calculateChunkSize(levelChunk) + 128)); -+ var sections = levelChunk.getSections(); -+ var sectionLength = sections.length; -+ for (int i = 0; i < sectionLength; i++) { -+ LevelChunkSection section = sections[i]; -+ synchronized (section.getStates()) { -+ buffer.writeShort(section.nonEmptyBlockCount()); -+ section.getStates().write(buffer, chunkPacketInfo, i); -+ } -+ section.getBiomes().write(buffer, null, i); -+ } -+ this.buffer = buffer.array(); -+ this.bufferLength = buffer.writerIndex(); -+ -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ chunkPacketInfo.setBuffer(this.buffer); -+ chunkPacketInfo.setLength(this.bufferLength); -+ } -+ this.blockEntitiesData = Lists.newArrayList(); -+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks -+ -+ for (BlockEntity blockEntity : blockEntities) { -+ // Paper start - Handle oversized block entities in chunks -+ if (++totalTileEntities > BLOCK_ENTITY_LIMIT) { -+ net.minecraft.network.protocol.Packet packet = blockEntity.getUpdatePacket(); -+ if (packet != null) { -+ this.extraPackets.add(packet); -+ continue; -+ } -+ } -+ // Paper end - Handle oversized block entities in chunks -+ this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(blockEntity)); -+ } -+ } -+ // Leaf end - Async chunk sending -+ - public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) { - this.heightmaps = buffer.readNbt(); - if (this.heightmaps == null) { -@@ -84,6 +131,7 @@ public class ClientboundLevelChunkPacketData { - throw new RuntimeException("Chunk Packet trying to allocate too much memory on read."); - } else { - this.buffer = new byte[varInt]; -+ this.bufferLength = this.buffer.length; // Leaf - buffer.readBytes(this.buffer); - this.blockEntitiesData = ClientboundLevelChunkPacketData.BlockEntityInfo.LIST_STREAM_CODEC.decode(buffer); - } -@@ -92,8 +140,8 @@ public class ClientboundLevelChunkPacketData { - - public void write(RegistryFriendlyByteBuf buffer) { - buffer.writeNbt(this.heightmaps); -- buffer.writeVarInt(this.buffer.length); -- buffer.writeBytes(this.buffer); -+ buffer.writeVarInt(this.bufferLength); // Leaf -+ buffer.writeBytes(this.buffer, 0, this.bufferLength); // Leaf - ClientboundLevelChunkPacketData.BlockEntityInfo.LIST_STREAM_CODEC.encode(buffer, this.blockEntitiesData); - } - -diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..4eeb4967120b1c2cf13d2b4a8c07175fb4d98012 100644 ---- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java -@@ -45,6 +45,26 @@ public class ClientboundLevelChunkWithLightPacket implements Packet chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray -+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo, heightmaps, blockEntities); // Paper - Anti-Xray -+ this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight); -+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks -+ } -+ // Leaf end - Async chunk sending -+ - private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) { - this.x = buffer.readInt(); - this.z = buffer.readInt(); -diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index b8ac6a9ba7b56ccd034757f7d135d272b8e69e90..f85c1c3ab8cc0d0dafa6df8536318e3443265d2b 100644 ---- a/net/minecraft/world/level/chunk/LevelChunkSection.java -+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java -@@ -49,6 +49,8 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ - } - // Paper end - block counting - -+ public final int nonEmptyBlockCount() { return this.nonEmptyBlockCount; } // Leaf -+ - private LevelChunkSection(LevelChunkSection section) { - this.nonEmptyBlockCount = section.nonEmptyBlockCount; - this.tickingBlockCount = section.tickingBlockCount; diff --git a/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch b/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch index 03d616cf..61b8d35b 100644 --- a/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch +++ b/leaf-server/minecraft-patches/features/0157-Optimise-chunkUnloads.patch @@ -159,7 +159,7 @@ index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..bed3a64388bb43e47c2ba4e67f7dde5b public static final class SaveState { diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java -index f85c1c3ab8cc0d0dafa6df8536318e3443265d2b..449f180c49f191786c5f253baf04ddd9b72835f7 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_ @@ -170,7 +170,7 @@ index f85c1c3ab8cc0d0dafa6df8536318e3443265d2b..449f180c49f191786c5f253baf04ddd9 // 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); -@@ -137,6 +138,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ +@@ -135,6 +136,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_ // Paper end - block counting public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { @@ -178,7 +178,7 @@ index f85c1c3ab8cc0d0dafa6df8536318e3443265d2b..449f180c49f191786c5f253baf04ddd9 BlockState blockState; if (useLocks) { blockState = this.states.getAndSet(x, y, z, state); -@@ -330,7 +332,32 @@ 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; } diff --git a/leaf-server/paper-patches/features/0034-Async-chunk-sending.patch b/leaf-server/paper-patches/features/0034-Async-chunk-sending.patch deleted file mode 100644 index b52674a1..00000000 --- a/leaf-server/paper-patches/features/0034-Async-chunk-sending.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: hayanesuru -Date: Sun, 27 Apr 2025 21:19:30 +0800 -Subject: [PATCH] Async chunk sending - - -diff --git a/src/main/java/io/papermc/paper/antixray/BitStorageReader.java b/src/main/java/io/papermc/paper/antixray/BitStorageReader.java -index c27703775c845a8b0bd1b0cb8f05eb736d8a813c..7e72c910232b85b411ecba1fa0acdf7f81c85418 100644 ---- a/src/main/java/io/papermc/paper/antixray/BitStorageReader.java -+++ b/src/main/java/io/papermc/paper/antixray/BitStorageReader.java -@@ -8,11 +8,18 @@ public final class BitStorageReader { - private int longInBufferIndex; - private int bitInLongIndex; - private long current; -+ private int length; // Leaf - - public void setBuffer(byte[] buffer) { - this.buffer = buffer; - } - -+ // Leaf start -+ public void setLength(int length) { -+ this.length = length; -+ } -+ // Leaf end -+ - public void setBits(int bits) { - this.bits = bits; - mask = (1 << bits) - 1; -@@ -25,7 +32,7 @@ public final class BitStorageReader { - } - - private void init() { -- if (buffer.length > longInBufferIndex + 7) { -+ if (length > longInBufferIndex + 7) { // Leaf - current = ((((long) buffer[longInBufferIndex]) << 56) - | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48) - | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40) -diff --git a/src/main/java/io/papermc/paper/antixray/BitStorageWriter.java b/src/main/java/io/papermc/paper/antixray/BitStorageWriter.java -index 83412e0ddaade11eb7ac7b41bb8ae5b085802775..d521bd2275152575f5fe5038a817471026abccda 100644 ---- a/src/main/java/io/papermc/paper/antixray/BitStorageWriter.java -+++ b/src/main/java/io/papermc/paper/antixray/BitStorageWriter.java -@@ -9,11 +9,18 @@ public final class BitStorageWriter { - private int bitInLongIndex; - private long current; - private boolean dirty; -+ private int length; // Leaf - - public void setBuffer(byte[] buffer) { - this.buffer = buffer; - } - -+ // Leaf start -+ public void setLength(int length) { -+ this.length = length; -+ } -+ // Leaf end -+ - public void setBits(int bits) { - this.bits = bits; - mask = (1L << bits) - 1; -@@ -26,7 +33,7 @@ public final class BitStorageWriter { - } - - private void init() { -- if (buffer.length > longInBufferIndex + 7) { -+ if (length > longInBufferIndex + 7) { // Leaf - current = ((((long) buffer[longInBufferIndex]) << 56) - | (((long) buffer[longInBufferIndex + 1] & 0xff) << 48) - | (((long) buffer[longInBufferIndex + 2] & 0xff) << 40) -@@ -41,7 +48,7 @@ public final class BitStorageWriter { - } - - public void flush() { -- if (dirty && buffer.length > longInBufferIndex + 7) { -+ if (dirty && length > longInBufferIndex + 7) { // Leaf - buffer[longInBufferIndex] = (byte) (current >> 56 & 0xff); - buffer[longInBufferIndex + 1] = (byte) (current >> 48 & 0xff); - buffer[longInBufferIndex + 2] = (byte) (current >> 40 & 0xff); -diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java -index ee2d3a54d760f9c26542eab03c51651a30e279a0..bbf338e5e98a7b7d1c7cd986333df03f4aad4bb1 100644 ---- a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java -+++ b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -227,6 +227,8 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo - boolean[] obfuscateTemp = null; - bitStorageReader.setBuffer(chunkPacketInfoAntiXray.getBuffer()); - bitStorageWriter.setBuffer(chunkPacketInfoAntiXray.getBuffer()); -+ bitStorageReader.setLength(chunkPacketInfoAntiXray.getLength()); -+ bitStorageWriter.setLength(chunkPacketInfoAntiXray.getLength()); - int numberOfBlocks = presetBlockStateBits.length; - // Keep the lambda expressions as simple as possible. They are used very frequently. - LayeredIntSupplier random = numberOfBlocks == 1 ? (() -> 0) : engineMode == EngineMode.OBFUSCATE_LAYER ? new LayeredIntSupplier() { -diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketInfo.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketInfo.java -index a33a4d45d478ededff27244fcb910d3f369f2151..26f0e4b43c2adc27e0ca2bafb7a1bab3fcf8f6cc 100644 ---- a/src/main/java/io/papermc/paper/antixray/ChunkPacketInfo.java -+++ b/src/main/java/io/papermc/paper/antixray/ChunkPacketInfo.java -@@ -13,6 +13,7 @@ public class ChunkPacketInfo { - private final int[] indexes; - private final Object[][] presetValues; - private byte[] buffer; -+ private int length; // Leaf - - public ChunkPacketInfo(ClientboundLevelChunkWithLightPacket chunkPacket, LevelChunk chunk) { - this.chunkPacket = chunkPacket; -@@ -36,8 +37,19 @@ public class ChunkPacketInfo { - return buffer; - } - -+ // Leaf start -+ public int getLength() { -+ return length; -+ } -+ // Leaf end -+ - public void setBuffer(byte[] buffer) { - this.buffer = buffer; -+ this.length = buffer.length; // Leaf -+ } -+ -+ public void setLength(int length) { -+ this.length = length; // Leaf - } - - public int getBits(int chunkSectionIndex) {