From 9af6c643e3f146cacf3992ca8e7a543030f52c5d Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 21 Mar 2026 00:54:27 +0800 Subject: [PATCH] fix: handle 1.21.5 chunk data format changes In 1.21.5, two wire format changes in level chunk packets: 1. Heightmaps changed from NBT CompoundTag to map encoding 2. PalettedContainer data arrays no longer have VarInt length prefix (uses writeFixedSizeLongArray instead of writeLongArray) Both changes affect ChunkData (level_chunk_with_light) packet parsing. Without this fix, MCC crashes with "Queue empty" when processing chunks. Made-with: Cursor --- .../Protocol/Handlers/Protocol18.cs | 17 +++++++++++- .../Protocol/Handlers/Protocol18Terrain.cs | 26 ++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index e9019316..743fd3e9 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -1511,7 +1511,22 @@ namespace MinecraftClient.Protocol.Handlers dataTypes.ReadNextULongArray( packetData); // Bit Mask Length and Primary Bit Mask - dataTypes.ReadNextNbt(packetData); // Heightmaps + if (protocolVersion >= MC_1_21_5_Version) + { + // 1.21.5: Heightmaps encoded as map instead of NBT + var hmCount = dataTypes.ReadNextVarInt(packetData); + for (var hm = 0; hm < hmCount; hm++) + { + dataTypes.ReadNextVarInt(packetData); // Heightmap type id + var longCount = dataTypes.ReadNextVarInt(packetData); + for (var l = 0; l < longCount; l++) + dataTypes.ReadNextLong(packetData); + } + } + else + { + dataTypes.ReadNextNbt(packetData); // Heightmaps (NBT format) + } if (protocolVersion is MC_1_17_Version or MC_1_17_1_Version) { diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs index fc66edd0..75bf7b34 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; @@ -47,7 +47,8 @@ namespace MinecraftClient.Protocol.Handlers ushort blockId = (ushort)dataTypes.ReadNextVarInt(cache); Block block = new(blockId); - dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Data Array Length will be zero (removed in 1.21.5) // Empty chunks will not be stored if (block.Type == Material.Air) @@ -80,7 +81,8 @@ namespace MinecraftClient.Protocol.Handlers palette[i] = (uint)dataTypes.ReadNextVarInt(cache); //// Block IDs are packed in the array of 64-bits integers - dataTypes.SkipNextVarInt(cache); // Entry length + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Entry length (removed in 1.21.5) Span entryDataByte = stackalloc byte[8]; Span entryDataLong = MemoryMarshal.Cast(entryDataByte); // Faster than MemoryMarshal.Read @@ -196,8 +198,8 @@ namespace MinecraftClient.Protocol.Handlers if (bitsPerEntryBiome == 0) { dataTypes.SkipNextVarInt(cache); // Value - dataTypes.SkipNextVarInt(cache); // Data Array Length - // Data Array must be empty + if (protocolversion < Protocol18Handler.MC_1_21_5_Version) + dataTypes.SkipNextVarInt(cache); // Data Array Length (removed in 1.21.5) } else { @@ -207,8 +209,18 @@ namespace MinecraftClient.Protocol.Handlers for (int i = 0; i < paletteLength; i++) dataTypes.SkipNextVarInt(cache); // Palette } - int dataArrayLength = dataTypes.ReadNextVarInt(cache); // Data Array Length - dataTypes.DropData(dataArrayLength * 8, cache); // Data Array + if (protocolversion >= Protocol18Handler.MC_1_21_5_Version) + { + // 1.21.5: No VarInt length prefix; calculate from bits per entry + // Biome container has 64 entries (4x4x4) + int dataArrayLength = (64 * bitsPerEntryBiome + 63) / 64; + dataTypes.DropData(dataArrayLength * 8, cache); + } + else + { + int dataArrayLength = dataTypes.ReadNextVarInt(cache); // Data Array Length + dataTypes.DropData(dataArrayLength * 8, cache); // Data Array + } } } }