bugfix: Fixed the component system from 1.20.6 - 1.21.11

This commit is contained in:
Anon 2026-03-25 02:25:25 +01:00 committed by GitHub
commit c85dd4b498
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 823 additions and 34 deletions

View file

@ -436,13 +436,29 @@ namespace MinecraftClient.Protocol.Handlers
var numberOfComponentsToAdd = ReadNextVarInt(cache);
var numberofComponentsToRemove = ReadNextVarInt(cache);
var structuredComponentHandler = new StructuredComponentsHandler(protocolversion, this, itemPalette);
var parsedComponents = new List<string>(numberOfComponentsToAdd);
for (var i = 0; i < numberOfComponentsToAdd; i++)
{
var componentTypeId = ReadNextVarInt(cache);
var componentName = structuredComponentHandler.GetComponentName(componentTypeId);
parsedComponents.Add($"{i}:{componentTypeId}:{componentName}:next={GetQueuePreview(cache, 24)}");
var strcuturedComponentHandler = new StructuredComponentsHandler(protocolversion, this, itemPalette);
strcturedComponentsToAdd.Add(strcuturedComponentHandler.Parse(componentTypeId, cache));
try
{
strcturedComponentsToAdd.Add(structuredComponentHandler.Parse(componentTypeId, cache));
}
catch (Exception ex)
{
var preview = GetQueuePreview(cache, 48);
throw new System.IO.InvalidDataException(
$"Failed to decode item component {componentTypeId} ({componentName}) for itemId {itemId}, " +
$"itemCount {itemCount}, addCount {numberOfComponentsToAdd}, removeCount {numberofComponentsToRemove}, " +
$"componentIndex {i}, remainingBytes {cache.Count}, nextBytes {preview}, " +
$"componentTrace [{string.Join(" | ", parsedComponents)}].",
ex);
}
}
for (var i = 0; i < numberofComponentsToRemove; i++)
@ -516,6 +532,29 @@ namespace MinecraftClient.Protocol.Handlers
}
}
private static string GetQueuePreview(Queue<byte> cache, int maxBytes)
{
if (cache.Count == 0)
return "<empty>";
var bytes = cache.ToArray();
var length = Math.Min(bytes.Length, maxBytes);
var preview = new StringBuilder(length * 3);
for (var i = 0; i < length; i++)
{
if (i > 0)
preview.Append(' ');
preview.Append(bytes[i].ToString("X2"));
}
if (bytes.Length > maxBytes)
preview.Append(" ...");
return preview.ToString();
}
/// <summary>
/// Read entity information from a cache of bytes and remove it from the cache
/// </summary>

View file

@ -6,17 +6,47 @@ namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_2
public class CustomModelDataComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int Value { get; set; }
public List<float> Floats { get; set; } = [];
public List<bool> Flags { get; set; } = [];
public List<string> Strings { get; set; } = [];
public List<int> Colors { get; set; } = [];
public override void Parse(Queue<byte> data)
{
Value = DataTypes.ReadNextVarInt(data);
Floats = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextFloat(componentData));
Flags = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextBool(componentData));
Strings = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextString(componentData));
Colors = ReadList(data, static (dataTypes, componentData) => dataTypes.ReadNextInt(componentData));
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(Value));
WriteList(data, Floats, static (dataTypes, value) => dataTypes.GetFloat(value));
WriteList(data, Flags, static (dataTypes, value) => dataTypes.GetBool(value));
WriteList(data, Strings, static (dataTypes, value) => dataTypes.GetString(value));
WriteList(data, Colors, static (_, value) => DataTypes.GetInt(value));
return new Queue<byte>(data);
}
}
private List<T> ReadList<T>(Queue<byte> data, ReadDelegate<T> read)
{
var count = DataTypes.ReadNextVarInt(data);
var values = new List<T>(count);
for (var i = 0; i < count; i++)
values.Add(read(DataTypes, data));
return values;
}
private void WriteList<T>(List<byte> data, List<T> values, WriteDelegate<T> write)
{
data.AddRange(DataTypes.GetVarInt(values.Count));
foreach (var value in values)
data.AddRange(write(DataTypes, value));
}
private delegate T ReadDelegate<out T>(DataTypes dataTypes, Queue<byte> data);
private delegate byte[] WriteDelegate<in T>(DataTypes dataTypes, T value);
}

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
public class CustomModelDataComponent1206(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int Value { get; set; }
public override void Parse(Queue<byte> data)
{
Value = DataTypes.ReadNextVarInt(data);
}
public override Queue<byte> Serialize()
{
return new Queue<byte>(DataTypes.GetVarInt(Value));
}
}

View file

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21;
public class JukeBoxPlayableComponent121(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public bool IsHolder { get; set; }
public int HolderId { get; set; }
public string? ResourceKey { get; set; }
public SoundEventSubComponent? SoundEvent { get; set; }
public Dictionary<string, object>? DescriptionNbt { get; set; }
public string Description { get; set; } = string.Empty;
public float Duration { get; set; }
public int ComparatorOutput { get; set; }
public bool ShowTooltip { get; set; }
public override void Parse(Queue<byte> data)
{
IsHolder = DataTypes.ReadNextBool(data);
if (IsHolder)
{
HolderId = DataTypes.ReadNextVarInt(data);
if (HolderId == 0)
{
SoundEvent = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data);
DescriptionNbt = DataTypes.ReadNextNbt(data);
Description = ChatParser.ParseText(DescriptionNbt);
Duration = DataTypes.ReadNextFloat(data);
ComparatorOutput = DataTypes.ReadNextVarInt(data);
}
}
else
{
ResourceKey = DataTypes.ReadNextString(data);
}
ShowTooltip = DataTypes.ReadNextBool(data);
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetBool(IsHolder));
if (IsHolder)
{
data.AddRange(DataTypes.GetVarInt(HolderId));
if (HolderId == 0)
{
if (SoundEvent is null)
throw new ArgumentNullException(nameof(SoundEvent), "Inline jukebox song requires a sound event.");
if (DescriptionNbt is null)
throw new ArgumentNullException(nameof(DescriptionNbt), "Inline jukebox song requires a description.");
data.AddRange(SoundEvent.Serialize());
data.AddRange(DataTypes.GetNbt(DescriptionNbt));
data.AddRange(DataTypes.GetFloat(Duration));
data.AddRange(DataTypes.GetVarInt(ComparatorOutput));
}
}
else
{
if (string.IsNullOrEmpty(ResourceKey))
throw new ArgumentNullException(nameof(ResourceKey), "Resource key is required for key-backed jukebox songs.");
data.AddRange(DataTypes.GetString(ResourceKey));
}
data.AddRange(DataTypes.GetBool(ShowTooltip));
return new Queue<byte>(data);
}
}

View file

@ -0,0 +1,61 @@
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
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_2;
public class PotionContentsComponent1212(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public bool HasPotionId { get; set; }
public int PotionId { get; set; }
public bool HasCustomColor { get; set; }
public int CustomColor { get; set; }
public List<PotionEffectSubComponent> Effects { get; set; } = [];
public bool HasCustomName { get; set; }
public string? CustomName { get; set; }
public override void Parse(Queue<byte> data)
{
HasPotionId = DataTypes.ReadNextBool(data);
if (HasPotionId)
PotionId = DataTypes.ReadNextVarInt(data);
HasCustomColor = DataTypes.ReadNextBool(data);
if (HasCustomColor)
CustomColor = DataTypes.ReadNextInt(data);
var numberOfEffects = DataTypes.ReadNextVarInt(data);
Effects = new List<PotionEffectSubComponent>(numberOfEffects);
for (var i = 0; i < numberOfEffects; i++)
Effects.Add((PotionEffectSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.PotionEffect, data));
HasCustomName = DataTypes.ReadNextBool(data);
if (HasCustomName)
CustomName = DataTypes.ReadNextString(data);
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetBool(HasPotionId));
if (HasPotionId)
data.AddRange(DataTypes.GetVarInt(PotionId));
data.AddRange(DataTypes.GetBool(HasCustomColor));
if (HasCustomColor)
data.AddRange(DataTypes.GetInt(CustomColor));
data.AddRange(DataTypes.GetVarInt(Effects.Count));
foreach (var effect in Effects)
data.AddRange(effect.Serialize());
data.AddRange(DataTypes.GetBool(HasCustomName));
if (HasCustomName && CustomName is not null)
data.AddRange(DataTypes.GetString(CustomName));
return new Queue<byte>(data);
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class AttributeModifiersComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int NumberOfAttributes { get; set; }
public List<SubComponent> Attributes { get; set; } = [];
public override void Parse(Queue<byte> data)
{
NumberOfAttributes = DataTypes.ReadNextVarInt(data);
Attributes = new List<SubComponent>(NumberOfAttributes);
for (var i = 0; i < NumberOfAttributes; i++)
Attributes.Add(SubComponentRegistry.ParseSubComponent(SubComponents.Attribute, data));
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(NumberOfAttributes));
if (Attributes.Count != NumberOfAttributes)
throw new ArgumentNullException(nameof(Attributes), "Attributes count must match NumberOfAttributes.");
foreach (var attribute in Attributes)
data.AddRange(attribute.Serialize());
return new Queue<byte>(data);
}
}

View file

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class EquippableComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int Slot { get; set; }
public SoundEventSubComponent? EquipSound { get; set; }
public bool HasAssetId { get; set; }
public string? AssetId { get; set; }
public bool HasCameraOverlay { get; set; }
public string? CameraOverlay { get; set; }
public bool HasAllowedEntities { get; set; }
public int AllowedEntitiesType { get; set; }
public string? AllowedEntitiesTag { get; set; }
public List<int>? AllowedEntitiesIds { get; set; }
public bool Dispensable { get; set; }
public bool Swappable { get; set; }
public bool DamageOnHurt { get; set; }
public bool EquipOnInteract { get; set; }
public override void Parse(Queue<byte> data)
{
Slot = DataTypes.ReadNextVarInt(data);
EquipSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data);
HasAssetId = DataTypes.ReadNextBool(data);
if (HasAssetId)
AssetId = DataTypes.ReadNextString(data);
HasCameraOverlay = DataTypes.ReadNextBool(data);
if (HasCameraOverlay)
CameraOverlay = DataTypes.ReadNextString(data);
HasAllowedEntities = DataTypes.ReadNextBool(data);
if (HasAllowedEntities)
{
AllowedEntitiesType = DataTypes.ReadNextVarInt(data);
if (AllowedEntitiesType == 0)
{
AllowedEntitiesTag = DataTypes.ReadNextString(data);
AllowedEntitiesIds = null;
}
else
{
AllowedEntitiesTag = null;
AllowedEntitiesIds = new List<int>(Math.Max(AllowedEntitiesType - 1, 0));
for (var i = 0; i < AllowedEntitiesType - 1; i++)
AllowedEntitiesIds.Add(DataTypes.ReadNextVarInt(data));
}
}
Dispensable = DataTypes.ReadNextBool(data);
Swappable = DataTypes.ReadNextBool(data);
DamageOnHurt = DataTypes.ReadNextBool(data);
EquipOnInteract = DataTypes.ReadNextBool(data);
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(Slot));
if (EquipSound is null)
throw new ArgumentNullException(nameof(EquipSound), "EquipSound is required.");
data.AddRange(EquipSound.Serialize());
data.AddRange(DataTypes.GetBool(HasAssetId));
if (HasAssetId && AssetId is not null)
data.AddRange(DataTypes.GetString(AssetId));
data.AddRange(DataTypes.GetBool(HasCameraOverlay));
if (HasCameraOverlay && CameraOverlay is not null)
data.AddRange(DataTypes.GetString(CameraOverlay));
data.AddRange(DataTypes.GetBool(HasAllowedEntities));
if (HasAllowedEntities)
{
data.AddRange(DataTypes.GetVarInt(AllowedEntitiesType));
if (AllowedEntitiesType == 0 && AllowedEntitiesTag is not null)
{
data.AddRange(DataTypes.GetString(AllowedEntitiesTag));
}
else if (AllowedEntitiesIds is not null)
{
foreach (var id in AllowedEntitiesIds)
data.AddRange(DataTypes.GetVarInt(id));
}
}
data.AddRange(DataTypes.GetBool(Dispensable));
data.AddRange(DataTypes.GetBool(Swappable));
data.AddRange(DataTypes.GetBool(DamageOnHurt));
data.AddRange(DataTypes.GetBool(EquipOnInteract));
return new Queue<byte>(data);
}
}

View file

@ -16,7 +16,7 @@ public class InstrumentComponent1215(DataTypes dataTypes, ItemPalette itemPalett
var holderId = DataTypes.ReadNextVarInt(data);
if (holderId == 0)
{
// Inline Instrument: SoundEvent holder + VarInt useDuration + Float range + Component description
// Inline Instrument: SoundEvent holder + Float useDuration + Float range + Component description
var soundHolderId = DataTypes.ReadNextVarInt(data);
if (soundHolderId == 0)
{
@ -25,9 +25,10 @@ public class InstrumentComponent1215(DataTypes dataTypes, ItemPalette itemPalett
if (hasFixedRange)
DataTypes.ReadNextFloat(data);
}
DataTypes.ReadNextVarInt(data); // useDuration
DataTypes.ReadNextFloat(data); // useDuration
DataTypes.ReadNextFloat(data); // range
DataTypes.ReadNextString(data); // description (Component as JSON string)
// ComponentSerialization.STREAM_CODEC is NBT-backed, not a plain string.
DataTypes.ReadNextNbt(data);
}
}
else

View file

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
public class JukeBoxPlayableComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public bool IsHolder { get; set; }
public int HolderId { get; set; }
public string? ResourceKey { get; set; }
public SoundEventSubComponent? SoundEvent { get; set; }
public Dictionary<string, object>? DescriptionNbt { get; set; }
public string Description { get; set; } = string.Empty;
public float Duration { get; set; }
public int ComparatorOutput { get; set; }
public override void Parse(Queue<byte> data)
{
IsHolder = DataTypes.ReadNextBool(data);
if (IsHolder)
{
HolderId = DataTypes.ReadNextVarInt(data);
if (HolderId == 0)
{
SoundEvent = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data);
DescriptionNbt = DataTypes.ReadNextNbt(data);
Description = ChatParser.ParseText(DescriptionNbt);
Duration = DataTypes.ReadNextFloat(data);
ComparatorOutput = DataTypes.ReadNextVarInt(data);
}
}
else
{
ResourceKey = DataTypes.ReadNextString(data);
}
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetBool(IsHolder));
if (IsHolder)
{
data.AddRange(DataTypes.GetVarInt(HolderId));
if (HolderId == 0)
{
if (SoundEvent is null)
throw new ArgumentNullException(nameof(SoundEvent), "Inline jukebox song requires a sound event.");
if (DescriptionNbt is null)
throw new ArgumentNullException(nameof(DescriptionNbt), "Inline jukebox song requires a description.");
data.AddRange(SoundEvent.Serialize());
data.AddRange(DataTypes.GetNbt(DescriptionNbt));
data.AddRange(DataTypes.GetFloat(Duration));
data.AddRange(DataTypes.GetVarInt(ComparatorOutput));
}
}
else
{
if (string.IsNullOrEmpty(ResourceKey))
throw new ArgumentNullException(nameof(ResourceKey), "Resource key is required for key-backed jukebox songs.");
data.AddRange(DataTypes.GetString(ResourceKey));
}
return new Queue<byte>(data);
}
}

View file

@ -20,11 +20,11 @@ public class PaintingVariantHolderComponent(DataTypes dataTypes, ItemPalette ite
// Optional<Component> title
if (DataTypes.ReadNextBool(data))
DataTypes.ReadNextString(data);
DataTypes.ReadNextNbt(data);
// Optional<Component> author
if (DataTypes.ReadNextBool(data))
DataTypes.ReadNextString(data);
DataTypes.ReadNextNbt(data);
}
}

View file

@ -25,8 +25,8 @@ public class ProvidesTrimMaterialComponent(DataTypes dataTypes, ItemPalette item
DataTypes.ReadNextString(data); // ResourceKey<EquipmentAsset>
DataTypes.ReadNextString(data); // override suffix
}
// description Component
DataTypes.ReadNextString(data);
// ComponentSerialization.STREAM_CODEC is NBT-backed, not a plain string.
DataTypes.ReadNextNbt(data);
}
}
else

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
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;
public class ToolComponent1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int NumberOfRules { get; set; }
public List<RuleSubComponent> Rules { get; set; } = [];
public float DefaultMiningSpeed { get; set; }
public int DamagePerBlock { get; set; }
public bool CanDestroyBlocksInCreative { get; set; }
public override void Parse(Queue<byte> data)
{
NumberOfRules = DataTypes.ReadNextVarInt(data);
Rules = new List<RuleSubComponent>(NumberOfRules);
for (var i = 0; i < NumberOfRules; i++)
Rules.Add((RuleSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.Rule, data));
DefaultMiningSpeed = DataTypes.ReadNextFloat(data);
DamagePerBlock = DataTypes.ReadNextVarInt(data);
CanDestroyBlocksInCreative = DataTypes.ReadNextBool(data);
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(NumberOfRules));
if (Rules.Count != NumberOfRules)
throw new ArgumentNullException(nameof(Rules), "Rules count must match NumberOfRules.");
foreach (var rule in Rules)
data.AddRange(rule.Serialize());
data.AddRange(DataTypes.GetFloat(DefaultMiningSpeed));
data.AddRange(DataTypes.GetVarInt(DamagePerBlock));
data.AddRange(DataTypes.GetBool(CanDestroyBlocksInCreative));
return new Queue<byte>(data);
}
}

View file

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.IO;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
using MinecraftClient.Protocol.Message;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8;
public class AttributeModifiersComponent1218(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int NumberOfAttributes { get; set; }
public List<SubComponent> Attributes { get; set; } = [];
public List<AttributeModifierDisplay> Displays { get; set; } = [];
public override void Parse(Queue<byte> data)
{
NumberOfAttributes = DataTypes.ReadNextVarInt(data);
Attributes = new List<SubComponent>(NumberOfAttributes);
Displays = new List<AttributeModifierDisplay>(NumberOfAttributes);
for (var i = 0; i < NumberOfAttributes; i++)
{
Attributes.Add(SubComponentRegistry.ParseSubComponent(SubComponents.Attribute, data));
Displays.Add(ReadDisplay(data));
}
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(NumberOfAttributes));
if (Attributes.Count != NumberOfAttributes)
throw new ArgumentNullException(nameof(Attributes), "Attributes count must match NumberOfAttributes.");
if (Displays.Count != NumberOfAttributes)
throw new ArgumentNullException(nameof(Displays), "Displays count must match NumberOfAttributes.");
for (var i = 0; i < NumberOfAttributes; i++)
{
data.AddRange(Attributes[i].Serialize());
data.AddRange(SerializeDisplay(Displays[i]));
}
return new Queue<byte>(data);
}
private AttributeModifierDisplay ReadDisplay(Queue<byte> data)
{
var displayType = DataTypes.ReadNextVarInt(data);
return displayType switch
{
0 => new AttributeModifierDisplay(AttributeModifierDisplayType.Default),
1 => new AttributeModifierDisplay(AttributeModifierDisplayType.Hidden),
2 => ReadOverrideDisplay(data),
_ => throw new InvalidDataException($"Unknown attribute modifier display type: {displayType}")
};
}
private AttributeModifierDisplay ReadOverrideDisplay(Queue<byte> data)
{
var overrideTextNbt = DataTypes.ReadNextNbt(data);
var overrideText = ChatParser.ParseText(overrideTextNbt);
return new AttributeModifierDisplay(AttributeModifierDisplayType.Override, overrideTextNbt, overrideText);
}
private Queue<byte> SerializeDisplay(AttributeModifierDisplay display)
{
var data = new List<byte>
{
};
data.AddRange(DataTypes.GetVarInt((int)display.Type));
if (display.Type == AttributeModifierDisplayType.Override)
{
if (display.OverrideTextNbt is null)
throw new ArgumentNullException(nameof(display.OverrideTextNbt), "Override display requires component NBT.");
data.AddRange(DataTypes.GetNbt(display.OverrideTextNbt));
}
return new Queue<byte>(data);
}
}
public sealed record AttributeModifierDisplay(
AttributeModifierDisplayType Type,
Dictionary<string, object>? OverrideTextNbt = null,
string? OverrideText = null);
public enum AttributeModifierDisplayType
{
Default = 0,
Hidden = 1,
Override = 2
}

View file

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_8;
public class EquippableComponent1218(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int Slot { get; set; }
public SoundEventSubComponent? EquipSound { get; set; }
public bool HasAssetId { get; set; }
public string? AssetId { get; set; }
public bool HasCameraOverlay { get; set; }
public string? CameraOverlay { get; set; }
public bool HasAllowedEntities { get; set; }
public int AllowedEntitiesType { get; set; }
public string? AllowedEntitiesTag { get; set; }
public List<int>? AllowedEntitiesIds { get; set; }
public bool Dispensable { get; set; }
public bool Swappable { get; set; }
public bool DamageOnHurt { get; set; }
public bool EquipOnInteract { get; set; }
public bool CanBeSheared { get; set; }
public SoundEventSubComponent? ShearingSound { get; set; }
public override void Parse(Queue<byte> data)
{
Slot = DataTypes.ReadNextVarInt(data);
EquipSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data);
HasAssetId = DataTypes.ReadNextBool(data);
if (HasAssetId)
AssetId = DataTypes.ReadNextString(data);
HasCameraOverlay = DataTypes.ReadNextBool(data);
if (HasCameraOverlay)
CameraOverlay = DataTypes.ReadNextString(data);
HasAllowedEntities = DataTypes.ReadNextBool(data);
if (HasAllowedEntities)
{
AllowedEntitiesType = DataTypes.ReadNextVarInt(data);
if (AllowedEntitiesType == 0)
{
AllowedEntitiesTag = DataTypes.ReadNextString(data);
AllowedEntitiesIds = null;
}
else
{
AllowedEntitiesTag = null;
AllowedEntitiesIds = new List<int>(Math.Max(AllowedEntitiesType - 1, 0));
for (var i = 0; i < AllowedEntitiesType - 1; i++)
AllowedEntitiesIds.Add(DataTypes.ReadNextVarInt(data));
}
}
Dispensable = DataTypes.ReadNextBool(data);
Swappable = DataTypes.ReadNextBool(data);
DamageOnHurt = DataTypes.ReadNextBool(data);
EquipOnInteract = DataTypes.ReadNextBool(data);
CanBeSheared = DataTypes.ReadNextBool(data);
ShearingSound = (SoundEventSubComponent)SubComponentRegistry.ParseSubComponent(SubComponents.SoundEvent, data);
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(Slot));
if (EquipSound is null)
throw new ArgumentNullException(nameof(EquipSound), "EquipSound is required.");
if (ShearingSound is null)
throw new ArgumentNullException(nameof(ShearingSound), "ShearingSound is required.");
data.AddRange(EquipSound.Serialize());
data.AddRange(DataTypes.GetBool(HasAssetId));
if (HasAssetId && AssetId is not null)
data.AddRange(DataTypes.GetString(AssetId));
data.AddRange(DataTypes.GetBool(HasCameraOverlay));
if (HasCameraOverlay && CameraOverlay is not null)
data.AddRange(DataTypes.GetString(CameraOverlay));
data.AddRange(DataTypes.GetBool(HasAllowedEntities));
if (HasAllowedEntities)
{
data.AddRange(DataTypes.GetVarInt(AllowedEntitiesType));
if (AllowedEntitiesType == 0 && AllowedEntitiesTag is not null)
{
data.AddRange(DataTypes.GetString(AllowedEntitiesTag));
}
else if (AllowedEntitiesIds is not null)
{
foreach (var id in AllowedEntitiesIds)
data.AddRange(DataTypes.GetVarInt(id));
}
}
data.AddRange(DataTypes.GetBool(Dispensable));
data.AddRange(DataTypes.GetBool(Swappable));
data.AddRange(DataTypes.GetBool(DamageOnHurt));
data.AddRange(DataTypes.GetBool(EquipOnInteract));
data.AddRange(DataTypes.GetBool(CanBeSheared));
data.AddRange(ShearingSound.Serialize());
return new Queue<byte>(data);
}
}

View file

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Inventory.ItemPalettes;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_9;
public class BeesComponent1219(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: StructuredComponent(dataTypes, itemPalette, subComponentRegistry)
{
public int NumberOfBees { get; set; }
public List<TypedBee> Bees { get; set; } = [];
public override void Parse(Queue<byte> data)
{
NumberOfBees = DataTypes.ReadNextVarInt(data);
Bees = new List<TypedBee>(NumberOfBees);
for (var i = 0; i < NumberOfBees; i++)
{
Bees.Add(
new TypedBee(
DataTypes.ReadNextVarInt(data),
DataTypes.ReadNextNbt(data),
DataTypes.ReadNextVarInt(data),
DataTypes.ReadNextVarInt(data)));
}
}
public override Queue<byte> Serialize()
{
var data = new List<byte>();
data.AddRange(DataTypes.GetVarInt(NumberOfBees));
if (NumberOfBees != Bees.Count)
throw new InvalidOperationException("Can't serialize the BeesComponent1219 because NumberOfBees and Bees.Count differ!");
foreach (var bee in Bees)
{
data.AddRange(DataTypes.GetVarInt(bee.EntityTypeId));
data.AddRange(DataTypes.GetNbt(bee.EntityDataNbt));
data.AddRange(DataTypes.GetVarInt(bee.TicksInHive));
data.AddRange(DataTypes.GetVarInt(bee.MinTicksInHive));
}
return new Queue<byte>(data);
}
}
public sealed record TypedBee(int EntityTypeId, Dictionary<string, object>? EntityDataNbt, int TicksInHive, int MinTicksInHive);

View file

@ -22,7 +22,7 @@ public class StructuredComponentsRegistry1206 : StructuredComponentRegistry
RegisterComponent<CanPlaceOnComponent>(10, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(11, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(12, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(13, "minecraft:custom_model_data");
RegisterComponent<CustomModelDataComponent1206>(13, "minecraft:custom_model_data");
RegisterComponent<HideAdditionalTooltipComponent>(14, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost");
@ -66,4 +66,4 @@ public class StructuredComponentsRegistry1206 : StructuredComponentRegistry
RegisterComponent<LockComponent>(54, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(55, "minecraft:container_loot");
}
}
}

View file

@ -23,7 +23,7 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<CanPlaceOnComponent>(10, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(11, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(12, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(13, "minecraft:custom_model_data");
RegisterComponent<CustomModelDataComponent1206>(13, "minecraft:custom_model_data");
RegisterComponent<HideAdditionalTooltipComponent>(14, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost");
@ -52,7 +52,7 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<BlockEntityDataComponent>(39, "minecraft:block_entity_data");
RegisterComponent<InstrumentComponent>(40, "minecraft:instrument");
RegisterComponent<OmniousBottleAmplifierComponent>(41, "minecraft:ominous_bottle_amplifier");
RegisterComponent<JukeBoxPlayableComponent>(42, "minecraft:jukebox_playable");
RegisterComponent<JukeBoxPlayableComponent121>(42, "minecraft:jukebox_playable");
RegisterComponent<RecipesComponent>(43, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(44, "minecraft:lodestone_tracker");
RegisterComponent<FireworkExplosionComponent>(45, "minecraft:firework_explosion");
@ -68,4 +68,4 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<LockComponent>(55, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(56, "minecraft:container_loot");
}
}
}

View file

@ -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.Core;
@ -30,7 +32,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<EnchantmentsComponent1215>(13, "minecraft:enchantments");
RegisterComponent<CanPlaceOnComponent>(14, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(15, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(16, "minecraft:attribute_modifiers");
RegisterComponent<AttributeModifiersComponent1218>(16, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(17, "minecraft:custom_model_data");
RegisterComponent<TooltipDisplayComponent>(18, "minecraft:tooltip_display");
RegisterComponent<RepairCostComponent>(19, "minecraft:repair_cost");
@ -42,11 +44,11 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<UseRemainderComponent>(25, "minecraft:use_remainder");
RegisterComponent<UseCooldownComponent>(26, "minecraft:use_cooldown");
RegisterComponent<DamageResistantComponent>(27, "minecraft:damage_resistant");
RegisterComponent<ToolComponent>(28, "minecraft:tool");
RegisterComponent<ToolComponent1215>(28, "minecraft:tool");
RegisterComponent<WeaponComponent>(29, "minecraft:weapon");
RegisterComponent<AttackRangeComponent>(30, "minecraft:attack_range");
RegisterComponent<EnchantableComponent>(31, "minecraft:enchantable");
RegisterComponent<EquippableComponent>(32, "minecraft:equippable");
RegisterComponent<EquippableComponent1218>(32, "minecraft:equippable");
RegisterComponent<RepairableComponent>(33, "minecraft:repairable");
RegisterComponent<GliderComponent>(34, "minecraft:glider");
RegisterComponent<TooltipStyleComponent>(35, "minecraft:tooltip_style");
@ -63,7 +65,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(46, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(47, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(48, "minecraft:bundle_contents");
RegisterComponent<PotionContentsComponent>(49, "minecraft:potion_contents");
RegisterComponent<PotionContentsComponent1212>(49, "minecraft:potion_contents");
RegisterComponent<PotionDurationScaleComponent>(50, "minecraft:potion_duration_scale");
RegisterComponent<SuspiciousStewEffectsComponent>(51, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(52, "minecraft:writable_book_content");
@ -76,7 +78,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<InstrumentComponent1215>(59, "minecraft:instrument");
RegisterComponent<ProvidesTrimMaterialComponent>(60, "minecraft:provides_trim_material");
RegisterComponent<OmniousBottleAmplifierComponent>(61, "minecraft:ominous_bottle_amplifier");
RegisterComponent<JukeBoxPlayableComponent>(62, "minecraft:jukebox_playable");
RegisterComponent<JukeBoxPlayableComponent1215>(62, "minecraft:jukebox_playable");
RegisterComponent<ProvidesBannerPatternsComponent>(63, "minecraft:provides_banner_patterns");
RegisterComponent<RecipesComponent>(64, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(65, "minecraft:lodestone_tracker");
@ -89,7 +91,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<PotDecorationsComponent>(72, "minecraft:pot_decorations");
RegisterComponent<ContainerComponent>(73, "minecraft:container");
RegisterComponent<BlockStateComponent>(74, "minecraft:block_state");
RegisterComponent<BeesComponent>(75, "minecraft:bees");
RegisterComponent<BeesComponent1219>(75, "minecraft:bees");
RegisterComponent<LockComponent>(76, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(77, "minecraft:container_loot");

View file

@ -25,7 +25,7 @@ public class StructuredComponentsRegistry1212 : StructuredComponentRegistry
RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(12, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(13, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(14, "minecraft:custom_model_data");
RegisterComponent<CustomModelDataComponent1206>(14, "minecraft:custom_model_data");
RegisterComponent<HideAdditionalTooltipComponent>(15, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(16, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(17, "minecraft:repair_cost");
@ -52,7 +52,7 @@ public class StructuredComponentsRegistry1212 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(38, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(39, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(40, "minecraft:bundle_contents");
RegisterComponent<PotionContentsComponent>(41, "minecraft:potion_contents");
RegisterComponent<PotionContentsComponent1212>(41, "minecraft:potion_contents");
RegisterComponent<SuspiciousStewEffectsComponent>(42, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(43, "minecraft:writable_book_content");
RegisterComponent<WrittenBlookContentComponent>(44, "minecraft:written_book_content");
@ -63,7 +63,7 @@ public class StructuredComponentsRegistry1212 : StructuredComponentRegistry
RegisterComponent<BlockEntityDataComponent>(49, "minecraft:block_entity_data");
RegisterComponent<InstrumentComponent>(50, "minecraft:instrument");
RegisterComponent<OmniousBottleAmplifierComponent>(51, "minecraft:ominous_bottle_amplifier");
RegisterComponent<JukeBoxPlayableComponent>(52, "minecraft:jukebox_playable");
RegisterComponent<JukeBoxPlayableComponent121>(52, "minecraft:jukebox_playable");
RegisterComponent<RecipesComponent>(53, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(54, "minecraft:lodestone_tracker");
RegisterComponent<FireworkExplosionComponent>(55, "minecraft:firework_explosion");

View file

@ -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.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries;
@ -13,6 +15,9 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
public StructuredComponentsRegistry1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: base(dataTypes, itemPalette, subComponentRegistry)
{
var uses1218AttributeAndEquippableFormats = dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_6_Version;
var usesTypedBeesFormat = dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version;
RegisterComponent<CustomDataComponent>(0, "minecraft:custom_data");
RegisterComponent<MaxStackSizeComponent>(1, "minecraft:max_stack_size");
RegisterComponent<MaxDamageComponent>(2, "minecraft:max_damage");
@ -26,7 +31,10 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<EnchantmentsComponent1215>(10, "minecraft:enchantments");
RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(12, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(13, "minecraft:attribute_modifiers");
if (uses1218AttributeAndEquippableFormats)
RegisterComponent<AttributeModifiersComponent1218>(13, "minecraft:attribute_modifiers");
else
RegisterComponent<AttributeModifiersComponent1215>(13, "minecraft:attribute_modifiers");
RegisterComponent<CustomModelDataComponent>(14, "minecraft:custom_model_data");
// 15: tooltip_display (NEW, replaces hide_additional_tooltip + hide_tooltip)
RegisterComponent<TooltipDisplayComponent>(15, "minecraft:tooltip_display");
@ -39,10 +47,13 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<UseRemainderComponent>(22, "minecraft:use_remainder");
RegisterComponent<UseCooldownComponent>(23, "minecraft:use_cooldown");
RegisterComponent<DamageResistantComponent>(24, "minecraft:damage_resistant");
RegisterComponent<ToolComponent>(25, "minecraft:tool");
RegisterComponent<ToolComponent1215>(25, "minecraft:tool");
RegisterComponent<WeaponComponent>(26, "minecraft:weapon"); // NEW
RegisterComponent<EnchantableComponent>(27, "minecraft:enchantable");
RegisterComponent<EquippableComponent>(28, "minecraft:equippable");
if (uses1218AttributeAndEquippableFormats)
RegisterComponent<EquippableComponent1218>(28, "minecraft:equippable");
else
RegisterComponent<EquippableComponent1215>(28, "minecraft:equippable");
RegisterComponent<RepairableComponent>(29, "minecraft:repairable");
RegisterComponent<GliderComponent>(30, "minecraft:glider");
RegisterComponent<TooltipStyleComponent>(31, "minecraft:tooltip_style");
@ -56,7 +67,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(39, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(40, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(41, "minecraft:bundle_contents");
RegisterComponent<PotionContentsComponent>(42, "minecraft:potion_contents");
RegisterComponent<PotionContentsComponent1212>(42, "minecraft:potion_contents");
RegisterComponent<PotionDurationScaleComponent>(43, "minecraft:potion_duration_scale"); // NEW
RegisterComponent<SuspiciousStewEffectsComponent>(44, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(45, "minecraft:writable_book_content");
@ -69,7 +80,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<InstrumentComponent1215>(52, "minecraft:instrument"); // Changed to EitherHolder<Instrument> in 1.21.5
RegisterComponent<ProvidesTrimMaterialComponent>(53, "minecraft:provides_trim_material"); // NEW
RegisterComponent<OmniousBottleAmplifierComponent>(54, "minecraft:ominous_bottle_amplifier");
RegisterComponent<JukeBoxPlayableComponent>(55, "minecraft:jukebox_playable");
RegisterComponent<JukeBoxPlayableComponent1215>(55, "minecraft:jukebox_playable");
RegisterComponent<ProvidesBannerPatternsComponent>(56, "minecraft:provides_banner_patterns"); // NEW
RegisterComponent<RecipesComponent>(57, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(58, "minecraft:lodestone_tracker");
@ -82,7 +93,10 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<PotDecorationsComponent>(65, "minecraft:pot_decorations");
RegisterComponent<ContainerComponent>(66, "minecraft:container");
RegisterComponent<BlockStateComponent>(67, "minecraft:block_state");
RegisterComponent<BeesComponent>(68, "minecraft:bees"); // Wire format unchanged in 1.21.5
if (usesTypedBeesFormat)
RegisterComponent<BeesComponent1219>(68, "minecraft:bees");
else
RegisterComponent<BeesComponent>(68, "minecraft:bees");
RegisterComponent<LockComponent>(69, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(70, "minecraft:container_loot");

View file

@ -48,4 +48,16 @@ public class StructuredComponentsHandler
{
return ComponentRegistry.ParseComponent(componentId, data);
}
}
public string GetComponentName(int componentId)
{
try
{
return ComponentRegistry.GetComponentNameById(componentId);
}
catch
{
return "<unknown>";
}
}
}