diff --git a/leaves-server/minecraft-patches/features/0059-Faster-chunk-serialization.patch b/leaves-server/minecraft-patches/features/0059-Faster-chunk-serialization.patch index 4494dec8..578b32e1 100644 --- a/leaves-server/minecraft-patches/features/0059-Faster-chunk-serialization.patch +++ b/leaves-server/minecraft-patches/features/0059-Faster-chunk-serialization.patch @@ -1,33 +1,42 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: violetc <58360096+s-yh-china@users.noreply.github.com> -Date: Tue, 18 Jul 2023 13:14:15 +0800 +From: Martijn Muijsers +Date: Wed, 30 Nov 2022 21:51:16 +0100 Subject: [PATCH] Faster chunk serialization This patch is Powered by Gale(https://github.com/GaleMC/Gale) +License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) +Gale - https://galemc.org + +This patch is based on the following mixins and classes: +* "net/caffeinemc/mods/lithium/common/world/chunk/CompactingPackedIntegerArray.java" +* "net/caffeinemc/mods/lithium/common/world/chunk/LithiumHashPalette.java" +* "net/caffeinemc/mods/lithium/mixin/chunk/serialization/SimpleBitStorageMixin.java" +* "net/caffeinemc/mods/lithium/mixin/chunk/serialization/PalettedContainerMixin.java" +By: Angeline +As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) +Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/net/minecraft/util/BitStorage.java b/net/minecraft/util/BitStorage.java -index 02502d50f0255f5bbcc0ecb965abb48cc1a112da..0abee1cd9d6a5a22d3136e3711de926c3a2d4d73 100644 +index 02502d50f0255f5bbcc0ecb965abb48cc1a112da..e1f4ca261d106d176298b2afc016f5168abaa06b 100644 --- a/net/minecraft/util/BitStorage.java +++ b/net/minecraft/util/BitStorage.java -@@ -21,6 +21,8 @@ public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counti - - BitStorage copy(); - -+ void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out); // Leaves - faster chunk serialization +@@ -38,4 +38,6 @@ public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counti + return ret; + } + // Paper end - block counting + - // Paper start - block counting - // provide default impl in case mods implement this... - @Override ++ void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out); // Gale - Lithium - faster chunk serialization + } diff --git a/net/minecraft/util/SimpleBitStorage.java b/net/minecraft/util/SimpleBitStorage.java -index e6306a68c8652d4c5d22d5ecb1416f5f931f76ee..7b4b3b8a02be3ab5ecafdea1ab74c246daf37d98 100644 +index e6306a68c8652d4c5d22d5ecb1416f5f931f76ee..8091f0c0a536047ead4966e70785962e87faad9a 100644 --- a/net/minecraft/util/SimpleBitStorage.java +++ b/net/minecraft/util/SimpleBitStorage.java -@@ -465,4 +465,44 @@ public class SimpleBitStorage implements BitStorage { +@@ -465,4 +465,45 @@ public class SimpleBitStorage implements BitStorage { super(message); } } + -+ // Leaves start - faster chunk serialization ++ // Gale start - Lithium - faster chunk serialization + @Override + public void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out) { + if (this.size >= Short.MAX_VALUE) { @@ -65,49 +74,46 @@ index e6306a68c8652d4c5d22d5ecb1416f5f931f76ee..7b4b3b8a02be3ab5ecafdea1ab74c246 + } + } + } -+ // Leaves end - faster chunk serialization ++ // Gale end - Lithium - faster chunk serialization ++ } diff --git a/net/minecraft/util/ZeroBitStorage.java b/net/minecraft/util/ZeroBitStorage.java -index 09fd99c9cbd23b5f3c899bfb00c9b89651948ed8..50993ce7519a77c6a9d36cb925125adccda7037f 100644 +index 09fd99c9cbd23b5f3c899bfb00c9b89651948ed8..0066476f5e8289f0702ba3e525397419ef8b44ae 100644 --- a/net/minecraft/util/ZeroBitStorage.java +++ b/net/minecraft/util/ZeroBitStorage.java -@@ -63,6 +63,8 @@ public class ZeroBitStorage implements BitStorage { - return this; +@@ -80,4 +80,6 @@ public class ZeroBitStorage implements BitStorage { + return ret; } - -+ @Override public void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out) {} // Leaves - faster chunk serialization + // Paper end - block counting + - // Paper start - block counting - @Override - public final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap moonrise$countEntries() { ++ @Override public void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out) {} // Gale - Lithium - faster chunk serialization + } diff --git a/net/minecraft/world/level/chunk/PaletteResize.java b/net/minecraft/world/level/chunk/PaletteResize.java -index c723606fa0be811e580ba47de8c9c575583cc930..c768443c8c6a4b05018bbc70d54b6f41e53e7738 100644 +index c723606fa0be811e580ba47de8c9c575583cc930..2483210ca43221feaa5a2f1ced5c59731d5189fc 100644 --- a/net/minecraft/world/level/chunk/PaletteResize.java +++ b/net/minecraft/world/level/chunk/PaletteResize.java @@ -1,5 +1,5 @@ package net.minecraft.world.level.chunk; -interface PaletteResize { -+public interface PaletteResize { // Leaves - package -> public ++public interface PaletteResize { int onResize(int bits, T objectAdded); } diff --git a/net/minecraft/world/level/chunk/PalettedContainer.java b/net/minecraft/world/level/chunk/PalettedContainer.java -index a251ba67644cd02a0b00d7c8b0e2c64aa5e26291..c77426409e35530df106ade1c8080b9ab6d539c7 100644 +index a251ba67644cd02a0b00d7c8b0e2c64aa5e26291..59d48e7dc0911557c57a7e07f5f9013c010165bd 100644 --- a/net/minecraft/world/level/chunk/PalettedContainer.java +++ b/net/minecraft/world/level/chunk/PalettedContainer.java -@@ -24,6 +24,24 @@ import net.minecraft.util.ThreadingDetector; +@@ -24,6 +24,22 @@ import net.minecraft.util.ThreadingDetector; import net.minecraft.util.ZeroBitStorage; public class PalettedContainer implements PaletteResize, PalettedContainerRO { + -+ // Leaves start - faster chunk serialization ++ // Gale start - Lithium - faster chunk serialization + private static final ThreadLocal CACHED_ARRAY_4096 = ThreadLocal.withInitial(() -> new short[4096]); + private static final ThreadLocal CACHED_ARRAY_64 = ThreadLocal.withInitial(() -> new short[64]); -+ + private Optional asOptional(long[] data) { + return Optional.of(Arrays.stream(data)); + } -+ + private short[] getOrCreate(int size) { + return switch (size) { + case 64 -> CACHED_ARRAY_64.get(); @@ -115,17 +121,20 @@ index a251ba67644cd02a0b00d7c8b0e2c64aa5e26291..c77426409e35530df106ade1c8080b9a + default -> new short[size]; + }; + } -+ // Leaves end - faster chunk serialization ++ // Gale end - Lithium - faster chunk serialization + private static final int MIN_PALETTE_BITS = 0; private final PaletteResize dummyPaletteResize = (bits, objectAdded) -> 0; public final IdMap registry; -@@ -343,28 +361,76 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -343,28 +359,54 @@ public class PalettedContainer implements PaletteResize, PalettedContainer public synchronized PalettedContainerRO.PackedData pack(IdMap registry, PalettedContainer.Strategy strategy) { // Paper - synchronize this.acquire(); - PalettedContainerRO.PackedData var12; -- try { ++ // Gale start - Lithium - faster chunk serialization ++ Optional data = Optional.empty(); ++ List elements = null; + try { - HashMapPalette hashMapPalette = new HashMapPalette<>(registry, this.data.storage.getBits(), this.dummyPaletteResize); - int size = strategy.size(); - int[] ints = new int[size]; @@ -138,135 +147,92 @@ index a251ba67644cd02a0b00d7c8b0e2c64aa5e26291..c77426409e35530df106ade1c8080b9a - optional = Optional.of(Arrays.stream(simpleBitStorage.getRaw())); - } else { - optional = Optional.empty(); -- } -+ // Leaves start - faster chunk serialization -+ if (!org.leavesmc.leaves.LeavesConfig.performance.fasterChunkSerialization) { -+ PalettedContainerRO.PackedData var12; -+ try { -+ HashMapPalette hashMapPalette = new HashMapPalette<>(registry, this.data.storage.getBits(), this.dummyPaletteResize); -+ int i = strategy.size(); -+ int[] is = new int[i]; -+ this.data.storage.unpack(is); -+ swapPalette(is, (id) -> { -+ return hashMapPalette.idFor(this.data.palette.valueFor(id)); -+ }); -+ int j = strategy.calculateBitsForSerialization(registry, hashMapPalette.getSize()); -+ Optional optional; -+ if (j != 0) { -+ SimpleBitStorage simpleBitStorage = new SimpleBitStorage(j, i, is); -+ optional = Optional.of(Arrays.stream(simpleBitStorage.getRaw())); -+ } else { -+ optional = Optional.empty(); -+ } ++ // The palette that will be serialized ++ net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette hashPalette = null; ++ ++ final Palette palette = this.data.palette(); ++ final BitStorage storage = this.data.storage(); ++ if (storage instanceof ZeroBitStorage || palette.getSize() == 1) { ++ // If the palette only contains one entry, don't attempt to repack it. ++ elements = List.of(palette.valueFor(0)); ++ } else if (palette instanceof net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette lithiumHashPalette) { ++ hashPalette = lithiumHashPalette; + } - var12 = new PalettedContainerRO.PackedData<>(hashMapPalette.getEntries(), optional); -- } finally { -- this.release(); -+ var12 = new PalettedContainerRO.PackedData<>(hashMapPalette.getEntries(), optional); -+ } finally { -+ this.release(); -+ } -+ return var12; -+ } else { -+ Optional data = Optional.empty(); -+ List elements = null; -+ try { -+ // The palette that will be serialized -+ org.leavesmc.leaves.lithium.common.world.chunk.LithiumHashPalette hashPalette = null; ++ if (elements == null) { ++ net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette compactedPalette = new net.caffeinemc.mods.lithium.common.world.chunk.LithiumHashPalette<>(registry, storage.getBits(), this.dummyPaletteResize); ++ short[] array = this.getOrCreate(strategy.size()); + -+ final Palette palette = this.data.palette(); -+ final BitStorage storage = this.data.storage(); -+ if (storage instanceof ZeroBitStorage || palette.getSize() == 1) { -+ // If the palette only contains one entry, don't attempt to repack it. -+ elements = List.of(palette.valueFor(0)); -+ } else if (palette instanceof org.leavesmc.leaves.lithium.common.world.chunk.LithiumHashPalette lithiumHashPalette) { -+ hashPalette = lithiumHashPalette; -+ } -+ if (elements == null) { -+ org.leavesmc.leaves.lithium.common.world.chunk.LithiumHashPalette compactedPalette = new org.leavesmc.leaves.lithium.common.world.chunk.LithiumHashPalette<>(registry, storage.getBits(), this.dummyPaletteResize); -+ short[] array = this.getOrCreate(strategy.size()); ++ storage.compact(this.data.palette(), compactedPalette, array); + -+ storage.compact(this.data.palette(), compactedPalette, array); -+ -+ // If the palette didn't change during compaction, do a simple copy of the data array -+ if (hashPalette != null && hashPalette.getSize() == compactedPalette.getSize() && storage.getBits() == strategy.calculateBitsForSerialization(registry, hashPalette.getSize())) { // paletteSize can de-sync from palette - see https://github.com/CaffeineMC/lithium-fabric/issues/279 -+ data = this.asOptional(storage.getRaw().clone()); -+ elements = hashPalette.getElements(); -+ } else { -+ int bits = strategy.calculateBitsForSerialization(registry, compactedPalette.getSize()); -+ if (bits != 0) { -+ // Re-pack the integer array as the palette has changed size -+ SimpleBitStorage copy = new SimpleBitStorage(bits, array.length); -+ for (int i = 0; i < array.length; ++i) { -+ copy.set(i, array[i]); -+ } -+ // We don't need to clone the data array as we are the sole owner of it -+ data = this.asOptional(copy.getRaw()); ++ // If the palette didn't change during compaction, do a simple copy of the data array ++ if (hashPalette != null && hashPalette.getSize() == compactedPalette.getSize() && storage.getBits() == strategy.calculateBitsForSerialization(registry, hashPalette.getSize())) { // paletteSize can de-sync from palette - see https://github.com/CaffeineMC/lithium-fabric/issues/279 ++ data = this.asOptional(storage.getRaw().clone()); ++ elements = hashPalette.getElements(); ++ } else { ++ int bits = strategy.calculateBitsForSerialization(registry, compactedPalette.getSize()); ++ if (bits != 0) { ++ // Re-pack the integer array as the palette has changed size ++ SimpleBitStorage copy = new SimpleBitStorage(bits, array.length); ++ for (int i = 0; i < array.length; ++i) { ++ copy.set(i, array[i]); + } ++ ++ // We don't need to clone the data array as we are the sole owner of it ++ data = this.asOptional(copy.getRaw()); + } ++ + elements = compactedPalette.getElements(); + } -+ } finally { -+ this.release(); + } -+ return new PalettedContainerRO.PackedData<>(elements, data); + } finally { + this.release(); } -- + - return var12; -+ // Leaves end - faster chunk serialization ++ return new PalettedContainerRO.PackedData<>(elements, data); ++ // Gale end - Lithium - faster chunk serialization } private static void swapPalette(int[] bits, IntUnaryOperator operator) { -@@ -404,13 +470,47 @@ public class PalettedContainer implements PaletteResize, PalettedContainer +@@ -404,13 +446,33 @@ public class PalettedContainer implements PaletteResize, PalettedContainer @Override public void count(PalettedContainer.CountConsumer countConsumer) { - if (this.data.palette.getSize() == 1) { - countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); -+ // Leaves start - faster chunk serialization -+ if (!org.leavesmc.leaves.LeavesConfig.performance.fasterChunkSerialization) { -+ if (this.data.palette.getSize() == 1) { -+ countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); -+ } else { -+ Int2IntOpenHashMap int2IntOpenHashMap = new Int2IntOpenHashMap(); -+ this.data.storage.getAll((key) -> { -+ int2IntOpenHashMap.addTo(key, 1); -+ }); -+ int2IntOpenHashMap.int2IntEntrySet().forEach((entry) -> { -+ countConsumer.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue()); -+ }); -+ } - } else { +- } else { - Int2IntOpenHashMap map = new Int2IntOpenHashMap(); - this.data.storage.getAll(id -> map.addTo(id, 1)); - map.int2IntEntrySet().forEach(idEntry -> countConsumer.accept(this.data.palette.valueFor(idEntry.getIntKey()), idEntry.getIntValue())); -+ int len = this.data.palette().getSize(); ++ // Gale start - Lithium - faster chunk serialization ++ int len = this.data.palette().getSize(); + -+ // Do not allocate huge arrays if we're using a large palette -+ if (len > 4096) { -+ if (this.data.palette.getSize() == 1) { -+ countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); -+ } else { -+ Int2IntOpenHashMap int2IntOpenHashMap = new Int2IntOpenHashMap(); -+ this.data.storage.getAll((key) -> { -+ int2IntOpenHashMap.addTo(key, 1); -+ }); -+ int2IntOpenHashMap.int2IntEntrySet().forEach((entry) -> { -+ countConsumer.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue()); -+ }); -+ } ++ // Do not allocate huge arrays if we're using a large palette ++ if (len > 4096) { ++ // VanillaCopy ++ if (this.data.palette.getSize() == 1) { ++ countConsumer.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); ++ } else { ++ Int2IntOpenHashMap map = new Int2IntOpenHashMap(); ++ this.data.storage.getAll(id -> map.addTo(id, 1)); ++ map.int2IntEntrySet().forEach(idEntry -> countConsumer.accept(this.data.palette.valueFor(idEntry.getIntKey()), idEntry.getIntValue())); + } ++ } + -+ short[] counts = new short[len]; -+ this.data.storage().getAll(i -> counts[i]++); -+ for (int i = 0; i < counts.length; i++) { -+ T obj = this.data.palette().valueFor(i); -+ if (obj != null) { -+ countConsumer.accept(obj, counts[i]); -+ } ++ short[] counts = new short[len]; ++ ++ this.data.storage().getAll(i -> counts[i]++); ++ ++ for (int i = 0; i < counts.length; i++) { ++ T obj = this.data.palette().valueFor(i); ++ ++ if (obj != null) { ++ countConsumer.accept(obj, counts[i]); + } } -+ // Leaves end - faster chunk serialization ++ // Gale end - Lithium - faster chunk serialization } record Configuration(Palette.Factory factory, int bits) {