9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-26 02:19:19 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0206-Rewrite-ClientboundLightUpdatePacketData.patch
Dreeam 9a4efaa230 Drop patch that causes performance regression
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.
2025-08-04 19:25:56 +08:00

241 lines
10 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Mon, 24 Feb 2025 21:33:24 +0100
Subject: [PATCH] Rewrite ClientboundLightUpdatePacketData
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On writing operations:
At 256 sections with 0.75 fill ratio: 18x faster (333.489 μs → 18.606 μs)
At 256 sections with 0.5 fill ratio: 13.5x faster (147.804 μs → 10.962 μs)
diff --git a/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java b/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java
index a0b54f3a3d11e0f0f1cb806406a870ba36da8f07..234280499fe1bc495bcdd4c3e144d1f99b7e6975 100644
--- a/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java
+++ b/net/minecraft/network/protocol/game/ClientboundLightUpdatePacketData.java
@@ -16,30 +16,113 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
public class ClientboundLightUpdatePacketData {
private static final StreamCodec<ByteBuf, byte[]> DATA_LAYER_STREAM_CODEC = ByteBufCodecs.byteArray(2048);
+
+ // Leaf start - Rewrite ClientboundLightUpdatePacketData
+ // Static constants to avoid allocations
+ private static final byte[][] EMPTY_ARRAY = new byte[0][];
+
+ // Pre-sized arrays to avoid dynamic resizing
+ private static final ThreadLocal<byte[][]> SKY_BUFFER = ThreadLocal.withInitial(() -> new byte[256][]);
+ private static final ThreadLocal<byte[][]> BLOCK_BUFFER = ThreadLocal.withInitial(() -> new byte[256][]);
+
+ // Pre-cached BitSets with fixed size
private final BitSet skyYMask;
private final BitSet blockYMask;
private final BitSet emptySkyYMask;
private final BitSet emptyBlockYMask;
- private final List<byte[]> skyUpdates;
- private final List<byte[]> blockUpdates;
+
+ // Fixed arrays with exact counts
+ private final byte[][] skyUpdates;
+ private final byte[][] blockUpdates;
+ private final int skyUpdateCount;
+ private final int blockUpdateCount;
+ // Leaf end - Rewrite ClientboundLightUpdatePacketData
public ClientboundLightUpdatePacketData(ChunkPos chunkPos, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight) {
- this.skyYMask = new BitSet();
- this.blockYMask = new BitSet();
- this.emptySkyYMask = new BitSet();
- this.emptyBlockYMask = new BitSet();
- this.skyUpdates = Lists.newArrayList();
- this.blockUpdates = Lists.newArrayList();
-
- for (int i = 0; i < lightEngine.getLightSectionCount(); i++) {
+ // Leaf start - Rewrite ClientboundLightUpdatePacketData
+ int sectionCount = lightEngine.getLightSectionCount();
+
+ // Round up to nearest long boundary (64 bits) to prevent BitSet expansion
+ int longWords = (sectionCount + 63) >>> 6;
+ int bitSetSize = longWords << 6;
+
+ // Pre-size all BitSets to exact size needed
+ this.skyYMask = new BitSet(bitSetSize);
+ this.blockYMask = new BitSet(bitSetSize);
+ this.emptySkyYMask = new BitSet(bitSetSize);
+ this.emptyBlockYMask = new BitSet(bitSetSize);
+
+ // Get buffer arrays from thread local storage to avoid allocations
+ byte[][] skyBuffer = SKY_BUFFER.get();
+ byte[][] blockBuffer = BLOCK_BUFFER.get();
+
+ // Process all sections in a single pass
+ int skyCount = 0;
+ int blockCount = 0;
+ int minLightSection = lightEngine.getMinLightSection();
+
+ // Cache layer listeners to avoid repeated method calls
+ var skyLayerListener = lightEngine.getLayerListener(LightLayer.SKY);
+ var blockLayerListener = lightEngine.getLayerListener(LightLayer.BLOCK);
+
+ // Single pass through all sections
+ for (int i = 0; i < sectionCount; i++) {
+ int sectionY = minLightSection + i;
+ SectionPos sectionPos = SectionPos.of(chunkPos.x, sectionY, chunkPos.z);
+
+ // Process sky light
if (skyLight == null || skyLight.get(i)) {
- this.prepareSectionData(chunkPos, lightEngine, LightLayer.SKY, i, this.skyYMask, this.emptySkyYMask, this.skyUpdates);
+ DataLayer skyData = skyLayerListener.getDataLayerData(sectionPos);
+ if (skyData != null) {
+ if (skyData.isEmpty()) {
+ emptySkyYMask.set(i);
+ } else {
+ skyYMask.set(i);
+ // Store in buffer temporarily - only clone at the end
+ skyBuffer[skyCount++] = skyData.getData();
+ }
+ }
}
+ // Process block light
if (blockLight == null || blockLight.get(i)) {
- this.prepareSectionData(chunkPos, lightEngine, LightLayer.BLOCK, i, this.blockYMask, this.emptyBlockYMask, this.blockUpdates);
+ DataLayer blockData = blockLayerListener.getDataLayerData(sectionPos);
+ if (blockData != null) {
+ if (blockData.isEmpty()) {
+ emptyBlockYMask.set(i);
+ } else {
+ blockYMask.set(i);
+ // Store in buffer temporarily - only clone at the end
+ blockBuffer[blockCount++] = blockData.getData();
+ }
+ }
+ }
+ }
+
+ // Create final arrays with exact sizes
+ if (skyCount > 0) {
+ this.skyUpdates = new byte[skyCount][];
+ // Clone only at the end to minimize work
+ for (int i = 0; i < skyCount; i++) {
+ this.skyUpdates[i] = skyBuffer[i].clone();
+ }
+ } else {
+ this.skyUpdates = EMPTY_ARRAY;
+ }
+
+ if (blockCount > 0) {
+ this.blockUpdates = new byte[blockCount][];
+ // Clone only at the end to minimize work
+ for (int i = 0; i < blockCount; i++) {
+ this.blockUpdates[i] = blockBuffer[i].clone();
}
+ } else {
+ this.blockUpdates = EMPTY_ARRAY;
}
+
+ this.skyUpdateCount = skyCount;
+ this.blockUpdateCount = blockCount;
+ // Leaf end - Rewrite ClientboundLightUpdatePacketData
}
public ClientboundLightUpdatePacketData(FriendlyByteBuf buffer, int x, int z) {
@@ -47,8 +130,30 @@ public class ClientboundLightUpdatePacketData {
this.blockYMask = buffer.readBitSet();
this.emptySkyYMask = buffer.readBitSet();
this.emptyBlockYMask = buffer.readBitSet();
- this.skyUpdates = buffer.readList(DATA_LAYER_STREAM_CODEC);
- this.blockUpdates = buffer.readList(DATA_LAYER_STREAM_CODEC);
+
+ // Leaf start - Rewrite ClientboundLightUpdatePacketData
+ // Read lists directly as arrays to avoid intermediate collections
+ List<byte[]> skyList = buffer.readList(DATA_LAYER_STREAM_CODEC);
+ List<byte[]> blockList = buffer.readList(DATA_LAYER_STREAM_CODEC);
+
+ int skySize = skyList.size();
+ int blockSize = blockList.size();
+
+ if (skySize > 0) {
+ this.skyUpdates = skyList.toArray(new byte[skySize][]);
+ } else {
+ this.skyUpdates = EMPTY_ARRAY;
+ }
+
+ if (blockSize > 0) {
+ this.blockUpdates = blockList.toArray(new byte[blockSize][]);
+ } else {
+ this.blockUpdates = EMPTY_ARRAY;
+ }
+
+ this.skyUpdateCount = skySize;
+ this.blockUpdateCount = blockSize;
+ // Leaf end - Rewrite ClientboundLightUpdatePacketData
}
public void write(FriendlyByteBuf buffer) {
@@ -56,25 +161,33 @@ public class ClientboundLightUpdatePacketData {
buffer.writeBitSet(this.blockYMask);
buffer.writeBitSet(this.emptySkyYMask);
buffer.writeBitSet(this.emptyBlockYMask);
- buffer.writeCollection(this.skyUpdates, DATA_LAYER_STREAM_CODEC);
- buffer.writeCollection(this.blockUpdates, DATA_LAYER_STREAM_CODEC);
- }
- private void prepareSectionData(
- ChunkPos chunkPos, LevelLightEngine levelLightEngine, LightLayer lightLayer, int index, BitSet skyLight, BitSet blockLight, List<byte[]> updates
- ) {
- DataLayer dataLayerData = levelLightEngine.getLayerListener(lightLayer)
- .getDataLayerData(SectionPos.of(chunkPos, levelLightEngine.getMinLightSection() + index));
- if (dataLayerData != null) {
- if (dataLayerData.isEmpty()) {
- blockLight.set(index);
- } else {
- skyLight.set(index);
- updates.add(dataLayerData.copy().getData());
+ // Leaf start - Rewrite ClientboundLightUpdatePacketData
+ // Avoid creating unnecessary objects when writing
+ if (this.skyUpdateCount > 0) {
+ // Use direct array access for efficiency
+ buffer.writeVarInt(this.skyUpdateCount);
+ for (int i = 0; i < this.skyUpdateCount; i++) {
+ DATA_LAYER_STREAM_CODEC.encode(buffer, this.skyUpdates[i]);
}
+ } else {
+ buffer.writeVarInt(0);
}
+
+ if (this.blockUpdateCount > 0) {
+ // Use direct array access for efficiency
+ buffer.writeVarInt(this.blockUpdateCount);
+ for (int i = 0; i < this.blockUpdateCount; i++) {
+ DATA_LAYER_STREAM_CODEC.encode(buffer, this.blockUpdates[i]);
+ }
+ } else {
+ buffer.writeVarInt(0);
+ }
+ // Leaf end - Rewrite ClientboundLightUpdatePacketData
}
+ // Getter methods
+
public BitSet getSkyYMask() {
return this.skyYMask;
}
@@ -84,7 +197,7 @@ public class ClientboundLightUpdatePacketData {
}
public List<byte[]> getSkyUpdates() {
- return this.skyUpdates;
+ return this.skyUpdateCount > 0 ? java.util.Arrays.asList(this.skyUpdates) : List.of(); // Leaf - Rewrite ClientboundLightUpdatePacketData
}
public BitSet getBlockYMask() {
@@ -96,6 +209,6 @@ public class ClientboundLightUpdatePacketData {
}
public List<byte[]> getBlockUpdates() {
- return this.blockUpdates;
+ return this.blockUpdateCount > 0 ? java.util.Arrays.asList(this.blockUpdates) : List.of(); // Leaf - Rewrite ClientboundLightUpdatePacketData
}
}