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");