diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index 8add5c23..be666f4f 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -28,6 +28,11 @@ namespace MinecraftClient.Mapping /// private static Dictionary dimensionIdMap = new(); + /// + /// VarInt ID → attribute name mapping, populated from RegistryData (minecraft:attribute) in 1.20.6+ + /// + private static Dictionary attributeIdMap = new(); + /// /// Chunk data parsing progress /// @@ -232,6 +237,19 @@ namespace MinecraftClient.Mapping return dimensionList.Count > 0; } + public static void SetAttributeIdMap(Dictionary idMap) + { + attributeIdMap = idMap; + } + + /// + /// Get attribute name by its registry VarInt ID. Returns null if the ID is unknown. + /// + public static string? GetAttributeNameById(int id) + { + return attributeIdMap.TryGetValue(id, out var name) ? name : null; + } + /// /// 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 dab9f2e0..7806af21 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -463,9 +463,11 @@ namespace MinecraftClient.Protocol.Handlers var isChat = registryId == "minecraft:chat_type"; var isDimension = registryId == "minecraft:dimension_type"; + var isAttribute = registryId == "minecraft:attribute"; var availableChats = isChat ? new Dictionary() : null; var dimensionIdMap = isDimension ? new Dictionary() : null; + var attributeIdMap = isAttribute ? new Dictionary() : null; for (var i = 0; i < entryCount; i++) { @@ -484,6 +486,14 @@ namespace MinecraftClient.Protocol.Handlers if (nbtData != null && handler.GetTerrainEnabled()) World.StoreOneDimension(entryId, nbtData); } + else if (isAttribute) + { + // Strip "minecraft:" prefix to match the format used in EntityProperties packets + var attrName = entryId.StartsWith("minecraft:") + ? entryId.Substring("minecraft:".Length) + : entryId; + attributeIdMap!.Add(i, attrName); + } } if (isChat) @@ -494,6 +504,8 @@ namespace MinecraftClient.Protocol.Handlers if (!handler.GetTerrainEnabled() || !World.HasAnyDimension()) World.LoadDefaultDimensions1206Plus(); } + else if (isAttribute) + World.SetAttributeIdMap(attributeIdMap!); } break; @@ -2502,39 +2514,19 @@ namespace MinecraftClient.Protocol.Handlers ? dataTypes.ReadNextVarInt(packetData) : dataTypes.ReadNextInt(packetData); - var attributeDictionary = new Dictionary - { - { 0, "generic.armor" }, - { 1, "generic.armor_toughness" }, - { 2, "generic.attack_damage" }, - { 3, "generic.attack_knockback" }, - { 4, "generic.attack_speed" }, - { 5, "player.block_break_speed" }, - { 6, "player.block_interaction_range" }, - { 7, "player.entity_interaction_range" }, - { 8, "generic.fall_damage_multiplier" }, - { 9, "generic.flying_speed" }, - { 10, "generic.follow_range" }, - { 11, "generic.gravity" }, - { 12, "generic.jump_strength" }, - { 13, "generic.knockback_resistance" }, - { 14, "generic.luck" }, - { 15, "generic.max_absorption" }, - { 16, "generic.max_health" }, - { 17, "generic.movement_speed" }, - { 18, "generic.safe_fall_distance" }, - { 19, "generic.scale" }, - { 20, "zombie.spawn_reinforcements" }, - { 21, "generic.step_height" } - }; - Dictionary keys = new(); for (var i = 0; i < numberOfProperties; i++) { - var propertyKey = protocolVersion < MC_1_20_6_Version - ? dataTypes.ReadNextString(packetData) - : (attributeDictionary.TryGetValue(dataTypes.ReadNextVarInt(packetData), out var attrName) - ? attrName : "unknown"); + string propertyKey; + if (protocolVersion < MC_1_20_6_Version) + { + propertyKey = dataTypes.ReadNextString(packetData); + } + else + { + var attrId = dataTypes.ReadNextVarInt(packetData); + propertyKey = World.GetAttributeNameById(attrId) ?? "unknown"; + } var propertyValue2 = dataTypes.ReadNextDouble(packetData); List op0 = new();