From 75dfa5046cfa70dd43273fe1accfe2e7c0767321 Mon Sep 17 00:00:00 2001 From: MartijnMuijsers Date: Wed, 30 Nov 2022 21:59:10 +0100 Subject: [PATCH] Faster chunk serialization --- .../0075-Faster-chunk-serialization.patch | 470 ++++++++++++++++++ ...kip-entity-move-if-movement-is-zero.patch} | 0 ...> 0077-Store-mob-counts-in-an-array.patch} | 0 ...h => 0078-Optimize-noise-generation.patch} | 0 ...durability-change-equipment-updates.patch} | 0 ...es-on-entities-with-fire-resistance.patch} | 0 ...1-Skip-cloning-advancement-criteria.patch} | 0 ...> 0082-Player-canSee-by-entity-UUID.patch} | 0 ...-Spread-out-sending-all-player-info.patch} | 0 ...player-list-for-sending-player-info.patch} | 0 ...dSendEvent-if-there-are-no-listener.patch} | 0 ...86-Send-multiple-keep-alive-packets.patch} | 0 ...m-strolling-into-non-ticking-chunks.patch} | 0 ...h => 0088-Specific-interval-TPS-API.patch} | 0 ....patch => 0089-5-second-TPS-average.patch} | 0 ...atch => 0090-Measure-last-tick-time.patch} | 0 ...PI.patch => 0091-Last-tick-time-API.patch} | 0 ...-Show-last-tick-time-in-tps-command.patch} | 0 ...eck-has-physics-before-same-vehicle.patch} | 0 ...094-Variable-main-thread-task-delay.patch} | 0 ... 0095-Reduce-RandomSource-instances.patch} | 0 ....patch => 0096-CPU-cores-estimation.patch} | 0 ... 0097-Add-centralized-AsyncExecutor.patch} | 0 ...=> 0098-Remove-Paper-async-executor.patch} | 0 ... 0099-Remove-Paper-cleaner-executor.patch} | 0 ... => 0100-Remove-background-executor.patch} | 0 ...h => 0101-Remove-bootstrap-executor.patch} | 0 ...0102-Remove-world-upgrade-executors.patch} | 0 ...> 0103-Remove-tab-complete-executor.patch} | 0 ...=> 0104-Remove-text-filter-executor.patch} | 0 30 files changed, 470 insertions(+) create mode 100644 patches/server/0075-Faster-chunk-serialization.patch rename patches/server/{0075-Skip-entity-move-if-movement-is-zero.patch => 0076-Skip-entity-move-if-movement-is-zero.patch} (100%) rename patches/server/{0076-Store-mob-counts-in-an-array.patch => 0077-Store-mob-counts-in-an-array.patch} (100%) rename patches/server/{0077-Optimize-noise-generation.patch => 0078-Optimize-noise-generation.patch} (100%) rename patches/server/{0078-Ignore-durability-change-equipment-updates.patch => 0079-Ignore-durability-change-equipment-updates.patch} (100%) rename patches/server/{0079-Hide-flames-on-entities-with-fire-resistance.patch => 0080-Hide-flames-on-entities-with-fire-resistance.patch} (100%) rename patches/server/{0080-Skip-cloning-advancement-criteria.patch => 0081-Skip-cloning-advancement-criteria.patch} (100%) rename patches/server/{0081-Player-canSee-by-entity-UUID.patch => 0082-Player-canSee-by-entity-UUID.patch} (100%) rename patches/server/{0082-Spread-out-sending-all-player-info.patch => 0083-Spread-out-sending-all-player-info.patch} (100%) rename patches/server/{0083-Optimize-player-list-for-sending-player-info.patch => 0084-Optimize-player-list-for-sending-player-info.patch} (100%) rename patches/server/{0084-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch => 0085-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch} (100%) rename patches/server/{0085-Send-multiple-keep-alive-packets.patch => 0086-Send-multiple-keep-alive-packets.patch} (100%) rename patches/server/{0086-Prevent-random-strolling-into-non-ticking-chunks.patch => 0087-Prevent-random-strolling-into-non-ticking-chunks.patch} (100%) rename patches/server/{0087-Specific-interval-TPS-API.patch => 0088-Specific-interval-TPS-API.patch} (100%) rename patches/server/{0088-5-second-TPS-average.patch => 0089-5-second-TPS-average.patch} (100%) rename patches/server/{0089-Measure-last-tick-time.patch => 0090-Measure-last-tick-time.patch} (100%) rename patches/server/{0090-Last-tick-time-API.patch => 0091-Last-tick-time-API.patch} (100%) rename patches/server/{0091-Show-last-tick-time-in-tps-command.patch => 0092-Show-last-tick-time-in-tps-command.patch} (100%) rename patches/server/{0092-For-collision-check-has-physics-before-same-vehicle.patch => 0093-For-collision-check-has-physics-before-same-vehicle.patch} (100%) rename patches/server/{0093-Variable-main-thread-task-delay.patch => 0094-Variable-main-thread-task-delay.patch} (100%) rename patches/server/{0094-Reduce-RandomSource-instances.patch => 0095-Reduce-RandomSource-instances.patch} (100%) rename patches/server/{0095-CPU-cores-estimation.patch => 0096-CPU-cores-estimation.patch} (100%) rename patches/server/{0096-Add-centralized-AsyncExecutor.patch => 0097-Add-centralized-AsyncExecutor.patch} (100%) rename patches/server/{0097-Remove-Paper-async-executor.patch => 0098-Remove-Paper-async-executor.patch} (100%) rename patches/server/{0098-Remove-Paper-cleaner-executor.patch => 0099-Remove-Paper-cleaner-executor.patch} (100%) rename patches/server/{0099-Remove-background-executor.patch => 0100-Remove-background-executor.patch} (100%) rename patches/server/{0100-Remove-bootstrap-executor.patch => 0101-Remove-bootstrap-executor.patch} (100%) rename patches/server/{0101-Remove-world-upgrade-executors.patch => 0102-Remove-world-upgrade-executors.patch} (100%) rename patches/server/{0102-Remove-tab-complete-executor.patch => 0103-Remove-tab-complete-executor.patch} (100%) rename patches/server/{0103-Remove-text-filter-executor.patch => 0104-Remove-text-filter-executor.patch} (100%) diff --git a/patches/server/0075-Faster-chunk-serialization.patch b/patches/server/0075-Faster-chunk-serialization.patch new file mode 100644 index 0000000..7efcb71 --- /dev/null +++ b/patches/server/0075-Faster-chunk-serialization.patch @@ -0,0 +1,470 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MartijnMuijsers +Date: Wed, 30 Nov 2022 21:51:16 +0100 +Subject: [PATCH] Faster chunk serialization + +License: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) + +This patch is based on the following mixins and classes: +* "me/jellysquid/mods/lithium/common/world/chunk/CompactingPackedIntegerArray.java" +* "me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java" +* "me/jellysquid/mods/lithium/mixin/chunk/serialization/PackedIntegerArrayMixin.java" +* "me/jellysquid/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.nl.html) + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java +new file mode 100644 +index 0000000000000000000000000000000000000000..28ece835ca32532c8a3547e7b2122d20664fb23d +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java +@@ -0,0 +1,191 @@ ++// Gale - Lithium - faster chunk serialization ++ ++package me.jellysquid.mods.lithium.common.world.chunk; ++ ++import com.google.common.collect.ImmutableList; ++import it.unimi.dsi.fastutil.HashCommon; ++import it.unimi.dsi.fastutil.objects.Reference2IntMap; ++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; ++import java.util.Arrays; ++import java.util.List; ++import java.util.function.Predicate; ++import net.minecraft.core.IdMap; ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.world.level.chunk.Palette; ++import net.minecraft.world.level.chunk.PaletteResize; ++ ++import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; ++ ++/** ++ * Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling ++ * {@link LithiumHashPalette#idFor(Object)} through using a faster backing map and reducing pointer chasing. ++ */ ++public class LithiumHashPalette implements Palette { ++ private static final int ABSENT_VALUE = -1; ++ ++ private final IdMap idList; ++ private final PaletteResize resizeHandler; ++ private final int indexBits; ++ ++ private final Reference2IntMap table; ++ private T[] entries; ++ private int size = 0; ++ ++ public LithiumHashPalette(IdMap idList, PaletteResize resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { ++ this.idList = idList; ++ this.resizeHandler = resizeHandler; ++ this.indexBits = indexBits; ++ this.entries = entries; ++ this.table = table; ++ this.size = size; ++ } ++ ++ public LithiumHashPalette(IdMap idList, int bits, PaletteResize resizeHandler, List list) { ++ this(idList, bits, resizeHandler); ++ ++ for (T t : list) { ++ this.addEntry(t); ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ public LithiumHashPalette(IdMap idList, int bits, PaletteResize resizeHandler) { ++ this.idList = idList; ++ this.indexBits = bits; ++ this.resizeHandler = resizeHandler; ++ ++ int capacity = 1 << bits; ++ ++ this.entries = (T[]) new Object[capacity]; ++ this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); ++ this.table.defaultReturnValue(ABSENT_VALUE); ++ } ++ ++ @Override ++ public int idFor(T obj) { ++ int id = this.table.getInt(obj); ++ ++ if (id == ABSENT_VALUE) { ++ id = this.computeEntry(obj); ++ } ++ ++ return id; ++ } ++ ++ @Override ++ public boolean maybeHas(Predicate predicate) { ++ for (int i = 0; i < this.size; ++i) { ++ if (predicate.test(this.entries[i])) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ private int computeEntry(T obj) { ++ int id = this.addEntry(obj); ++ ++ if (id >= 1 << this.indexBits) { ++ if (this.resizeHandler == null) { ++ throw new IllegalStateException("Cannot grow"); ++ } else { ++ id = this.resizeHandler.onResize(this.indexBits + 1, obj); ++ } ++ } ++ ++ return id; ++ } ++ ++ private int addEntry(T obj) { ++ int nextId = this.size; ++ ++ if (nextId >= this.entries.length) { ++ this.resize(this.size); ++ } ++ ++ this.table.put(obj, nextId); ++ this.entries[nextId] = obj; ++ ++ this.size++; ++ ++ return nextId; ++ } ++ ++ private void resize(int neededCapacity) { ++ this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); ++ } ++ ++ @Override ++ public T valueFor(int id) { ++ T[] entries = this.entries; ++ ++ if (id >= 0 && id < entries.length) { ++ return entries[id]; ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public void read(FriendlyByteBuf buf) { ++ this.clear(); ++ ++ int entryCount = buf.readVarInt(); ++ ++ for (int i = 0; i < entryCount; ++i) { ++ this.addEntry(this.idList.byId(buf.readVarInt())); ++ } ++ } ++ ++ @Override ++ public void write(FriendlyByteBuf buf) { ++ int size = this.size; ++ buf.writeVarInt(size); ++ ++ for (int i = 0; i < size; ++i) { ++ buf.writeVarInt(this.idList.getId(this.valueFor(i))); ++ } ++ } ++ ++ @Override ++ public int getSerializedSize() { ++ int size = FriendlyByteBuf.getVarIntSize(this.size); ++ ++ for (int i = 0; i < this.size; ++i) { ++ size += FriendlyByteBuf.getVarIntSize(this.idList.getId(this.valueFor(i))); ++ } ++ ++ return size; ++ } ++ ++ @Override ++ public int getSize() { ++ return this.size; ++ } ++ ++ @Override ++ public Palette copy() { ++ return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); ++ } ++ ++ private void clear() { ++ Arrays.fill(this.entries, null); ++ this.table.clear(); ++ this.size = 0; ++ } ++ ++ public List getElements() { ++ ImmutableList.Builder builder = new ImmutableList.Builder<>(); ++ for (T entry : this.entries) { ++ if (entry != null) { ++ builder.add(entry); ++ } ++ } ++ return builder.build(); ++ } ++ ++ public static Palette create(int bits, IdMap idList, PaletteResize listener, List list) { ++ return new LithiumHashPalette<>(idList, bits, listener, list); ++ } ++} +diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java +index 106610ccc74b70b557b01c61262d56c4f1147acf..ccf2a8544cf4cd1865d29b4e8cb8c058c74c7fe9 100644 +--- a/src/main/java/net/minecraft/util/BitStorage.java ++++ b/src/main/java/net/minecraft/util/BitStorage.java +@@ -1,6 +1,7 @@ + package net.minecraft.util; + + import java.util.function.IntConsumer; ++import net.minecraft.world.level.chunk.Palette; + + public interface BitStorage { + int getAndSet(int index, int value); +@@ -31,4 +32,6 @@ public interface BitStorage { + + } + // Paper end ++ ++ void compact(Palette srcPalette, Palette dstPalette, short[] out); // Gale - Lithium - faster chunk serialization + } +diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java +index 36e33923bf48e56c743ed043bcbc66bc32f0422f..9c8b23640fa45764601242c9345071267795a17f 100644 +--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java ++++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java +@@ -2,6 +2,7 @@ package net.minecraft.util; + + import java.util.function.IntConsumer; + import javax.annotation.Nullable; ++import net.minecraft.world.level.chunk.Palette; + import org.apache.commons.lang3.Validate; + + public class SimpleBitStorage implements BitStorage { +@@ -201,4 +202,45 @@ public class SimpleBitStorage implements BitStorage { + super(message); + } + } ++ ++ // Gale start - Lithium - faster chunk serialization ++ @Override ++ public void compact(Palette srcPalette, Palette dstPalette, short[] out) { ++ if (this.size >= Short.MAX_VALUE) { ++ throw new IllegalStateException("Array too large"); ++ } ++ ++ if (this.size != out.length) { ++ throw new IllegalStateException("Array size mismatch"); ++ } ++ ++ short[] mappings = new short[(int) (this.mask + 1)]; ++ ++ int idx = 0; ++ ++ for (long word : this.data) { ++ long bits = word; ++ ++ for (int elementIdx = 0; elementIdx < this.valuesPerLong; ++elementIdx) { ++ int value = (int) (bits & this.mask); ++ int remappedId = mappings[value]; ++ ++ if (remappedId == 0) { ++ remappedId = dstPalette.idFor(srcPalette.valueFor(value)) + 1; ++ mappings[value] = (short) remappedId; ++ } ++ ++ out[idx] = (short) (remappedId - 1); ++ bits >>= this.bits; ++ ++ ++idx; ++ ++ if (idx >= this.size) { ++ return; ++ } ++ } ++ } ++ } ++ // Gale end - Lithium - faster chunk serialization ++ + } +diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java +index 75ca7ae6028e971f73988f5e71598696c0765cc8..41e795b67a138dea11f6d0270e7c36072810ef21 100644 +--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java ++++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java +@@ -2,6 +2,7 @@ package net.minecraft.util; + + import java.util.Arrays; + import java.util.function.IntConsumer; ++import net.minecraft.world.level.chunk.Palette; + + import me.titaniumtown.ArrayConstants; + +@@ -73,4 +74,6 @@ public class ZeroBitStorage implements BitStorage { + public BitStorage copy() { + return this; + } ++ ++ @Override public void compact(Palette srcPalette, Palette dstPalette, short[] out) {} // Gale - Lithium - faster chunk serialization + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java b/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java +index acae3eb30e0689048937f479dc3070f0688abdad..4b79f0474a9013dd4fdb68c6363ca1942ba8b007 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java +@@ -1,5 +1,5 @@ + package net.minecraft.world.level.chunk; + +-interface PaletteResize { ++public interface PaletteResize { // Gale - Lithium - faster chunk serialization - package -> public + int onResize(int newBits, T object); + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +index 7908360dd47937b2cb702e381802b7b278a5198e..1da043e5883dcde484fbbaab11dda3ec4a6e2481 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -22,8 +22,25 @@ import net.minecraft.util.Mth; + import net.minecraft.util.SimpleBitStorage; + import net.minecraft.util.ThreadingDetector; + import net.minecraft.util.ZeroBitStorage; ++import me.jellysquid.mods.lithium.common.world.chunk.LithiumHashPalette; + + public class PalettedContainer implements PaletteResize, PalettedContainerRO { ++ ++ // 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(); ++ case 4096 -> CACHED_ARRAY_4096.get(); ++ default -> new short[size]; ++ }; ++ } ++ // Gale end - Lithium - faster chunk serialization ++ + private static final int MIN_PALETTE_BITS = 0; + private final PaletteResize dummyPaletteResize = (newSize, added) -> { + return 0; +@@ -299,30 +316,54 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + public synchronized PalettedContainerRO.PackedData pack(IdMap idList, PalettedContainer.Strategy paletteProvider) { // Paper - synchronize + this.acquire(); + +- PalettedContainerRO.PackedData var12; ++ // Gale start - Lithium - faster chunk serialization ++ Optional data = Optional.empty(); ++ List elements = null; + try { +- HashMapPalette hashMapPalette = new HashMapPalette<>(idList, this.data.storage.getBits(), this.dummyPaletteResize); +- int i = paletteProvider.size(); +- int[] is = new int[i]; +- this.data.storage.unpack(is); +- swapPalette(is, (id) -> { +- return hashMapPalette.idFor(this.data.palette.valueFor(id)); +- }); +- int j = paletteProvider.calculateBitsForSerialization(idList, 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 ++ 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 LithiumHashPalette lithiumHashPalette) { ++ hashPalette = lithiumHashPalette; + } + +- var12 = new PalettedContainerRO.PackedData<>(hashMapPalette.getEntries(), optional); ++ if (elements == null) { ++ LithiumHashPalette compactedPalette = new LithiumHashPalette<>(idList, storage.getBits(), this.dummyPaletteResize); ++ short[] array = this.getOrCreate(paletteProvider.size()); ++ ++ 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() == paletteProvider.calculateBitsForSerialization(idList, 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 = paletteProvider.calculateBitsForSerialization(idList, 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 var12; ++ return new PalettedContainerRO.PackedData<>(elements, data); ++ // Gale end - Lithium - faster chunk serialization + } + + private static void swapPalette(int[] is, IntUnaryOperator applier) { +@@ -362,17 +403,37 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + + @Override + public void count(PalettedContainer.CountConsumer counter) { +- if (this.data.palette.getSize() == 1) { +- counter.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) -> { +- counter.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue()); +- }); ++ // 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) { ++ // VanillaCopy ++ if (this.data.palette.getSize() == 1) { ++ counter.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) -> { ++ counter.accept(this.data.palette.valueFor(entry.getIntKey()), entry.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) { ++ counter.accept(obj, counts[i]); ++ } + } ++ // Gale end - Lithium - faster chunk serialization + } + + static record Configuration(Palette.Factory factory, int bits) { diff --git a/patches/server/0075-Skip-entity-move-if-movement-is-zero.patch b/patches/server/0076-Skip-entity-move-if-movement-is-zero.patch similarity index 100% rename from patches/server/0075-Skip-entity-move-if-movement-is-zero.patch rename to patches/server/0076-Skip-entity-move-if-movement-is-zero.patch diff --git a/patches/server/0076-Store-mob-counts-in-an-array.patch b/patches/server/0077-Store-mob-counts-in-an-array.patch similarity index 100% rename from patches/server/0076-Store-mob-counts-in-an-array.patch rename to patches/server/0077-Store-mob-counts-in-an-array.patch diff --git a/patches/server/0077-Optimize-noise-generation.patch b/patches/server/0078-Optimize-noise-generation.patch similarity index 100% rename from patches/server/0077-Optimize-noise-generation.patch rename to patches/server/0078-Optimize-noise-generation.patch diff --git a/patches/server/0078-Ignore-durability-change-equipment-updates.patch b/patches/server/0079-Ignore-durability-change-equipment-updates.patch similarity index 100% rename from patches/server/0078-Ignore-durability-change-equipment-updates.patch rename to patches/server/0079-Ignore-durability-change-equipment-updates.patch diff --git a/patches/server/0079-Hide-flames-on-entities-with-fire-resistance.patch b/patches/server/0080-Hide-flames-on-entities-with-fire-resistance.patch similarity index 100% rename from patches/server/0079-Hide-flames-on-entities-with-fire-resistance.patch rename to patches/server/0080-Hide-flames-on-entities-with-fire-resistance.patch diff --git a/patches/server/0080-Skip-cloning-advancement-criteria.patch b/patches/server/0081-Skip-cloning-advancement-criteria.patch similarity index 100% rename from patches/server/0080-Skip-cloning-advancement-criteria.patch rename to patches/server/0081-Skip-cloning-advancement-criteria.patch diff --git a/patches/server/0081-Player-canSee-by-entity-UUID.patch b/patches/server/0082-Player-canSee-by-entity-UUID.patch similarity index 100% rename from patches/server/0081-Player-canSee-by-entity-UUID.patch rename to patches/server/0082-Player-canSee-by-entity-UUID.patch diff --git a/patches/server/0082-Spread-out-sending-all-player-info.patch b/patches/server/0083-Spread-out-sending-all-player-info.patch similarity index 100% rename from patches/server/0082-Spread-out-sending-all-player-info.patch rename to patches/server/0083-Spread-out-sending-all-player-info.patch diff --git a/patches/server/0083-Optimize-player-list-for-sending-player-info.patch b/patches/server/0084-Optimize-player-list-for-sending-player-info.patch similarity index 100% rename from patches/server/0083-Optimize-player-list-for-sending-player-info.patch rename to patches/server/0084-Optimize-player-list-for-sending-player-info.patch diff --git a/patches/server/0084-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch b/patches/server/0085-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch similarity index 100% rename from patches/server/0084-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch rename to patches/server/0085-Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch diff --git a/patches/server/0085-Send-multiple-keep-alive-packets.patch b/patches/server/0086-Send-multiple-keep-alive-packets.patch similarity index 100% rename from patches/server/0085-Send-multiple-keep-alive-packets.patch rename to patches/server/0086-Send-multiple-keep-alive-packets.patch diff --git a/patches/server/0086-Prevent-random-strolling-into-non-ticking-chunks.patch b/patches/server/0087-Prevent-random-strolling-into-non-ticking-chunks.patch similarity index 100% rename from patches/server/0086-Prevent-random-strolling-into-non-ticking-chunks.patch rename to patches/server/0087-Prevent-random-strolling-into-non-ticking-chunks.patch diff --git a/patches/server/0087-Specific-interval-TPS-API.patch b/patches/server/0088-Specific-interval-TPS-API.patch similarity index 100% rename from patches/server/0087-Specific-interval-TPS-API.patch rename to patches/server/0088-Specific-interval-TPS-API.patch diff --git a/patches/server/0088-5-second-TPS-average.patch b/patches/server/0089-5-second-TPS-average.patch similarity index 100% rename from patches/server/0088-5-second-TPS-average.patch rename to patches/server/0089-5-second-TPS-average.patch diff --git a/patches/server/0089-Measure-last-tick-time.patch b/patches/server/0090-Measure-last-tick-time.patch similarity index 100% rename from patches/server/0089-Measure-last-tick-time.patch rename to patches/server/0090-Measure-last-tick-time.patch diff --git a/patches/server/0090-Last-tick-time-API.patch b/patches/server/0091-Last-tick-time-API.patch similarity index 100% rename from patches/server/0090-Last-tick-time-API.patch rename to patches/server/0091-Last-tick-time-API.patch diff --git a/patches/server/0091-Show-last-tick-time-in-tps-command.patch b/patches/server/0092-Show-last-tick-time-in-tps-command.patch similarity index 100% rename from patches/server/0091-Show-last-tick-time-in-tps-command.patch rename to patches/server/0092-Show-last-tick-time-in-tps-command.patch diff --git a/patches/server/0092-For-collision-check-has-physics-before-same-vehicle.patch b/patches/server/0093-For-collision-check-has-physics-before-same-vehicle.patch similarity index 100% rename from patches/server/0092-For-collision-check-has-physics-before-same-vehicle.patch rename to patches/server/0093-For-collision-check-has-physics-before-same-vehicle.patch diff --git a/patches/server/0093-Variable-main-thread-task-delay.patch b/patches/server/0094-Variable-main-thread-task-delay.patch similarity index 100% rename from patches/server/0093-Variable-main-thread-task-delay.patch rename to patches/server/0094-Variable-main-thread-task-delay.patch diff --git a/patches/server/0094-Reduce-RandomSource-instances.patch b/patches/server/0095-Reduce-RandomSource-instances.patch similarity index 100% rename from patches/server/0094-Reduce-RandomSource-instances.patch rename to patches/server/0095-Reduce-RandomSource-instances.patch diff --git a/patches/server/0095-CPU-cores-estimation.patch b/patches/server/0096-CPU-cores-estimation.patch similarity index 100% rename from patches/server/0095-CPU-cores-estimation.patch rename to patches/server/0096-CPU-cores-estimation.patch diff --git a/patches/server/0096-Add-centralized-AsyncExecutor.patch b/patches/server/0097-Add-centralized-AsyncExecutor.patch similarity index 100% rename from patches/server/0096-Add-centralized-AsyncExecutor.patch rename to patches/server/0097-Add-centralized-AsyncExecutor.patch diff --git a/patches/server/0097-Remove-Paper-async-executor.patch b/patches/server/0098-Remove-Paper-async-executor.patch similarity index 100% rename from patches/server/0097-Remove-Paper-async-executor.patch rename to patches/server/0098-Remove-Paper-async-executor.patch diff --git a/patches/server/0098-Remove-Paper-cleaner-executor.patch b/patches/server/0099-Remove-Paper-cleaner-executor.patch similarity index 100% rename from patches/server/0098-Remove-Paper-cleaner-executor.patch rename to patches/server/0099-Remove-Paper-cleaner-executor.patch diff --git a/patches/server/0099-Remove-background-executor.patch b/patches/server/0100-Remove-background-executor.patch similarity index 100% rename from patches/server/0099-Remove-background-executor.patch rename to patches/server/0100-Remove-background-executor.patch diff --git a/patches/server/0100-Remove-bootstrap-executor.patch b/patches/server/0101-Remove-bootstrap-executor.patch similarity index 100% rename from patches/server/0100-Remove-bootstrap-executor.patch rename to patches/server/0101-Remove-bootstrap-executor.patch diff --git a/patches/server/0101-Remove-world-upgrade-executors.patch b/patches/server/0102-Remove-world-upgrade-executors.patch similarity index 100% rename from patches/server/0101-Remove-world-upgrade-executors.patch rename to patches/server/0102-Remove-world-upgrade-executors.patch diff --git a/patches/server/0102-Remove-tab-complete-executor.patch b/patches/server/0103-Remove-tab-complete-executor.patch similarity index 100% rename from patches/server/0102-Remove-tab-complete-executor.patch rename to patches/server/0103-Remove-tab-complete-executor.patch diff --git a/patches/server/0103-Remove-text-filter-executor.patch b/patches/server/0104-Remove-text-filter-executor.patch similarity index 100% rename from patches/server/0103-Remove-text-filter-executor.patch rename to patches/server/0104-Remove-text-filter-executor.patch