From e47a923a783cc8e89bef03abaccd440686984f4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:35:34 +0000 Subject: [PATCH 1/8] Initial plan From 17fe0f8ee9635521c651686056943ffae06f9ced Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:40:28 +0000 Subject: [PATCH 2/8] Add benchmark tests for Polar world loading Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- Tests/PolarBenchmark.cs | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Tests/PolarBenchmark.cs diff --git a/Tests/PolarBenchmark.cs b/Tests/PolarBenchmark.cs new file mode 100644 index 00000000..d8846b85 --- /dev/null +++ b/Tests/PolarBenchmark.cs @@ -0,0 +1,100 @@ +using System.Diagnostics; +using Minecraft.Data.Generated; +using Minecraft.Schemas.Chunks; +using PolarWorlds; + +namespace Tests; + +public class PolarBenchmark { + + [Test] + public void BenchmarkPolarWorldCreation() { + Console.WriteLine("=== Polar World Creation Benchmark ==="); + + // Create test chunks with random data + const int chunkCount = 10; + ChunkData[] chunks = new ChunkData[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + chunks[i] = new ChunkData(384) { + ChunkX = i * 32, + ChunkZ = i * 32 + }; + chunks[i].FillRandom(); + } + + // Warmup + Console.WriteLine("Warming up..."); + _ = PolarLoader.CreateWorld(chunks); + + // Benchmark + Stopwatch sw = Stopwatch.StartNew(); + byte[] polarData = PolarLoader.CreateWorld(chunks); + sw.Stop(); + + Console.WriteLine($"Time to create world with {chunkCount} chunks: {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Output size: {polarData.Length} bytes"); + Console.WriteLine($"Average time per chunk: {sw.ElapsedMilliseconds / (double)chunkCount:F2}ms"); + } + + [Test] + public void BenchmarkPolarWorldLoading() { + Console.WriteLine("=== Polar World Loading Benchmark ==="); + + // Create test data + const int chunkCount = 10; + ChunkData[] chunks = new ChunkData[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + chunks[i] = new ChunkData(384) { + ChunkX = i * 32, + ChunkZ = i * 32 + }; + chunks[i].FillRandom(); + } + + byte[] polarData = PolarLoader.CreateWorld(chunks); + + // Warmup + Console.WriteLine("Warming up..."); + _ = new PolarLoader(polarData, VanillaRegistry.Data); + + // Benchmark + Stopwatch sw = Stopwatch.StartNew(); + PolarLoader loader = new(polarData, VanillaRegistry.Data); + sw.Stop(); + + Console.WriteLine($"Time to load world with {chunkCount} chunks: {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Average time per chunk: {sw.ElapsedMilliseconds / (double)chunkCount:F2}ms"); + } + + [Test] + public void BenchmarkLargerWorld() { + Console.WriteLine("=== Large Polar World Benchmark ==="); + + const int chunkCount = 50; + ChunkData[] chunks = new ChunkData[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + chunks[i] = new ChunkData(384) { + ChunkX = i % 10, + ChunkZ = i / 10 + }; + chunks[i].FillRandom(); + } + + Console.WriteLine("Creating world..."); + Stopwatch sw = Stopwatch.StartNew(); + byte[] polarData = PolarLoader.CreateWorld(chunks); + sw.Stop(); + long creationTime = sw.ElapsedMilliseconds; + + Console.WriteLine("Loading world..."); + sw.Restart(); + PolarLoader loader = new(polarData, VanillaRegistry.Data); + sw.Stop(); + long loadTime = sw.ElapsedMilliseconds; + + Console.WriteLine($"\nResults for {chunkCount} chunks:"); + Console.WriteLine($" Creation: {creationTime}ms ({creationTime / (double)chunkCount:F2}ms per chunk)"); + Console.WriteLine($" Loading: {loadTime}ms ({loadTime / (double)chunkCount:F2}ms per chunk)"); + Console.WriteLine($" Size: {polarData.Length} bytes"); + } +} From f8c3a38cb84270c200c07ff233684159a6aaf230 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:46:55 +0000 Subject: [PATCH 3/8] Optimize Polar world loading with Dictionary-based palette lookups Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index 66da18aa..6cca68f5 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -242,6 +242,7 @@ private ChunkData ReadChunk(DataReader reader, short version, int dataVersion, i } private static void WriteSection(DataWriter writer, ChunkSection section, MinecraftRegistry registry) { + Dictionary blockPaletteMap = []; List blockPalette = []; ushort[] blockData = new ushort[ChunkSection.Size * ChunkSection.Size * ChunkSection.Size]; int cBlockDataIndex = 0; @@ -251,12 +252,9 @@ private static void WriteSection(DataWriter writer, ChunkSection section, Minecr IBlock block = section.LookupBlock(x, y, z, registry); string blockStr = GetStateStringFromBlock(block); - int paletteIndex; - if (blockPalette.Contains(blockStr)) { - paletteIndex = blockPalette.IndexOf(blockStr); - } - else { + if (!blockPaletteMap.TryGetValue(blockStr, out int paletteIndex)) { paletteIndex = blockPalette.Count; + blockPaletteMap[blockStr] = paletteIndex; blockPalette.Add(blockStr); } blockData[cBlockDataIndex++] = (ushort)paletteIndex; @@ -305,16 +303,19 @@ private ChunkSection ReadSection(DataReader reader, short version, int dataVersi string[] blockPalette = reader.ReadPrefixedArray(r => r.ReadString()); if (blockPalette.Length > 1) { + // Convert palette strings to state IDs once instead of repeatedly in the loop + uint[] stateIdPalette = new uint[blockPalette.Length]; + for (int i = 0; i < blockPalette.Length; i++) { + stateIdPalette[i] = GetBlockFromString(blockPalette[i]).StateId; + } + int bitsPerEntry = (int) Math.Ceiling(Math.Log(blockPalette.Length) / Math.Log(2)); ushort[] blockData = reader.ReadPrefixedPacketDataArray(bitsPerEntry); // palette indices for blocks for (int y = 0; y < ChunkSection.Size; y++) { for (int z = 0; z < ChunkSection.Size; z++) { for (int x = 0; x < ChunkSection.Size; x++) { int index = y * ChunkSection.Size * ChunkSection.Size + z * ChunkSection.Size + x; - string key = blockPalette[blockData[index]]; - - uint stateId = GetBlockFromString(key).StateId; - section.Blocks[x, y, z] = stateId; + section.Blocks[x, y, z] = stateIdPalette[blockData[index]]; } } } From f55278a44b0f8dc5e69eb1b6bbc073d726829237 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:49:48 +0000 Subject: [PATCH 4/8] Remove debug console output from PolarLoader Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index 6cca68f5..09094a67 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -58,7 +58,6 @@ public static byte[] CreateWorld(ChunkData[] chunks, MinecraftRegistry? registry data.WriteVarInt(chunks.Length); // Number of chunks foreach (ChunkData chunk in chunks) { - Console.WriteLine("Writing chunk: " + chunk.ChunkX + ", " + chunk.ChunkZ); WriteChunk(data, chunk, registry); } @@ -66,7 +65,6 @@ public static byte[] CreateWorld(ChunkData[] chunks, MinecraftRegistry? registry writer.WriteByte((sbyte)CompressionType.Zstd); // Compression type byte[] compressedData = CompressZstd(data.ToArray()); writer.WriteVarInt((int)data.Length); // Original length of the data before compression - Console.WriteLine("Compressed data length: " + data.Length); writer.Write(compressedData); // Compressed data return writer.ToArray(); } @@ -422,7 +420,6 @@ private enum LightContent { public void GetChunk(ref ChunkData chunk) { Chunks.TryGetValue(new Vec2(chunk.ChunkX, chunk.ChunkZ), out ChunkData? data); if (data == null) { - Console.WriteLine("Polar chunk not found: " + chunk.ChunkX + ", " + chunk.ChunkZ); return; } @@ -433,8 +430,6 @@ public void GetChunks(int start, int count, ChunkData[] chunks) { for (int i = start; i < start + count; i++) { if (Chunks.TryGetValue(new Vec2(chunks[i].ChunkX, chunks[i].ChunkZ), out ChunkData? data)) { chunks[i] = data; - } else { - Console.WriteLine("Polar chunk not found: " + chunks[i].ChunkX + ", " + chunks[i].ChunkZ); } } } From db5bc0fc38a90cde2bd159686104525e484c001e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:52:33 +0000 Subject: [PATCH 5/8] Address code review feedback: add capacity hint and prevent optimizer eliminations Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 2 +- Tests/PolarBenchmark.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index 09094a67..35276ea3 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -240,7 +240,7 @@ private ChunkData ReadChunk(DataReader reader, short version, int dataVersion, i } private static void WriteSection(DataWriter writer, ChunkSection section, MinecraftRegistry registry) { - Dictionary blockPaletteMap = []; + Dictionary blockPaletteMap = new(256); // Typical palette size estimate List blockPalette = []; ushort[] blockData = new ushort[ChunkSection.Size * ChunkSection.Size * ChunkSection.Size]; int cBlockDataIndex = 0; diff --git a/Tests/PolarBenchmark.cs b/Tests/PolarBenchmark.cs index d8846b85..f94d2e05 100644 --- a/Tests/PolarBenchmark.cs +++ b/Tests/PolarBenchmark.cs @@ -24,7 +24,7 @@ public void BenchmarkPolarWorldCreation() { // Warmup Console.WriteLine("Warming up..."); - _ = PolarLoader.CreateWorld(chunks); + byte[] warmupData = PolarLoader.CreateWorld(chunks); // Benchmark Stopwatch sw = Stopwatch.StartNew(); @@ -55,7 +55,7 @@ public void BenchmarkPolarWorldLoading() { // Warmup Console.WriteLine("Warming up..."); - _ = new PolarLoader(polarData, VanillaRegistry.Data); + PolarLoader warmupLoader = new(polarData, VanillaRegistry.Data); // Benchmark Stopwatch sw = Stopwatch.StartNew(); From bae11b179ea3a6e1dc087c32324219d22a3ab78b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:18:26 +0000 Subject: [PATCH 6/8] Further optimize Polar: direct block access, state ID caching, block string caching Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 41 ++++++++++++---- Tests/PolarDetailedProfiling.cs | 80 ++++++++++++++++++++++++++++++++ Tests/PolarProfilingBenchmark.cs | 69 +++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 Tests/PolarDetailedProfiling.cs create mode 100644 Tests/PolarProfilingBenchmark.cs diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index 35276ea3..961deeac 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -22,6 +22,7 @@ public class PolarLoader : ITerrainProvider { public Dictionary, ChunkData> Chunks = null!; private MinecraftRegistry _registry; + private Dictionary _blockStringCache = new(); // Cache parsed block strings public PolarLoader(string path, MinecraftRegistry registry) : this(File.ReadAllBytes(path), registry) { @@ -240,20 +241,26 @@ private ChunkData ReadChunk(DataReader reader, short version, int dataVersion, i } private static void WriteSection(DataWriter writer, ChunkSection section, MinecraftRegistry registry) { - Dictionary blockPaletteMap = new(256); // Typical palette size estimate + Dictionary stateIdToPaletteIndex = new(256); // Map state ID directly to palette index List blockPalette = []; ushort[] blockData = new ushort[ChunkSection.Size * ChunkSection.Size * ChunkSection.Size]; int cBlockDataIndex = 0; + + // Access blocks array directly - much faster than LookupBlock + uint[,,] blocks = section.Blocks; + for (int y = 0; y < ChunkSection.Size; y++) { for (int z = 0; z < ChunkSection.Size; z++) { for (int x = 0; x < ChunkSection.Size; x++) { - IBlock block = section.LookupBlock(x, y, z, registry); - string blockStr = GetStateStringFromBlock(block); + uint stateId = blocks[x, y, z]; - if (!blockPaletteMap.TryGetValue(blockStr, out int paletteIndex)) { + // Use state ID as key - only convert to string once per unique state + if (!stateIdToPaletteIndex.TryGetValue(stateId, out int paletteIndex)) { paletteIndex = blockPalette.Count; - blockPaletteMap[blockStr] = paletteIndex; - blockPalette.Add(blockStr); + stateIdToPaletteIndex[stateId] = paletteIndex; + // Only lookup block when we need to convert to string for the first time + IBlock block = registry.Blocks.GetByStateId(stateId); + blockPalette.Add(GetStateStringFromBlock(block)); } blockData[cBlockDataIndex++] = (ushort)paletteIndex; } @@ -309,11 +316,13 @@ private ChunkSection ReadSection(DataReader reader, short version, int dataVersi int bitsPerEntry = (int) Math.Ceiling(Math.Log(blockPalette.Length) / Math.Log(2)); ushort[] blockData = reader.ReadPrefixedPacketDataArray(bitsPerEntry); // palette indices for blocks + + // Optimized loop with direct indexing + int dataIndex = 0; for (int y = 0; y < ChunkSection.Size; y++) { for (int z = 0; z < ChunkSection.Size; z++) { for (int x = 0; x < ChunkSection.Size; x++) { - int index = y * ChunkSection.Size * ChunkSection.Size + z * ChunkSection.Size + x; - section.Blocks[x, y, z] = stateIdPalette[blockData[index]]; + section.Blocks[x, y, z] = stateIdPalette[blockData[dataIndex++]]; } } } @@ -347,6 +356,11 @@ private ChunkSection ReadSection(DataReader reader, short version, int dataVersi } private IBlock GetBlockFromString(string blockStr) { + // Cache to avoid re-parsing the same block strings + if (_blockStringCache.TryGetValue(blockStr, out IBlock? cachedBlock)) { + return cachedBlock; + } + // Example: "minecraft:stone[variant=granite]" string[] parts = blockStr.Split('[', 2); string blockName = parts[0]; @@ -359,11 +373,14 @@ private IBlock GetBlockFromString(string blockStr) { IBlock block = _registry.Blocks[blockName]; if (parts.Length <= 1) { + _blockStringCache[blockStr] = block; return block; } CompoundTag properties = PropertiesStringToNbt(parts[1].TrimEnd(']')); - return block.WithState(properties); + IBlock result = block.WithState(properties); + _blockStringCache[blockStr] = result; + return result; } private static CompoundTag PropertiesStringToNbt(string propsStr) { @@ -393,7 +410,11 @@ private static string GetStateStringFromBlock(IBlock block) { private static string GetPropsStringFromBlock(IBlock block) { CompoundTag properties = block.ToStateNbt(); - List props = []; + if (properties.Children.Length == 0) { + return string.Empty; + } + + List props = new(properties.Children.Length); foreach (INbtTag? tag in properties.Children) { if (tag is StringTag stringTag) { props.Add($"{stringTag.Name}={stringTag.Value}"); diff --git a/Tests/PolarDetailedProfiling.cs b/Tests/PolarDetailedProfiling.cs new file mode 100644 index 00000000..87bedb16 --- /dev/null +++ b/Tests/PolarDetailedProfiling.cs @@ -0,0 +1,80 @@ +using System.Diagnostics; +using System.Reflection; +using Minecraft.Data.Generated; +using Minecraft.Schemas.Chunks; +using PolarWorlds; + +namespace Tests; + +public class PolarDetailedProfiling { + + [Test] + public void ProfileSectionWriting() { + Console.WriteLine("=== Profiling Section Writing ==="); + + ChunkData chunk = new ChunkData(384) { + ChunkX = 0, + ChunkZ = 0 + }; + chunk.FillRandom(); + + // Get the WriteSection method via reflection + var writerType = typeof(PolarLoader); + var dataWriterType = Assembly.Load("Minecraft").GetType("Minecraft.DataWriter"); + var chunkSectionType = Assembly.Load("Minecraft").GetType("Minecraft.Schemas.Chunks.ChunkSection"); + + // Warm up + for (int i = 0; i < 3; i++) { + _ = PolarLoader.CreateWorld([chunk]); + } + + // Measure just the section writing part (all sections) + Stopwatch sw = Stopwatch.StartNew(); + int sectionCount = 0; + foreach (var section in chunk.Sections) { + sectionCount++; + } + sw.Stop(); + + Console.WriteLine($"Chunk has {sectionCount} sections"); + Console.WriteLine($"Time to iterate sections: {sw.ElapsedMilliseconds}ms"); + + // Now measure full creation + sw.Restart(); + byte[] result = PolarLoader.CreateWorld([chunk]); + sw.Stop(); + + Console.WriteLine($"Full creation time (1 chunk): {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Output size: {result.Length / 1024.0:F2} KB"); + } + + [Test] + public void ProfileCompressionOnly() { + Console.WriteLine("=== Profiling Compression ==="); + + // Create some data to compress + byte[] testData = new byte[10 * 1024 * 1024]; // 10MB of random data + Random rand = new Random(42); + rand.NextBytes(testData); + + // Test Zstd compression + Stopwatch sw = Stopwatch.StartNew(); + var compressor = new ZstdSharp.Compressor(); + byte[] compressed = compressor.Wrap(testData).ToArray(); + sw.Stop(); + + Console.WriteLine($"Compressed {testData.Length / 1024.0 / 1024.0:F2} MB in {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Compression ratio: {testData.Length / (double)compressed.Length:F2}x"); + Console.WriteLine($"Throughput: {testData.Length / 1024.0 / 1024.0 / (sw.ElapsedMilliseconds / 1000.0):F2} MB/s"); + + // Test decompression + sw.Restart(); + var decompressor = new ZstdSharp.Decompressor(); + byte[] decompressed = new byte[testData.Length]; + decompressor.Unwrap(compressed, decompressed); + sw.Stop(); + + Console.WriteLine($"Decompressed in {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Throughput: {testData.Length / 1024.0 / 1024.0 / (sw.ElapsedMilliseconds / 1000.0):F2} MB/s"); + } +} diff --git a/Tests/PolarProfilingBenchmark.cs b/Tests/PolarProfilingBenchmark.cs new file mode 100644 index 00000000..97fd8e04 --- /dev/null +++ b/Tests/PolarProfilingBenchmark.cs @@ -0,0 +1,69 @@ +using System.Diagnostics; +using Minecraft.Data.Generated; +using Minecraft.Schemas.Chunks; +using PolarWorlds; + +namespace Tests; + +public class PolarProfilingBenchmark { + + [Test] + public void ProfileWorldCreationSteps() { + Console.WriteLine("=== Profiling Polar World Creation ==="); + + const int chunkCount = 10; + ChunkData[] chunks = new ChunkData[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + chunks[i] = new ChunkData(384) { + ChunkX = i * 32, + ChunkZ = i * 32 + }; + chunks[i].FillRandom(); + } + + // Warmup + _ = PolarLoader.CreateWorld(chunks); + + // Now measure with GC tracking + long memBefore = GC.GetTotalMemory(true); + Stopwatch sw = Stopwatch.StartNew(); + byte[] polarData = PolarLoader.CreateWorld(chunks); + sw.Stop(); + long memAfter = GC.GetTotalMemory(false); + + Console.WriteLine($"Total time: {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Memory allocated: {(memAfter - memBefore) / 1024.0 / 1024.0:F2} MB"); + Console.WriteLine($"Output size: {polarData.Length / 1024.0 / 1024.0:F2} MB"); + Console.WriteLine($"Compression ratio: {(memAfter - memBefore) / (double)polarData.Length:F2}x"); + } + + [Test] + public void ProfileWorldLoadingSteps() { + Console.WriteLine("=== Profiling Polar World Loading ==="); + + const int chunkCount = 10; + ChunkData[] chunks = new ChunkData[chunkCount]; + for (int i = 0; i < chunkCount; i++) { + chunks[i] = new ChunkData(384) { + ChunkX = i * 32, + ChunkZ = i * 32 + }; + chunks[i].FillRandom(); + } + + byte[] polarData = PolarLoader.CreateWorld(chunks); + + // Warmup + _ = new PolarLoader(polarData, VanillaRegistry.Data); + + long memBefore = GC.GetTotalMemory(true); + Stopwatch sw = Stopwatch.StartNew(); + PolarLoader loader = new(polarData, VanillaRegistry.Data); + sw.Stop(); + long memAfter = GC.GetTotalMemory(false); + + Console.WriteLine($"Total time: {sw.ElapsedMilliseconds}ms"); + Console.WriteLine($"Memory allocated: {(memAfter - memBefore) / 1024.0 / 1024.0:F2} MB"); + Console.WriteLine($"Chunks loaded: {loader.Chunks.Count}"); + } +} From 98b783e8d4510b89f88cd443b32591b191ff0102 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:21:18 +0000 Subject: [PATCH 7/8] Use faster Zstd compression level (level 1) for better performance Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index 961deeac..c2614d90 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -126,7 +126,9 @@ private static void DecompressZstd(DataReader reader, int length) { } private static byte[] CompressZstd(ReadOnlySpan data) { - using Compressor compressor = new(); + // Use level 1 for faster compression (default is 3) + // This trades compression ratio for speed + using Compressor compressor = new(1); return compressor.Wrap(data).ToArray(); } From 671f0e128551ee575c7d4a5ed6e713baf2718d24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 10:24:05 +0000 Subject: [PATCH 8/8] Address code review feedback: extract magic number to named constant Co-authored-by: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> --- PolarWorlds/PolarLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PolarWorlds/PolarLoader.cs b/PolarWorlds/PolarLoader.cs index c2614d90..fdf6c852 100644 --- a/PolarWorlds/PolarLoader.cs +++ b/PolarWorlds/PolarLoader.cs @@ -19,6 +19,7 @@ public class PolarLoader : ITerrainProvider { private const int MaxHeightmaps = 32; private const int BlockPaletteSize = 4096; private const int DataVersion = 4325; + private const int InitialPaletteCapacity = 256; // Typical palette size estimate public Dictionary, ChunkData> Chunks = null!; private MinecraftRegistry _registry; @@ -243,7 +244,7 @@ private ChunkData ReadChunk(DataReader reader, short version, int dataVersion, i } private static void WriteSection(DataWriter writer, ChunkSection section, MinecraftRegistry registry) { - Dictionary stateIdToPaletteIndex = new(256); // Map state ID directly to palette index + Dictionary stateIdToPaletteIndex = new(InitialPaletteCapacity); // Map state ID directly to palette index List blockPalette = []; ushort[] blockData = new ushort[ChunkSection.Size * ChunkSection.Size * ChunkSection.Size]; int cBlockDataIndex = 0;