9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-19 15:09:25 +00:00
Files
Leaf/leaf-archived-patches/unapplied/mcserver/0222-Optimise-chunkUnloads.patch
Dreeam 8bffdef317 More patches
Shallou - Habitat
Genre: Progressive House (maybe)
2025-09-29 14:23:25 -04:00

289 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Mon, 14 Apr 2025 20:07:52 +0200
Subject: [PATCH] Optimise chunkUnloads
diff --git a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java
index 4ca68a903e67606fc4ef0bfa9862a73797121c8b..bed3a64388bb43e47c2ba4e67f7dde5b990d9578 100644
--- a/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java
+++ b/ca/spottedleaf/moonrise/patches/starlight/light/SWMRNibbleArray.java
@@ -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<SaveState[]> 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<ArrayDeque<byte[]>> WORKING_BYTES_POOL = ThreadLocal.withInitial(ArrayDeque::new);
+ static final ThreadLocal<ArrayDeque<byte[]>> 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<byte[]> queue = WORKING_BYTES_POOL.get();
+ final byte[] inPool = queue.pollFirst();
+ // Leaf end - Optimize chunkUnload
if (inPool != null) {
return inPool;
}
@@ -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<byte[]> 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 +148,44 @@ public final class SWMRNibbleArray {
public SaveState getSaveState() {
synchronized (this) {
final int state = this.stateVisible;
- final byte[] data = this.storageVisible;
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];
+ if (cachedState == null) {
+ cachedState = new SaveState(null, state);
+ cache[INIT_STATE_UNINIT] = cachedState;
+ }
+ return cachedState;
+ }
+
+ // Check if we need to test for all zeros
+ final byte[] data = this.storageVisible;
+ boolean zero;
+ if (cachedIsAllZeroValid) {
+ zero = cachedIsAllZero;
+ } else {
+ zero = isAllZero(data);
+ cachedIsAllZero = zero;
+ cachedIsAllZeroValid = true;
}
- final boolean zero = isAllZero(data);
if (zero) {
+ // Use array-based cache instead of WeakHashMap
+ SaveState[] cache = SAVE_STATE_CACHE.get();
+ int cacheKey = state == INIT_STATE_INIT ? INIT_STATE_UNINIT : -1;
+ if (cacheKey >= 0) {
+ SaveState cachedState = cache[cacheKey];
+ if (cachedState == null) {
+ cachedState = new SaveState(null, cacheKey);
+ cache[cacheKey] = cachedState;
+ }
+ 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,17 +194,28 @@ public final class SWMRNibbleArray {
}
protected static boolean isAllZero(final byte[] data) {
- for (int i = 0; i < (ARRAY_SIZE >>> 4); ++i) {
- byte whole = data[i << 4];
-
- for (int k = 1; k < (1 << 4); ++k) {
- whole |= data[(i << 4) | k];
+ // 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);
+ }
+ if (value != 0) {
+ return false;
}
+ }
- if (whole != 0) {
+ // Check remaining bytes
+ for (int i = longLength << 3; i < ARRAY_SIZE; i++) {
+ if (data[i] != 0) {
return false;
}
}
+ // Leaf end - Optimize chunkUnload
return true;
}
@@ -349,6 +406,7 @@ public final class SWMRNibbleArray {
}
this.updatingDirty = false;
this.stateVisible = this.stateUpdating;
+ this.cachedIsAllZeroValid = false; // Leaf - Optimize chunkUnload - Invalidate cache on update
}
return true;
@@ -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.cachedIsAllZeroValid = false;
+ }
+ // Leaf end - Optimize chunkUnload
}
public static final class SaveState {
diff --git a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
index 08b98e60518027a6d0aa99d03ba02982355ffa76..bbb871f011e947e426f8be32f0ab71aded4e0980 100644
--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -493,14 +493,16 @@ public record SerializableChunkData(
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
} else {
ChunkPos pos = chunk.getPos();
- List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
- LevelChunkSection[] sections = chunk.getSections();
- LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
// Paper start - starlight
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 - Optimize chunkUnload
+ // Pre-allocate with correct capacity to avoid resizing
+ final int expectedSectionCount = maxLightSection - minLightSection + 1;
+ List<SerializableChunkData.SectionData> list = new ArrayList<>(expectedSectionCount);
+ // Leaf end - Optimize chunkUnload
final LevelChunkSection[] chunkSections = chunk.getSections();
final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
@@ -518,10 +520,20 @@ public record SerializableChunkData(
continue;
}
+ // Leaf start - Optimize chunkUnload
+ DataLayer blockDataLayer = null;
+ if (blockNibble != null && blockNibble.data != null) {
+ blockDataLayer = new DataLayer(blockNibble.data);
+ }
+
+ DataLayer skyDataLayer = null;
+ if (skyNibble != null && skyNibble.data != null) {
+ skyDataLayer = new DataLayer(skyNibble.data);
+ }
+ // Leaf end - Optimize chunkUnload
+
final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
- lightSection, chunkSection,
- blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
- skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
+ lightSection, chunkSection, blockDataLayer, skyDataLayer // Leaf - Optimize chunkUnload
);
if (blockNibble != null) {
@@ -532,12 +544,16 @@ public record SerializableChunkData(
((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
}
- sectionsList.add(sectionData);
+ list.add(sectionData); // Leaf - Optimize chunkUnload
}
// Paper end - starlight
- List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
+ // Leaf start - Optimize chunkUnload
+ // Pre-allocate block entities list with exact size needed
+ final int blockEntityCount = chunk.getBlockEntitiesPos().size();
+ List<CompoundTag> list1 = blockEntityCount > 0 ? new ArrayList<>(blockEntityCount) : java.util.Collections.emptyList();
+ if (blockEntityCount > 0) // Leaf - Optimize chunkUnload
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
CompoundTag blockEntityNbtForSaving = chunk.getBlockEntityNbtForSaving(blockPos, level.registryAccess());
if (blockEntityNbtForSaving != null) {
@@ -545,15 +561,27 @@ public record SerializableChunkData(
}
}
- List<CompoundTag> list2 = new ArrayList<>();
+ // Leaf start - Optimize chunkUnload
+ // For entities, use an initial estimated capacity if it's a ProtoChunk
+ List<CompoundTag> list2;
long[] longs = null;
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
ProtoChunk protoChunk = (ProtoChunk)chunk;
- list2.addAll(protoChunk.getEntities());
+ // Leaf start - Optimize chunkUnload
+ int entitySize = protoChunk.getEntities().size();
+ if (entitySize > 0) {
+ list2 = new ArrayList<>(Math.max(16, entitySize));
+ list2.addAll(protoChunk.getEntities());
+ } else {
+ list2 = java.util.Collections.emptyList();
+ }
+ // Leaf end - Optimize chunkUnload
CarvingMask carvingMask = protoChunk.getCarvingMask();
if (carvingMask != null) {
longs = carvingMask.toArray();
}
+ } else {
+ list2 = java.util.Collections.emptyList(); // Leaf - Optimize chunkUnload
}
Map<Heightmap.Types, long[]> map = new EnumMap<>(Heightmap.Types.class);
@@ -561,14 +589,26 @@ public record SerializableChunkData(
for (Entry<Heightmap.Types, Heightmap> 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)); // Leaf - Optimize chunkUnload
}
}
ChunkAccess.PackedTicks ticksForSerialization = chunk.getTicksForSerialization(level.getGameTime());
- ShortList[] lists = Arrays.stream(chunk.getPostProcessing())
- .map(list3 -> list3 != null ? new ShortArrayList(list3) : null)
- .toArray(ShortList[]::new);
+ // Leaf start - Optimize chunkUnload - remove stream
+ ShortList[] postProcessing = chunk.getPostProcessing();
+ ShortList[] lists = new ShortList[postProcessing.length];
+ for (int i = 0; i < postProcessing.length; i++) {
+ ShortList source = postProcessing[i];
+ // Only create a new list if there's actual data to copy
+ if (source != null) {
+ int size = source.size();
+ if (size > 0) {
+ lists[i] = new ShortArrayList(size);
+ lists[i].addAll(source);
+ }
+ }
+ }
+ // Leaf end - Optimize chunkUnload - remove stream
CompoundTag compoundTag = packStructureData(
StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
);