diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 29fbc601..d203e90f 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -2454,7 +2454,7 @@ namespace MinecraftClient.Protocol.Handlers var bitsData = dataTypes.ReadNextByte(packetData); // Top bit set if another entry follows, and otherwise unset if this is the last item in the array hasNext = bitsData >> 7 == 1; - var slot2 = bitsData >> 1; + var slot2 = bitsData & 0x7F; var item = dataTypes.ReadNextItemSlot(packetData, itemPalette); handler.OnEntityEquipment(entityId, slot2, item); } while (hasNext); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs new file mode 100644 index 00000000..28652f86 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/AdventureModePredicateComponents1215.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ uses AdventureModePredicate.STREAM_CODEC: +/// list, where each BlockPredicate ends with DataComponentMatchers. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public sealed class CanBreakComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : AdventureModePredicateComponent1215Base(dataTypes, itemPalette, subComponentRegistry); + +/// +/// 1.21.5+ uses AdventureModePredicate.STREAM_CODEC: +/// list, where each BlockPredicate ends with DataComponentMatchers. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public sealed class CanPlaceOnComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : AdventureModePredicateComponent1215Base(dataTypes, itemPalette, subComponentRegistry); + +public abstract class AdventureModePredicateComponent1215Base(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List BlockPredicates { get; set; } = []; + + public override void Parse(Queue data) + { + var predicateCount = DataTypes.ReadNextVarInt(data); + BlockPredicates = new List(predicateCount); + var componentHandler = new StructuredComponentsHandler(DataTypes.ProtocolVersion, DataTypes, ItemPalette); + + for (var i = 0; i < predicateCount; i++) + BlockPredicates.Add(ParseBlockPredicate(data, componentHandler)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(BlockPredicates.Count)); + + foreach (var predicate in BlockPredicates) + SerializeBlockPredicate(data, predicate); + + return new Queue(data); + } + + private AdventureModeBlockPredicate1215 ParseBlockPredicate(Queue data, StructuredComponentsHandler componentHandler) + { + BlockSetSubcomponent? blockSet = null; + if (DataTypes.ReadNextBool(data)) + blockSet = (BlockSetSubcomponent)SubComponentRegistry.ParseSubComponent(SubComponents.BlockSet, data); + + List properties = []; + if (DataTypes.ReadNextBool(data)) + { + var propertyCount = DataTypes.ReadNextVarInt(data); + properties = new List(propertyCount); + for (var i = 0; i < propertyCount; i++) + properties.Add((PropertySubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Property, data)); + } + + Dictionary? nbt = null; + if (DataTypes.ReadNextBool(data)) + nbt = DataTypes.ReadNextNbt(data); + + var exactComponentCount = DataTypes.ReadNextVarInt(data); + var exactComponents = new List(exactComponentCount); + for (var i = 0; i < exactComponentCount; i++) + { + var componentTypeId = DataTypes.ReadNextVarInt(data); + exactComponents.Add(componentHandler.Parse(componentTypeId, data)); + } + + var partialPredicateCount = DataTypes.ReadNextVarInt(data); + var partialPredicates = new List(partialPredicateCount); + for (var i = 0; i < partialPredicateCount; i++) + { + partialPredicates.Add(new DataComponentPredicatePayload1215( + DataTypes.ReadNextVarInt(data), + DataTypes.ReadNextNbt(data))); + } + + return new AdventureModeBlockPredicate1215(blockSet, properties, nbt, exactComponents, partialPredicates); + } + + private void SerializeBlockPredicate(List data, AdventureModeBlockPredicate1215 predicate) + { + data.AddRange(DataTypes.GetBool(predicate.BlockSet is not null)); + if (predicate.BlockSet is not null) + data.AddRange(predicate.BlockSet.Serialize()); + + data.AddRange(DataTypes.GetBool(predicate.Properties.Count > 0)); + if (predicate.Properties.Count > 0) + { + data.AddRange(DataTypes.GetVarInt(predicate.Properties.Count)); + foreach (var property in predicate.Properties) + data.AddRange(property.Serialize()); + } + + data.AddRange(DataTypes.GetBool(predicate.Nbt is not null)); + if (predicate.Nbt is not null) + data.AddRange(DataTypes.GetNbt(predicate.Nbt)); + + data.AddRange(DataTypes.GetVarInt(predicate.ExactComponents.Count)); + foreach (var component in predicate.ExactComponents) + { + if (component.TypeId < 0) + throw new ArgumentException("Exact predicate component is missing its data component type id.", nameof(component)); + + data.AddRange(DataTypes.GetVarInt(component.TypeId)); + data.AddRange(component.Serialize()); + } + + data.AddRange(DataTypes.GetVarInt(predicate.PartialPredicates.Count)); + foreach (var predicatePayload in predicate.PartialPredicates) + { + data.AddRange(DataTypes.GetVarInt(predicatePayload.TypeId)); + data.AddRange(DataTypes.GetNbt(predicatePayload.Payload)); + } + } +} + +public sealed record AdventureModeBlockPredicate1215( + BlockSetSubcomponent? BlockSet, + List Properties, + Dictionary? Nbt, + List ExactComponents, + List PartialPredicates); + +public sealed record DataComponentPredicatePayload1215(int TypeId, Dictionary Payload); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs new file mode 100644 index 00000000..7fa2453c --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/DyeColorComponent1215.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ dyed_color only carries the RGB integer. +/// Tooltip visibility moved to minecraft:tooltip_display. +/// +public class DyeColorComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int Color { get; set; } + + public override void Parse(Queue data) + { + Color = DataTypes.ReadNextInt(data); + } + + public override Queue Serialize() + { + return new Queue(DataTypes.GetInt(Color)); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs new file mode 100644 index 00000000..e1b1ebc9 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_21_5/TrimComponent1215.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; +using MinecraftClient.Protocol.Message; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; + +/// +/// 1.21.5+ trim uses ArmorTrim.STREAM_CODEC: +/// Holder<TrimMaterial> + Holder<TrimPattern>. +/// The holder codec is ByteBufCodecs.holder(), so 0 means direct inline data and non-zero means registry reference. +/// +public class TrimComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int MaterialHolderValue { get; set; } + public DirectTrimMaterial1215? DirectMaterial { get; set; } + public int PatternHolderValue { get; set; } + public DirectTrimPattern1215? DirectPattern { get; set; } + + public override void Parse(Queue data) + { + MaterialHolderValue = DataTypes.ReadNextVarInt(data); + if (MaterialHolderValue == 0) + DirectMaterial = ParseDirectMaterial(data); + + PatternHolderValue = DataTypes.ReadNextVarInt(data); + if (PatternHolderValue == 0) + DirectPattern = ParseDirectPattern(data); + } + + public override Queue Serialize() + { + var data = new List(); + + data.AddRange(DataTypes.GetVarInt(MaterialHolderValue)); + if (MaterialHolderValue == 0) + { + if (DirectMaterial is null) + throw new ArgumentNullException(nameof(DirectMaterial), "Direct trim material payload is required when holder value is 0."); + + data.AddRange(DataTypes.GetString(DirectMaterial.BaseSuffix)); + data.AddRange(DataTypes.GetVarInt(DirectMaterial.OverrideSuffixes.Count)); + foreach (var (assetKey, suffix) in DirectMaterial.OverrideSuffixes) + { + data.AddRange(DataTypes.GetString(assetKey)); + data.AddRange(DataTypes.GetString(suffix)); + } + + data.AddRange(DataTypes.GetNbt(DirectMaterial.DescriptionNbt)); + } + + data.AddRange(DataTypes.GetVarInt(PatternHolderValue)); + if (PatternHolderValue == 0) + { + if (DirectPattern is null) + throw new ArgumentNullException(nameof(DirectPattern), "Direct trim pattern payload is required when holder value is 0."); + + data.AddRange(DataTypes.GetString(DirectPattern.AssetId)); + data.AddRange(DataTypes.GetNbt(DirectPattern.DescriptionNbt)); + data.AddRange(DataTypes.GetBool(DirectPattern.Decal)); + } + + return new Queue(data); + } + + private DirectTrimMaterial1215 ParseDirectMaterial(Queue data) + { + var baseSuffix = DataTypes.ReadNextString(data); + var overrideCount = DataTypes.ReadNextVarInt(data); + var overrideSuffixes = new Dictionary(overrideCount, StringComparer.Ordinal); + + for (var i = 0; i < overrideCount; i++) + { + var assetKey = DataTypes.ReadNextString(data); + var suffix = DataTypes.ReadNextString(data); + overrideSuffixes[assetKey] = suffix; + } + + var descriptionNbt = DataTypes.ReadNextNbt(data); + var description = ChatParser.ParseText(descriptionNbt); + return new DirectTrimMaterial1215(baseSuffix, overrideSuffixes, descriptionNbt, description); + } + + private DirectTrimPattern1215 ParseDirectPattern(Queue data) + { + var assetId = DataTypes.ReadNextString(data); + var descriptionNbt = DataTypes.ReadNextNbt(data); + var description = ChatParser.ParseText(descriptionNbt); + var decal = DataTypes.ReadNextBool(data); + return new DirectTrimPattern1215(assetId, descriptionNbt, description, decal); + } +} + +public sealed record DirectTrimMaterial1215( + string BaseSuffix, + Dictionary OverrideSuffixes, + Dictionary DescriptionNbt, + string Description); + +public sealed record DirectTrimPattern1215( + string AssetId, + Dictionary DescriptionNbt, + string Description, + bool Decal); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs index 7e9ec9c7..7788f0c2 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry12111.cs @@ -30,8 +30,8 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry RegisterComponent(11, "minecraft:lore"); RegisterComponent(12, "minecraft:rarity"); RegisterComponent(13, "minecraft:enchantments"); - RegisterComponent(14, "minecraft:can_place_on"); - RegisterComponent(15, "minecraft:can_break"); + RegisterComponent(14, "minecraft:can_place_on"); + RegisterComponent(15, "minecraft:can_break"); RegisterComponent(16, "minecraft:attribute_modifiers"); RegisterComponent(17, "minecraft:custom_model_data"); RegisterComponent(18, "minecraft:tooltip_display"); @@ -58,7 +58,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry RegisterComponent(39, "minecraft:kinetic_weapon"); RegisterComponent(40, "minecraft:swing_animation"); RegisterComponent(41, "minecraft:stored_enchantments"); - RegisterComponent(42, "minecraft:dyed_color"); + RegisterComponent(42, "minecraft:dyed_color"); RegisterComponent(43, "minecraft:map_color"); RegisterComponent(44, "minecraft:map_id"); RegisterComponent(45, "minecraft:map_decorations"); @@ -70,7 +70,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry RegisterComponent(51, "minecraft:suspicious_stew_effects"); RegisterComponent(52, "minecraft:writable_book_content"); RegisterComponent(53, "minecraft:written_book_content"); - RegisterComponent(54, "minecraft:trim"); + RegisterComponent(54, "minecraft:trim"); RegisterComponent(55, "minecraft:debug_stick_state"); RegisterComponent(56, "minecraft:entity_data"); RegisterComponent(57, "minecraft:bucket_entity_data"); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs index 3980e34d..cca30281 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry1215.cs @@ -29,8 +29,8 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry RegisterComponent(8, "minecraft:lore"); RegisterComponent(9, "minecraft:rarity"); RegisterComponent(10, "minecraft:enchantments"); - RegisterComponent(11, "minecraft:can_place_on"); - RegisterComponent(12, "minecraft:can_break"); + RegisterComponent(11, "minecraft:can_place_on"); + RegisterComponent(12, "minecraft:can_break"); if (uses1218AttributeAndEquippableFormats) RegisterComponent(13, "minecraft:attribute_modifiers"); else @@ -60,7 +60,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry RegisterComponent(32, "minecraft:death_protection"); RegisterComponent(33, "minecraft:blocks_attacks"); // NEW RegisterComponent(34, "minecraft:stored_enchantments"); - RegisterComponent(35, "minecraft:dyed_color"); + RegisterComponent(35, "minecraft:dyed_color"); RegisterComponent(36, "minecraft:map_color"); RegisterComponent(37, "minecraft:map_id"); RegisterComponent(38, "minecraft:map_decorations"); @@ -72,7 +72,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry RegisterComponent(44, "minecraft:suspicious_stew_effects"); RegisterComponent(45, "minecraft:writable_book_content"); RegisterComponent(46, "minecraft:written_book_content"); - RegisterComponent(47, "minecraft:trim"); + RegisterComponent(47, "minecraft:trim"); RegisterComponent(48, "minecraft:debug_stick_state"); RegisterComponent(49, "minecraft:entity_data"); RegisterComponent(50, "minecraft:bucket_entity_data"); diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs index 579ff9d6..eff20fe7 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs @@ -28,8 +28,8 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(11, "minecraft:lore"); RegisterComponent(12, "minecraft:rarity"); RegisterComponent(13, "minecraft:enchantments"); - RegisterComponent(14, "minecraft:can_place_on"); - RegisterComponent(15, "minecraft:can_break"); + RegisterComponent(14, "minecraft:can_place_on"); + RegisterComponent(15, "minecraft:can_break"); RegisterComponent(16, "minecraft:attribute_modifiers"); RegisterComponent(17, "minecraft:custom_model_data"); RegisterComponent(18, "minecraft:tooltip_display"); @@ -58,7 +58,7 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(41, "minecraft:additional_trade_cost"); // New in 26.1 RegisterComponent(42, "minecraft:stored_enchantments"); RegisterComponent(43, "minecraft:dye"); // New in 26.1 - RegisterComponent(44, "minecraft:dyed_color"); + RegisterComponent(44, "minecraft:dyed_color"); RegisterComponent(45, "minecraft:map_color"); RegisterComponent(46, "minecraft:map_id"); RegisterComponent(47, "minecraft:map_decorations"); @@ -70,7 +70,7 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(53, "minecraft:suspicious_stew_effects"); RegisterComponent(54, "minecraft:writable_book_content"); RegisterComponent(55, "minecraft:written_book_content"); - RegisterComponent(56, "minecraft:trim"); + RegisterComponent(56, "minecraft:trim"); RegisterComponent(57, "minecraft:debug_stick_state"); RegisterComponent(58, "minecraft:entity_data"); RegisterComponent(59, "minecraft:bucket_entity_data");