From 41a701b6b2d651d81cca3e477586a1c226dfea28 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Thu, 19 Mar 2026 00:13:22 +0800 Subject: [PATCH] Fix RegistryData parsing and KnownDataPacks negotiation for 1.20.6 Two critical issues in the 1.20.6 configuration phase that could cause connection instability and packet desync: 1. RegistryData: The handler used an early `break` when it encountered a registryId other than "minecraft:dimension_type" or "minecraft:chat_type". This skipped reading the remaining entries for that registry, leaving unconsumed data in the packet buffer. Subsequent packet reads would start at the wrong offset, causing cascading parse failures and eventual disconnection. Fix: Always read all entries (entryId + hasData + optional NBT) for every registry, regardless of whether we process it. For dimension_type entries, if the server sends inline NBT data (i.e. non-vanilla dimensions from mods/datapacks), parse and store the dimension directly via World.StoreOneDimension(). Only fall back to hardcoded defaults when no dimension data was received. 2. KnownDataPacks: The client echoed back ALL packs the server listed, including non-vanilla ones. This told the server "I have these packs cached" when the client actually did not, so the server would skip sending full registry data for those packs. The result: incomplete registries for modded/datapack content. Fix: Filter the response to only include packs with the "minecraft" namespace. Non-vanilla packs are omitted, forcing the server to send their full registry data inline. Also adds supporting methods to World.cs: - SetDimensionIdMap(): Store VarInt ID -> dimension name mapping from RegistryData entries (needed by JoinGame/Respawn) - GetDimensionNameById(): Look up dimension name by numeric ID - HasAnyDimension(): Check if any dimensions were loaded from server-provided data Made-with: Cursor --- MinecraftClient/Mapping/World.cs | 22 ++++++++- .../Protocol/Handlers/Protocol18.cs | 48 ++++++++++--------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index b6240ffc..8add5c23 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -23,6 +23,11 @@ namespace MinecraftClient.Mapping private static readonly Dictionary dimensionList = new(); + /// + /// VarInt ID → dimension name mapping, populated from RegistryData in 1.20.6+ + /// + private static Dictionary dimensionIdMap = new(); + /// /// Chunk data parsing progress /// @@ -212,6 +217,21 @@ namespace MinecraftClient.Mapping StoreDimensionList(defaultRegistryCodec); } + public static void SetDimensionIdMap(Dictionary idMap) + { + dimensionIdMap = idMap; + } + + public static string GetDimensionNameById(int id) + { + return dimensionIdMap.TryGetValue(id, out var name) ? name : "minecraft:overworld"; + } + + public static bool HasAnyDimension() + { + return dimensionList.Count > 0; + } + /// /// Store one dimension - Directly used in 1.16.2 to 1.18.2 /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index a274b6e4..3a0abe27 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -458,40 +458,41 @@ namespace MinecraftClient.Protocol.Handlers } else { - // TODO: Implement proper parsing for 1.20.6 / 1.21 when there is a custom data pack on the server - // THis is a temporary workaround to get the client to be useable asap - var registryId = dataTypes.ReadNextString(packetData); var entryCount = dataTypes.ReadNextVarInt(packetData); - // Ignore other registries to save on time, we need only these 2 - if(registryId is not ("minecraft:dimension_type" or "minecraft:chat_type")) - break; + var isChat = registryId == "minecraft:chat_type"; + var isDimension = registryId == "minecraft:dimension_type"; - var avaliableChats = new Dictionary(); - var dimensionType = new Dictionary(); + var availableChats = isChat ? new Dictionary() : null; + var dimensionIdMap = isDimension ? new Dictionary() : null; for (var i = 0; i < entryCount; i++) { var entryId = dataTypes.ReadNextString(packetData); var hasData = dataTypes.ReadNextBool(packetData); - - if (hasData) - { - // TODO: Parse in case when the server data packs differ from the client - dataTypes.ReadNextNbt(packetData); - } - if (registryId == "minecraft:chat_type") - avaliableChats.Add(i, entryId); - else dimensionType.Add(i, entryId); + Dictionary? nbtData = null; + if (hasData) + nbtData = dataTypes.ReadNextNbt(packetData); + + if (isChat) + availableChats!.Add(i, entryId); + else if (isDimension) + { + dimensionIdMap!.Add(i, entryId); + if (nbtData != null && handler.GetTerrainEnabled()) + World.StoreOneDimension(entryId, nbtData); + } } - if (registryId == "minecraft:chat_type") - ChatParser.ReadChatType(avaliableChats); - else + if (isChat) + ChatParser.ReadChatType(availableChats!); + else if (isDimension) { - World.LoadDefaultDimensions1206Plus(); + World.SetDimensionIdMap(dimensionIdMap!); + if (!handler.GetTerrainEnabled() || !World.HasAnyDimension()) + World.LoadDefaultDimensions1206Plus(); } } @@ -531,7 +532,10 @@ namespace MinecraftClient.Protocol.Handlers knownDataPacks.Add((nameSpace, id, version)); } - SendKnownDataPacks(knownDataPacks); + var vanillaPacks = knownDataPacks + .Where(p => p.Item1 == "minecraft") + .ToList(); + SendKnownDataPacks(vanillaPacks); break; // Ignore other packets at this stage