diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 4b4f0488..bd2c84e4 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -1679,6 +1679,33 @@ namespace MinecraftClient.Protocol.Handlers return locationBytes; } + /// + /// Get a byte array representing the given item as a HashedStack (1.21.5+). + /// Used for serverbound container_click where the server expects HashedStack instead of full ItemStack. + /// Wire format: Optional<ActualItem> where ActualItem = holderRegistry(item_id) + VarInt(count) + HashedPatchMap. + /// Since MCC doesn't track component hashes, we send an empty HashedPatchMap (0 added, 0 removed). + /// The server will detect the stateId mismatch and resync. + /// + public byte[] GetHashedItemSlot(Item? item, ItemPalette itemPalette) + { + List slotData = new(); + + if (item == null || item.IsEmpty) + { + slotData.AddRange(GetBool(false)); + } + else + { + slotData.AddRange(GetBool(true)); + slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + slotData.AddRange(GetVarInt(item.Count)); + slotData.AddRange(GetVarInt(0)); // HashedPatchMap: 0 added components + slotData.AddRange(GetVarInt(0)); // HashedPatchMap: 0 removed components + } + + return slotData.ToArray(); + } + /// /// Get a byte array representing the given item as an item slot /// diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 4fe6b857..ab00dfc6 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -4567,11 +4567,19 @@ namespace MinecraftClient.Protocol.Handlers foreach (var slot in changedSlots) { packet.AddRange(dataTypes.GetShort(slot.Item1)); // slot ID - packet.AddRange(dataTypes.GetItemSlot(slot.Item2, itemPalette)); // slot Data + // 1.21.5+ uses HashedStack instead of ItemStack for container_click + if (protocolVersion >= MC_1_21_5_Version) + packet.AddRange(dataTypes.GetHashedItemSlot(slot.Item2, itemPalette)); + else + packet.AddRange(dataTypes.GetItemSlot(slot.Item2, itemPalette)); } } - packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); // Carried item (Clicked item) + // 1.21.5+ uses HashedStack instead of ItemStack for carried item + if (protocolVersion >= MC_1_21_5_Version) + packet.AddRange(dataTypes.GetHashedItemSlot(item, itemPalette)); + else + packet.AddRange(dataTypes.GetItemSlot(item, itemPalette)); SendPacket(PacketTypesOut.ClickWindow, packet); return true; diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs new file mode 100644 index 00000000..4ade9885 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/EnchantmentsComponent1215.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ enchantments: showInTooltip removed from wire format (moved to tooltip_display component). +/// Wire: VarInt count, then (VarInt holder_id + VarInt level) per entry. No trailing boolean. +/// +public class EnchantmentsComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EnchantmentsComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public override void Parse(Queue data) + { + NumberOfEnchantments = dataTypes.ReadNextVarInt(data); + + for (var i = 0; i < NumberOfEnchantments; i++) + { + var registryId = dataTypes.ReadNextVarInt(data); + var level = dataTypes.ReadNextVarInt(data); + Enchantments.Add(new Enchantment(EnchantmentMapping.GetEnchantmentByRegistryId1206(registryId), level)); + } + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Enchantments.Count)); + foreach (var enchantment in Enchantments) + { + data.AddRange(DataTypes.GetVarInt(EnchantmentMapping.GetRegistryId1206ByEnchantment(enchantment.Type))); + data.AddRange(DataTypes.GetVarInt(enchantment.Level)); + } + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs new file mode 100644 index 00000000..8daf09ea --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/StoredEnchantmentsComponent1215.cs @@ -0,0 +1,7 @@ +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +public class StoredEnchantmentsComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : EnchantmentsComponent1215(dataTypes, itemPalette, subComponentRegistry); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs index ec848fb2..c7faf6a4 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs @@ -27,7 +27,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry RegisterComponent(10, "minecraft:item_model"); RegisterComponent(11, "minecraft:lore"); RegisterComponent(12, "minecraft:rarity"); - RegisterComponent(13, "minecraft:enchantments"); + RegisterComponent(13, "minecraft:enchantments"); RegisterComponent(14, "minecraft:can_place_on"); RegisterComponent(15, "minecraft:can_break"); RegisterComponent(16, "minecraft:attribute_modifiers"); @@ -55,7 +55,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry RegisterComponent(38, "minecraft:piercing_weapon"); RegisterComponent(39, "minecraft:kinetic_weapon"); RegisterComponent(40, "minecraft:swing_animation"); - RegisterComponent(41, "minecraft:stored_enchantments"); + RegisterComponent(41, "minecraft:stored_enchantments"); RegisterComponent(42, "minecraft:dyed_color"); RegisterComponent(43, "minecraft:map_color"); RegisterComponent(44, "minecraft:map_id"); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs index 26b3b27e..a0b8a4c7 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs @@ -23,7 +23,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry RegisterComponent(7, "minecraft:item_model"); RegisterComponent(8, "minecraft:lore"); RegisterComponent(9, "minecraft:rarity"); - RegisterComponent(10, "minecraft:enchantments"); + RegisterComponent(10, "minecraft:enchantments"); RegisterComponent(11, "minecraft:can_place_on"); RegisterComponent(12, "minecraft:can_break"); RegisterComponent(13, "minecraft:attribute_modifiers"); @@ -48,7 +48,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry RegisterComponent(31, "minecraft:tooltip_style"); RegisterComponent(32, "minecraft:death_protection"); RegisterComponent(33, "minecraft:blocks_attacks"); // NEW - RegisterComponent(34, "minecraft:stored_enchantments"); + RegisterComponent(34, "minecraft:stored_enchantments"); RegisterComponent(35, "minecraft:dyed_color"); RegisterComponent(36, "minecraft:map_color"); RegisterComponent(37, "minecraft:map_id");