mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 19:39:17 +00:00
Originally vanilla logic is to use stream, and Mojang switched it to Guava's Collections2 since 1.21.4. It is much faster than using stream or manually adding to a new ArrayList. Manually adding to a new ArrayList requires allocating a new object array. However, the Collections2 lazy handles filter condition on iteration, so much better.
189 lines
12 KiB
Diff
189 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Taiyou06 <kaandindar21@gmail.com>
|
|
Date: Sun, 2 Mar 2025 21:23:20 +0100
|
|
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 20b1186f53c267f69ed7852f5cf3dd2460f8200d..19b5e4856d471ebfa49335ed19ced767e57df771 100644
|
|
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
@@ -448,7 +448,15 @@ public final class RegionizedPlayerChunkLoader {
|
|
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
|
|
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
|
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player);
|
|
- this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
|
+ // Leaf start - Async chunk sending
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
|
+ () -> this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)))
|
|
+ );
|
|
+ } else {
|
|
+ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
|
+ }
|
|
+ // Leaf end - Async chunk sending
|
|
// Paper start - PlayerChunkUnloadEvent
|
|
if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent();
|
|
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
index 9f6d7c5dc0e591488a8a3763d8a1f1b3671d5299..8af4c964ce67373f9b911ce13164f48c29a07d85 100644
|
|
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
|
@@ -75,6 +75,52 @@ public class ClientboundLevelChunkPacketData {
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - Async chunk sending
|
|
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo, BlockEntity[] blockEntities, Map<Heightmap.Types, long[]> heightmaps) {
|
|
+ this.heightmaps = heightmaps;
|
|
+
|
|
+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread) {
|
|
+ int size = calculateChunkSize(levelChunk);
|
|
+ ByteBuf buffer = Unpooled.buffer(size);
|
|
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
|
|
+ // make sure all sections is latest
|
|
+ while (size != buffer.writerIndex()) {
|
|
+ buffer.writerIndex(0);
|
|
+ size = calculateChunkSize(levelChunk);
|
|
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
|
|
+ }
|
|
+ byte[] array = it.unimi.dsi.fastutil.bytes.ByteArrays.setLength(buffer.array(), buffer.writerIndex());
|
|
+ if (chunkPacketInfo != null) {
|
|
+ chunkPacketInfo.setBuffer(array);
|
|
+ }
|
|
+ this.buffer = array;
|
|
+ } else {
|
|
+ this.buffer = new byte[calculateChunkSize(levelChunk)];
|
|
+ // Paper start - Anti-Xray - Add chunk packet info
|
|
+ if (chunkPacketInfo != null) {
|
|
+ chunkPacketInfo.setBuffer(this.buffer);
|
|
+ }
|
|
+ extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), levelChunk, chunkPacketInfo);
|
|
+ }
|
|
+
|
|
+ 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<ClientGamePacketListener> 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 = HEIGHTMAPS_STREAM_CODEC.decode(buffer);
|
|
int varInt = buffer.readVarInt();
|
|
@@ -123,6 +169,7 @@ public class ClientboundLevelChunkPacketData {
|
|
// Paper end - Anti-Xray - Add chunk packet info
|
|
}
|
|
|
|
+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread) return; // Leaf - Async chunk sending
|
|
if (buffer.writerIndex() != buffer.capacity()) {
|
|
throw new IllegalStateException("Didn't fill chunk buffer: expected " + buffer.capacity() + " bytes, got " + buffer.writerIndex());
|
|
}
|
|
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..d8e938abf5123b092cec80feb6468e3d91ae823e 100644
|
|
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
|
@@ -44,6 +44,17 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
|
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
|
chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
|
}
|
|
+ // Leaf start - Async chunk sending
|
|
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks, net.minecraft.world.level.block.entity.BlockEntity[] blockEntities, java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps) {
|
|
+ ChunkPos pos = chunk.getPos();
|
|
+ this.x = pos.x;
|
|
+ this.z = pos.z;
|
|
+ io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo = modifyBlocks ? chunk.getLevel().chunkPacketBlockController.getChunkPacketInfo(this, chunk) : null; // Paper - Ant-Xray
|
|
+ this.chunkData = new ClientboundLevelChunkPacketData(chunk, chunkPacketInfo, blockEntities, heightmaps); // 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();
|
|
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
|
|
index 0376a10ee0544b13e8fd629a7b13f78811e57a30..aa6b900347635857b84460fa8435b81f794f0747 100644
|
|
--- a/net/minecraft/server/network/PlayerChunkSender.java
|
|
+++ b/net/minecraft/server/network/PlayerChunkSender.java
|
|
@@ -64,13 +64,29 @@ public class PlayerChunkSender {
|
|
if (!list.isEmpty()) {
|
|
ServerGamePacketListenerImpl serverGamePacketListenerImpl = player.connection;
|
|
this.unacknowledgedBatches++;
|
|
- serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
|
+ // Leaf start - Async chunk sending
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
|
+ () -> serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE)
|
|
+ );
|
|
+ } else {
|
|
+ serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
|
+ }
|
|
+ // Leaf end - Async chunk sending
|
|
|
|
for (LevelChunk levelChunk : list) {
|
|
sendChunk(serverGamePacketListenerImpl, serverLevel, levelChunk);
|
|
}
|
|
|
|
- serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
|
+ // Leaf start - Async chunk sending
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
|
+ () -> serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()))
|
|
+ );
|
|
+ } else {
|
|
+ serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
|
+ }
|
|
+ // Leaf end - Async chunk sending
|
|
this.batchQuota = this.batchQuota - list.size();
|
|
}
|
|
}
|
|
@@ -81,7 +97,23 @@ public class PlayerChunkSender {
|
|
// Paper start - Anti-Xray
|
|
public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
|
|
final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
|
|
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
|
+ // Leaf start - Async chunk sending
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
|
+ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]);
|
|
+ java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps = new java.util.concurrent.ConcurrentHashMap<>();
|
|
+
|
|
+ for (var entry : chunk.getHeightmaps()) {
|
|
+ if (entry.getKey().sendToClient()) {
|
|
+ heightmaps.put(entry.getKey(), entry.getValue().getRawData());
|
|
+ }
|
|
+ }
|
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
|
+ () -> packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify, blockEntities, heightmaps))
|
|
+ );
|
|
+ } else {
|
|
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
|
+ }
|
|
+ // Leaf end - Async chunk sending
|
|
// Paper end - Anti-Xray
|
|
// Paper start - PlayerChunkLoadEvent
|
|
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
index 36c033b0ee63dfc273d721fb4b614733e8fdef19..4d06df242ab73411bdefc4770e131b27a6ea668a 100644
|
|
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
@@ -18,7 +18,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
public static final int SECTION_HEIGHT = 16;
|
|
public static final int SECTION_SIZE = 4096;
|
|
public static final int BIOME_CONTAINER_BITS = 2;
|
|
- short nonEmptyBlockCount; // Paper - package private
|
|
+ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - Async chunk sending - volatile
|
|
private short tickingBlockCount;
|
|
private short tickingFluidCount;
|
|
private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status
|