diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 36b26917..3578fdf5 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -411,6 +411,12 @@ namespace MinecraftClient.Protocol.Handlers return ReadNextNbt(cache, true); } + public object? ReadNextNbtTag(Queue cache) + { + var tagType = ReadNextByte(cache); + return tagType == 0 ? null : ReadNbtField(cache, tagType); + } + /// /// Read an ItemStackTemplate (26.1+) from a cache of bytes. /// Unlike ItemStack, this uses item-first encoding: item_id, count, DataComponentPatch. @@ -1441,6 +1447,17 @@ namespace MinecraftClient.Protocol.Handlers return GetNbt(nbt, true); } + public byte[] GetNbtTag(object? tag) + { + if (tag is null) + return [0]; + + var tagData = GetNbtField(tag, out var tagType); + var data = new List { tagType }; + data.AddRange(tagData); + return data.ToArray(); + } + /// /// Build an uncompressed Named Binary Tag blob for sending over the network (internal) /// @@ -1892,6 +1909,39 @@ namespace MinecraftClient.Protocol.Handlers return slotData.ToArray(); } + /// + /// Get a byte array representing the given item as a non-empty ItemStackTemplate. + /// + /// Item + /// Item Palette + /// ItemStackTemplate representation + public byte[] GetItemStackTemplate(Item item, ItemPalette itemPalette) + { + List slotData = new(); + + slotData.AddRange(GetVarInt(itemPalette.ToId(item.Type))); + slotData.AddRange(GetVarInt(item.Count)); + + if (item.Components is not null && item.Components.Count > 0) + { + slotData.AddRange(GetVarInt(item.Components.Count)); + slotData.AddRange(GetVarInt(0)); // components to remove + foreach (var component in item.Components) + { + slotData.AddRange(GetVarInt(component.TypeId)); + var serialized = component.Serialize(); + slotData.AddRange(serialized); + } + } + else + { + slotData.AddRange(GetVarInt(0)); // no components to add + slotData.AddRange(GetVarInt(0)); // no components to remove + } + + return slotData.ToArray(); + } + /// /// Get a byte array representing an array of item slots /// diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs new file mode 100644 index 00000000..87a83a4e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ContainerComponent261.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class ContainerComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextBool(data) ? DataTypes.ReadNextItemStackTemplate(data, ItemPalette) : null); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + foreach (var item in Items) + { + var hasItem = item is not null && !item.IsEmpty; + data.AddRange(DataTypes.GetBool(hasItem)); + if (hasItem) + data.AddRange(DataTypes.GetItemStackTemplate(item!, ItemPalette)); + } + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs new file mode 100644 index 00000000..7c7fdc74 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/ItemStackTemplateListComponent261.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class ItemStackTemplateListComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public List Items { get; set; } = []; + + public override void Parse(Queue data) + { + var count = DataTypes.ReadNextVarInt(data); + + for (var i = 0; i < count; i++) + Items.Add(DataTypes.ReadNextItemStackTemplate(data, ItemPalette)); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(Items.Count)); + + foreach (var item in Items) + data.AddRange(DataTypes.GetItemStackTemplate(item, ItemPalette)); + + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs new file mode 100644 index 00000000..1c864dcf --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/NbtTagComponent261.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class NbtTagComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public object? Tag { get; set; } + + public override void Parse(Queue data) + { + Tag = DataTypes.ReadNextNbtTag(data); + } + + public override Queue Serialize() + { + return new Queue(DataTypes.GetNbtTag(Tag)); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs new file mode 100644 index 00000000..d3d7c507 --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/TypedEntityDataComponent261.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class TypedEntityDataComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public int EntityTypeId { get; set; } + public Dictionary? Nbt { get; set; } + + public override void Parse(Queue data) + { + EntityTypeId = DataTypes.ReadNextVarInt(data); + Nbt = DataTypes.ReadNextNbt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(EntityTypeId)); + data.AddRange(DataTypes.GetNbt(Nbt)); + return new Queue(data); + } +} + +public class BlockEntityDataComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : TypedEntityDataComponent261(dataTypes, itemPalette, subComponentRegistry) {} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs new file mode 100644 index 00000000..e5b3323e --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/26_1/UseRemainderComponent261.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using MinecraftClient.Inventory; +using MinecraftClient.Inventory.ItemPalettes; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; + +public class UseRemainderComponent261(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) + : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) +{ + public Item? ConvertInto { get; set; } + + public override void Parse(Queue data) + { + ConvertInto = DataTypes.ReadNextItemStackTemplate(data, ItemPalette); + } + + public override Queue Serialize() + { + var data = new List(); + if (ConvertInto is not null && !ConvertInto.IsEmpty) + data.AddRange(DataTypes.GetItemStackTemplate(ConvertInto, ItemPalette)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs index d3a3cd12..c4d809fd 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/StructuredComponentsRegistry261.cs @@ -4,6 +4,8 @@ using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21; using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9; using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_11; using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._26_1; using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; @@ -31,7 +33,7 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(13, "minecraft:enchantments"); RegisterComponent(14, "minecraft:can_place_on"); RegisterComponent(15, "minecraft:can_break"); - RegisterComponent(16, "minecraft:attribute_modifiers"); + RegisterComponent(16, "minecraft:attribute_modifiers"); RegisterComponent(17, "minecraft:custom_model_data"); RegisterComponent(18, "minecraft:tooltip_display"); RegisterComponent(19, "minecraft:repair_cost"); @@ -40,14 +42,14 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(22, "minecraft:intangible_projectile"); RegisterComponent(23, "minecraft:food"); RegisterComponent(24, "minecraft:consumable"); - RegisterComponent(25, "minecraft:use_remainder"); + RegisterComponent(25, "minecraft:use_remainder"); RegisterComponent(26, "minecraft:use_cooldown"); RegisterComponent(27, "minecraft:damage_resistant"); - RegisterComponent(28, "minecraft:tool"); + RegisterComponent(28, "minecraft:tool"); RegisterComponent(29, "minecraft:weapon"); RegisterComponent(30, "minecraft:attack_range"); RegisterComponent(31, "minecraft:enchantable"); - RegisterComponent(32, "minecraft:equippable"); + RegisterComponent(32, "minecraft:equippable"); RegisterComponent(33, "minecraft:repairable"); RegisterComponent(34, "minecraft:glider"); RegisterComponent(35, "minecraft:tooltip_style"); @@ -64,24 +66,24 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(46, "minecraft:map_id"); RegisterComponent(47, "minecraft:map_decorations"); RegisterComponent(48, "minecraft:map_post_processing"); - RegisterComponent(49, "minecraft:charged_projectiles"); - RegisterComponent(50, "minecraft:bundle_contents"); - RegisterComponent(51, "minecraft:potion_contents"); + RegisterComponent(49, "minecraft:charged_projectiles"); + RegisterComponent(50, "minecraft:bundle_contents"); + RegisterComponent(51, "minecraft:potion_contents"); RegisterComponent(52, "minecraft:potion_duration_scale"); RegisterComponent(53, "minecraft:suspicious_stew_effects"); RegisterComponent(54, "minecraft:writable_book_content"); RegisterComponent(55, "minecraft:written_book_content"); RegisterComponent(56, "minecraft:trim"); RegisterComponent(57, "minecraft:debug_stick_state"); - RegisterComponent(58, "minecraft:entity_data"); + RegisterComponent(58, "minecraft:entity_data"); RegisterComponent(59, "minecraft:bucket_entity_data"); - RegisterComponent(60, "minecraft:block_entity_data"); + RegisterComponent(60, "minecraft:block_entity_data"); RegisterComponent(61, "minecraft:instrument"); RegisterComponent(62, "minecraft:provides_trim_material"); RegisterComponent(63, "minecraft:ominous_bottle_amplifier"); - RegisterComponent(64, "minecraft:jukebox_playable"); + RegisterComponent(64, "minecraft:jukebox_playable"); RegisterComponent(65, "minecraft:provides_banner_patterns"); - RegisterComponent(66, "minecraft:recipes"); + RegisterComponent(66, "minecraft:recipes"); RegisterComponent(67, "minecraft:lodestone_tracker"); RegisterComponent(68, "minecraft:firework_explosion"); RegisterComponent(69, "minecraft:fireworks"); @@ -90,9 +92,9 @@ public class StructuredComponentsRegistry261 : StructuredComponentRegistry RegisterComponent(72, "minecraft:banner_patterns"); RegisterComponent(73, "minecraft:base_color"); RegisterComponent(74, "minecraft:pot_decorations"); - RegisterComponent(75, "minecraft:container"); + RegisterComponent(75, "minecraft:container"); RegisterComponent(76, "minecraft:block_state"); - RegisterComponent(77, "minecraft:bees"); + RegisterComponent(77, "minecraft:bees"); RegisterComponent(78, "minecraft:lock"); RegisterComponent(79, "minecraft:container_loot");