Merge remote-tracking branch 'origin/master' into copilot/reimplement-chat-formatting-feature

# Conflicts:
#	MinecraftClient/Protocol/Message/ChatParser.cs
This commit is contained in:
copilot-swe-agent[bot] 2026-03-25 10:05:37 +00:00
commit 7e9be430db
27 changed files with 883 additions and 81 deletions

View file

@ -436,13 +436,29 @@ namespace MinecraftClient.Protocol.Handlers
var numberOfComponentsToAdd = ReadNextVarInt(cache); var numberOfComponentsToAdd = ReadNextVarInt(cache);
var numberofComponentsToRemove = 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++) for (var i = 0; i < numberOfComponentsToAdd; i++)
{ {
var componentTypeId = ReadNextVarInt(cache); 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); try
strcturedComponentsToAdd.Add(strcuturedComponentHandler.Parse(componentTypeId, cache)); {
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++) 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> /// <summary>
/// Read entity information from a cache of bytes and remove it from the cache /// Read entity information from a cache of bytes and remove it from the cache
/// </summary> /// </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 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) 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() public override Queue<byte> Serialize()
{ {
var data = new List<byte>(); 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); 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); var holderId = DataTypes.ReadNextVarInt(data);
if (holderId == 0) 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); var soundHolderId = DataTypes.ReadNextVarInt(data);
if (soundHolderId == 0) if (soundHolderId == 0)
{ {
@ -25,9 +25,10 @@ public class InstrumentComponent1215(DataTypes dataTypes, ItemPalette itemPalett
if (hasFixedRange) if (hasFixedRange)
DataTypes.ReadNextFloat(data); DataTypes.ReadNextFloat(data);
} }
DataTypes.ReadNextVarInt(data); // useDuration DataTypes.ReadNextFloat(data); // useDuration
DataTypes.ReadNextFloat(data); // range 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 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 // Optional<Component> title
if (DataTypes.ReadNextBool(data)) if (DataTypes.ReadNextBool(data))
DataTypes.ReadNextString(data); DataTypes.ReadNextNbt(data);
// Optional<Component> author // Optional<Component> author
if (DataTypes.ReadNextBool(data)) 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); // ResourceKey<EquipmentAsset>
DataTypes.ReadNextString(data); // override suffix DataTypes.ReadNextString(data); // override suffix
} }
// description Component // ComponentSerialization.STREAM_CODEC is NBT-backed, not a plain string.
DataTypes.ReadNextString(data); DataTypes.ReadNextNbt(data);
} }
} }
else 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<CanPlaceOnComponent>(10, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(11, "minecraft:can_break"); RegisterComponent<CanBreakComponent>(11, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(12, "minecraft:attribute_modifiers"); 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<HideAdditionalTooltipComponent>(14, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip"); RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost"); RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost");
@ -66,4 +66,4 @@ public class StructuredComponentsRegistry1206 : StructuredComponentRegistry
RegisterComponent<LockComponent>(54, "minecraft:lock"); RegisterComponent<LockComponent>(54, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(55, "minecraft:container_loot"); RegisterComponent<ContainerLootComponent>(55, "minecraft:container_loot");
} }
} }

View file

@ -23,7 +23,7 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<CanPlaceOnComponent>(10, "minecraft:can_place_on"); RegisterComponent<CanPlaceOnComponent>(10, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(11, "minecraft:can_break"); RegisterComponent<CanBreakComponent>(11, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(12, "minecraft:attribute_modifiers"); 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<HideAdditionalTooltipComponent>(14, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip"); RegisterComponent<HideTooltipComponent>(15, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost"); RegisterComponent<RepairCostComponent>(16, "minecraft:repair_cost");
@ -52,7 +52,7 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<BlockEntityDataComponent>(39, "minecraft:block_entity_data"); RegisterComponent<BlockEntityDataComponent>(39, "minecraft:block_entity_data");
RegisterComponent<InstrumentComponent>(40, "minecraft:instrument"); RegisterComponent<InstrumentComponent>(40, "minecraft:instrument");
RegisterComponent<OmniousBottleAmplifierComponent>(41, "minecraft:ominous_bottle_amplifier"); 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<RecipesComponent>(43, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(44, "minecraft:lodestone_tracker"); RegisterComponent<LodestoneTrackerComponent>(44, "minecraft:lodestone_tracker");
RegisterComponent<FireworkExplosionComponent>(45, "minecraft:firework_explosion"); RegisterComponent<FireworkExplosionComponent>(45, "minecraft:firework_explosion");
@ -68,4 +68,4 @@ public class StructuredComponentsRegistry121 : StructuredComponentRegistry
RegisterComponent<LockComponent>(55, "minecraft:lock"); RegisterComponent<LockComponent>(55, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(56, "minecraft:container_loot"); 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;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; 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_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._1_21_11;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
@ -30,7 +32,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<EnchantmentsComponent1215>(13, "minecraft:enchantments"); RegisterComponent<EnchantmentsComponent1215>(13, "minecraft:enchantments");
RegisterComponent<CanPlaceOnComponent>(14, "minecraft:can_place_on"); RegisterComponent<CanPlaceOnComponent>(14, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(15, "minecraft:can_break"); 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<CustomModelDataComponent>(17, "minecraft:custom_model_data");
RegisterComponent<TooltipDisplayComponent>(18, "minecraft:tooltip_display"); RegisterComponent<TooltipDisplayComponent>(18, "minecraft:tooltip_display");
RegisterComponent<RepairCostComponent>(19, "minecraft:repair_cost"); RegisterComponent<RepairCostComponent>(19, "minecraft:repair_cost");
@ -42,11 +44,11 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<UseRemainderComponent>(25, "minecraft:use_remainder"); RegisterComponent<UseRemainderComponent>(25, "minecraft:use_remainder");
RegisterComponent<UseCooldownComponent>(26, "minecraft:use_cooldown"); RegisterComponent<UseCooldownComponent>(26, "minecraft:use_cooldown");
RegisterComponent<DamageResistantComponent>(27, "minecraft:damage_resistant"); RegisterComponent<DamageResistantComponent>(27, "minecraft:damage_resistant");
RegisterComponent<ToolComponent>(28, "minecraft:tool"); RegisterComponent<ToolComponent1215>(28, "minecraft:tool");
RegisterComponent<WeaponComponent>(29, "minecraft:weapon"); RegisterComponent<WeaponComponent>(29, "minecraft:weapon");
RegisterComponent<AttackRangeComponent>(30, "minecraft:attack_range"); RegisterComponent<AttackRangeComponent>(30, "minecraft:attack_range");
RegisterComponent<EnchantableComponent>(31, "minecraft:enchantable"); RegisterComponent<EnchantableComponent>(31, "minecraft:enchantable");
RegisterComponent<EquippableComponent>(32, "minecraft:equippable"); RegisterComponent<EquippableComponent1218>(32, "minecraft:equippable");
RegisterComponent<RepairableComponent>(33, "minecraft:repairable"); RegisterComponent<RepairableComponent>(33, "minecraft:repairable");
RegisterComponent<GliderComponent>(34, "minecraft:glider"); RegisterComponent<GliderComponent>(34, "minecraft:glider");
RegisterComponent<TooltipStyleComponent>(35, "minecraft:tooltip_style"); RegisterComponent<TooltipStyleComponent>(35, "minecraft:tooltip_style");
@ -63,7 +65,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(46, "minecraft:map_post_processing"); RegisterComponent<MapPostProcessingComponent>(46, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(47, "minecraft:charged_projectiles"); RegisterComponent<ChargedProjectilesComponent>(47, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(48, "minecraft:bundle_contents"); 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<PotionDurationScaleComponent>(50, "minecraft:potion_duration_scale");
RegisterComponent<SuspiciousStewEffectsComponent>(51, "minecraft:suspicious_stew_effects"); RegisterComponent<SuspiciousStewEffectsComponent>(51, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(52, "minecraft:writable_book_content"); RegisterComponent<WritableBlookContentComponent>(52, "minecraft:writable_book_content");
@ -76,7 +78,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<InstrumentComponent1215>(59, "minecraft:instrument"); RegisterComponent<InstrumentComponent1215>(59, "minecraft:instrument");
RegisterComponent<ProvidesTrimMaterialComponent>(60, "minecraft:provides_trim_material"); RegisterComponent<ProvidesTrimMaterialComponent>(60, "minecraft:provides_trim_material");
RegisterComponent<OmniousBottleAmplifierComponent>(61, "minecraft:ominous_bottle_amplifier"); 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<ProvidesBannerPatternsComponent>(63, "minecraft:provides_banner_patterns");
RegisterComponent<RecipesComponent>(64, "minecraft:recipes"); RegisterComponent<RecipesComponent>(64, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(65, "minecraft:lodestone_tracker"); RegisterComponent<LodestoneTrackerComponent>(65, "minecraft:lodestone_tracker");
@ -89,7 +91,7 @@ public class StructuredComponentsRegistry12111 : StructuredComponentRegistry
RegisterComponent<PotDecorationsComponent>(72, "minecraft:pot_decorations"); RegisterComponent<PotDecorationsComponent>(72, "minecraft:pot_decorations");
RegisterComponent<ContainerComponent>(73, "minecraft:container"); RegisterComponent<ContainerComponent>(73, "minecraft:container");
RegisterComponent<BlockStateComponent>(74, "minecraft:block_state"); RegisterComponent<BlockStateComponent>(74, "minecraft:block_state");
RegisterComponent<BeesComponent>(75, "minecraft:bees"); RegisterComponent<BeesComponent1219>(75, "minecraft:bees");
RegisterComponent<LockComponent>(76, "minecraft:lock"); RegisterComponent<LockComponent>(76, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(77, "minecraft:container_loot"); RegisterComponent<ContainerLootComponent>(77, "minecraft:container_loot");

View file

@ -25,7 +25,7 @@ public class StructuredComponentsRegistry1212 : StructuredComponentRegistry
RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on"); RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(12, "minecraft:can_break"); RegisterComponent<CanBreakComponent>(12, "minecraft:can_break");
RegisterComponent<AttributeModifiersComponent>(13, "minecraft:attribute_modifiers"); 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<HideAdditionalTooltipComponent>(15, "minecraft:hide_additional_tooltip");
RegisterComponent<HideTooltipComponent>(16, "minecraft:hide_tooltip"); RegisterComponent<HideTooltipComponent>(16, "minecraft:hide_tooltip");
RegisterComponent<RepairCostComponent>(17, "minecraft:repair_cost"); RegisterComponent<RepairCostComponent>(17, "minecraft:repair_cost");
@ -52,7 +52,7 @@ public class StructuredComponentsRegistry1212 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(38, "minecraft:map_post_processing"); RegisterComponent<MapPostProcessingComponent>(38, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(39, "minecraft:charged_projectiles"); RegisterComponent<ChargedProjectilesComponent>(39, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(40, "minecraft:bundle_contents"); 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<SuspiciousStewEffectsComponent>(42, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(43, "minecraft:writable_book_content"); RegisterComponent<WritableBlookContentComponent>(43, "minecraft:writable_book_content");
RegisterComponent<WrittenBlookContentComponent>(44, "minecraft:written_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<BlockEntityDataComponent>(49, "minecraft:block_entity_data");
RegisterComponent<InstrumentComponent>(50, "minecraft:instrument"); RegisterComponent<InstrumentComponent>(50, "minecraft:instrument");
RegisterComponent<OmniousBottleAmplifierComponent>(51, "minecraft:ominous_bottle_amplifier"); 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<RecipesComponent>(53, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(54, "minecraft:lodestone_tracker"); RegisterComponent<LodestoneTrackerComponent>(54, "minecraft:lodestone_tracker");
RegisterComponent<FireworkExplosionComponent>(55, "minecraft:firework_explosion"); 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;
using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_2; 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_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; using MinecraftClient.Protocol.Handlers.StructuredComponents.Core;
namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries; namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Registries;
@ -13,6 +15,9 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
public StructuredComponentsRegistry1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) public StructuredComponentsRegistry1215(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry)
: base(dataTypes, itemPalette, 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<CustomDataComponent>(0, "minecraft:custom_data");
RegisterComponent<MaxStackSizeComponent>(1, "minecraft:max_stack_size"); RegisterComponent<MaxStackSizeComponent>(1, "minecraft:max_stack_size");
RegisterComponent<MaxDamageComponent>(2, "minecraft:max_damage"); RegisterComponent<MaxDamageComponent>(2, "minecraft:max_damage");
@ -26,7 +31,10 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<EnchantmentsComponent1215>(10, "minecraft:enchantments"); RegisterComponent<EnchantmentsComponent1215>(10, "minecraft:enchantments");
RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on"); RegisterComponent<CanPlaceOnComponent>(11, "minecraft:can_place_on");
RegisterComponent<CanBreakComponent>(12, "minecraft:can_break"); 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"); RegisterComponent<CustomModelDataComponent>(14, "minecraft:custom_model_data");
// 15: tooltip_display (NEW, replaces hide_additional_tooltip + hide_tooltip) // 15: tooltip_display (NEW, replaces hide_additional_tooltip + hide_tooltip)
RegisterComponent<TooltipDisplayComponent>(15, "minecraft:tooltip_display"); RegisterComponent<TooltipDisplayComponent>(15, "minecraft:tooltip_display");
@ -39,10 +47,13 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<UseRemainderComponent>(22, "minecraft:use_remainder"); RegisterComponent<UseRemainderComponent>(22, "minecraft:use_remainder");
RegisterComponent<UseCooldownComponent>(23, "minecraft:use_cooldown"); RegisterComponent<UseCooldownComponent>(23, "minecraft:use_cooldown");
RegisterComponent<DamageResistantComponent>(24, "minecraft:damage_resistant"); RegisterComponent<DamageResistantComponent>(24, "minecraft:damage_resistant");
RegisterComponent<ToolComponent>(25, "minecraft:tool"); RegisterComponent<ToolComponent1215>(25, "minecraft:tool");
RegisterComponent<WeaponComponent>(26, "minecraft:weapon"); // NEW RegisterComponent<WeaponComponent>(26, "minecraft:weapon"); // NEW
RegisterComponent<EnchantableComponent>(27, "minecraft:enchantable"); 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<RepairableComponent>(29, "minecraft:repairable");
RegisterComponent<GliderComponent>(30, "minecraft:glider"); RegisterComponent<GliderComponent>(30, "minecraft:glider");
RegisterComponent<TooltipStyleComponent>(31, "minecraft:tooltip_style"); RegisterComponent<TooltipStyleComponent>(31, "minecraft:tooltip_style");
@ -56,7 +67,7 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<MapPostProcessingComponent>(39, "minecraft:map_post_processing"); RegisterComponent<MapPostProcessingComponent>(39, "minecraft:map_post_processing");
RegisterComponent<ChargedProjectilesComponent>(40, "minecraft:charged_projectiles"); RegisterComponent<ChargedProjectilesComponent>(40, "minecraft:charged_projectiles");
RegisterComponent<BundleContentsComponent>(41, "minecraft:bundle_contents"); 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<PotionDurationScaleComponent>(43, "minecraft:potion_duration_scale"); // NEW
RegisterComponent<SuspiciousStewEffectsComponent>(44, "minecraft:suspicious_stew_effects"); RegisterComponent<SuspiciousStewEffectsComponent>(44, "minecraft:suspicious_stew_effects");
RegisterComponent<WritableBlookContentComponent>(45, "minecraft:writable_book_content"); 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<InstrumentComponent1215>(52, "minecraft:instrument"); // Changed to EitherHolder<Instrument> in 1.21.5
RegisterComponent<ProvidesTrimMaterialComponent>(53, "minecraft:provides_trim_material"); // NEW RegisterComponent<ProvidesTrimMaterialComponent>(53, "minecraft:provides_trim_material"); // NEW
RegisterComponent<OmniousBottleAmplifierComponent>(54, "minecraft:ominous_bottle_amplifier"); 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<ProvidesBannerPatternsComponent>(56, "minecraft:provides_banner_patterns"); // NEW
RegisterComponent<RecipesComponent>(57, "minecraft:recipes"); RegisterComponent<RecipesComponent>(57, "minecraft:recipes");
RegisterComponent<LodestoneTrackerComponent>(58, "minecraft:lodestone_tracker"); RegisterComponent<LodestoneTrackerComponent>(58, "minecraft:lodestone_tracker");
@ -82,7 +93,10 @@ public class StructuredComponentsRegistry1215 : StructuredComponentRegistry
RegisterComponent<PotDecorationsComponent>(65, "minecraft:pot_decorations"); RegisterComponent<PotDecorationsComponent>(65, "minecraft:pot_decorations");
RegisterComponent<ContainerComponent>(66, "minecraft:container"); RegisterComponent<ContainerComponent>(66, "minecraft:container");
RegisterComponent<BlockStateComponent>(67, "minecraft:block_state"); 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<LockComponent>(69, "minecraft:lock");
RegisterComponent<ContainerLootComponent>(70, "minecraft:container_loot"); RegisterComponent<ContainerLootComponent>(70, "minecraft:container_loot");

View file

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

View file

@ -119,7 +119,7 @@
</resheader> </resheader>
<data name="AppVars.Variables" xml:space="preserve"> <data name="AppVars.Variables" xml:space="preserve">
<value>can be used in some other fields as %yourvar% <value>can be used in some other fields as %yourvar%
%username% and %serverip% are reserved variables.</value> %username%, %login%, %serverip%, %serverport%, %datetime% and %players% are reserved read-only variables.</value>
</data> </data>
<data name="ChatBot" xml:space="preserve"> <data name="ChatBot" xml:space="preserve">
<value>=============================== # <value>=============================== #
@ -930,4 +930,4 @@ Note: This does NOT require a Bot Token, only an Application ID. Discord must be
<data name="Main.General.AuthlibUser" xml:space="preserve"> <data name="Main.General.AuthlibUser" xml:space="preserve">
<value>Yggdrasil authlib multi-user selection.</value> <value>Yggdrasil authlib multi-user selection.</value>
</data> </data>
</root> </root>

View file

@ -132,6 +132,8 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
// Facade assemblies needed for compiling scripts that reference netstandard libraries (e.g. Brigadier.NET) // Facade assemblies needed for compiling scripts that reference netstandard libraries (e.g. Brigadier.NET)
assemblyrefs.Add(new("netstandard")); assemblyrefs.Add(new("netstandard"));
assemblyrefs.Add(new("System.Runtime")); assemblyrefs.Add(new("System.Runtime"));
assemblyrefs.Add(new("System.Private.Uri"));
assemblyrefs.Add(new("System.Net.Requests"));
foreach (var refs in assemblyrefs) { foreach (var refs in assemblyrefs) {
Assembly? loadedAssembly; Assembly? loadedAssembly;
@ -180,7 +182,7 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder
// Add facade assemblies needed for Roslyn compilation when referencing // Add facade assemblies needed for Roslyn compilation when referencing
// libraries that target netstandard (e.g. Brigadier.NET). // libraries that target netstandard (e.g. Brigadier.NET).
var runtimeDir = Path.GetDirectoryName(SystemPrivateCoreLib)!; var runtimeDir = Path.GetDirectoryName(SystemPrivateCoreLib)!;
foreach (var facadeName in new[] { "netstandard.dll", "System.Runtime.dll" }) foreach (var facadeName in new[] { "netstandard.dll", "System.Runtime.dll", "System.Private.Uri.dll", "System.Net.Requests.dll" })
{ {
var facadePath = Path.Combine(runtimeDir, facadeName); var facadePath = Path.Combine(runtimeDir, facadeName);
if (File.Exists(facadePath)) if (File.Exists(facadePath))

View file

@ -1024,6 +1024,34 @@ namespace MinecraftClient
[NonSerialized] [NonSerialized]
readonly Lock varLock = new(); readonly Lock varLock = new();
private static bool TryGetReadOnlyVar(string varName, [NotNullWhen(true)] out object? varData)
{
switch (Settings.ToLowerIfNeed(varName))
{
case "username":
varData = InternalConfig.Username;
return true;
case "login":
varData = InternalConfig.Account.Login;
return true;
case "serverip":
varData = InternalConfig.ServerIP;
return true;
case "serverport":
varData = InternalConfig.ServerPort;
return true;
case "datetime":
varData = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
return true;
case "players":
varData = string.Join(", ", McClient.Instance?.GetOnlinePlayers() ?? []);
return true;
default:
varData = null;
return false;
}
}
/// <summary> /// <summary>
/// Set a custom %variable% which will be available through expandVars() /// Set a custom %variable% which will be available through expandVars()
/// </summary> /// </summary>
@ -1066,6 +1094,8 @@ namespace MinecraftClient
/// <returns>The value or null if the variable does not exists</returns> /// <returns>The value or null if the variable does not exists</returns>
public object? GetVar(string varName) public object? GetVar(string varName)
{ {
if (TryGetReadOnlyVar(varName, out object? readOnlyVar))
return readOnlyVar;
if (VarStirng.TryGetValue(varName, out string? valueString)) if (VarStirng.TryGetValue(varName, out string? valueString))
return valueString; return valueString;
else if (VarObject.TryGetValue(varName, out object? valueObject)) else if (VarObject.TryGetValue(varName, out object? valueObject))
@ -1081,6 +1111,8 @@ namespace MinecraftClient
/// <returns>The value or null if the variable does not exists</returns> /// <returns>The value or null if the variable does not exists</returns>
public bool TryGetVar(string varName, [NotNullWhen(true)] out object? varData) public bool TryGetVar(string varName, [NotNullWhen(true)] out object? varData)
{ {
if (TryGetReadOnlyVar(varName, out varData))
return true;
if (VarStirng.TryGetValue(varName, out string? valueString)) if (VarStirng.TryGetValue(varName, out string? valueString))
{ {
varData = valueString; varData = valueString;
@ -1142,32 +1174,22 @@ namespace MinecraftClient
string varname = var_name.ToString(); string varname = var_name.ToString();
string varname_lower = Settings.ToLowerIfNeed(varname); string varname_lower = Settings.ToLowerIfNeed(varname);
i = i + varname.Length + 1; i = i + varname.Length + 1;
switch (varname_lower) if (TryGetReadOnlyVar(varname_lower, out object? readOnlyVar))
{ {
case "username": result.Append(InternalConfig.Username); break; result.Append(readOnlyVar.ToString());
case "login": result.Append(InternalConfig.Account.Login); break; }
case "serverip": result.Append(InternalConfig.ServerIP); break; else if (localVars is not null && localVars.ContainsKey(varname_lower))
case "serverport": result.Append(InternalConfig.ServerPort); break; {
case "datetime": result.Append(localVars[varname_lower].ToString());
DateTime time = DateTime.Now; }
result.Append(String.Format("{0}-{1}-{2} {3}:{4}:{5}", else if (TryGetVar(varname_lower, out object? var_value))
time.Year.ToString("0000"), {
time.Month.ToString("00"), result.Append(var_value.ToString());
time.Day.ToString("00"), }
time.Hour.ToString("00"), else
time.Minute.ToString("00"), {
time.Second.ToString("00"))); result.Append("%" + varname + '%');
break;
default:
if (localVars is not null && localVars.ContainsKey(varname_lower))
result.Append(localVars[varname_lower].ToString());
else if (TryGetVar(varname_lower, out object? var_value))
result.Append(var_value.ToString());
else
result.Append("%" + varname + '%');
break;
} }
} }
else result.Append(str[i]); else result.Append(str[i]);

View file

@ -5,30 +5,19 @@ MCC.LogToConsole(mojangStatus);
//MCCScript Extensions //MCCScript Extensions
private static readonly System.Net.Http.HttpClient s_httpClient = new();
string PerformHttpRequest(string uri) string PerformHttpRequest(string uri)
{ {
var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri); return s_httpClient.GetStringAsync(uri).GetAwaiter().GetResult();
var response = (System.Net.HttpWebResponse)request.GetResponse();
string responseString;
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
responseString = reader.ReadToEnd();
return responseString;
} }
void SendHttpPostAsync(string uri, string text) void SendHttpPostAsync(string uri, string text)
{ {
new Thread(() => { new Thread(() => {
var request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(uri); using var content = new System.Net.Http.StringContent(text, System.Text.Encoding.UTF8, "text/plain");
request.ContentType = "text/plain"; using var response = s_httpClient.PostAsync(uri, content).GetAwaiter().GetResult();
request.Method = "POST"; string responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
streamWriter.Write(text);
var response = (System.Net.HttpWebResponse)request.GetResponse();
string responseString;
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
responseString = reader.ReadToEnd();
//LogToConsole(responseString); //LogToConsole(responseString);
}).Start(); }).Start();
} }

View file

@ -1066,7 +1066,7 @@ Coordinate = { x = 145, y = 64, z = 2045 }
<div class="custom-container tip"><p class="custom-container-title">Tip</p> <div class="custom-container tip"><p class="custom-container-title">Tip</p>
**`%username%`, `%serverip%`, `%datetime%` are reserved variables** **`%username%`, `%login%`, `%serverip%`, `%serverport%`, `%datetime%`, `%players%` are reserved read-only variables**
</div> </div>

View file

@ -6,7 +6,7 @@ title: Creating Simple Script
A simple script is a text file with one command per line. See the [Internal Commands](usage.md#internal-commands) section, or type `/help` in the console to see the available commands. Any line beginning with `#` is ignored and treated as a comment. A simple script is a text file with one command per line. See the [Internal Commands](usage.md#internal-commands) section, or type `/help` in the console to see the available commands. Any line beginning with `#` is ignored and treated as a comment.
Application variables defined with the `set` command or in the `[AppVars]` config section can be used. The following read-only variables are also available: `%username%`, `%login%`, `%serverip%`, `%serverport%`, `%datetime%`. Application variables defined with the `set` command or in the `[AppVars]` config section can be used. The following read-only variables are also available: `%username%`, `%login%`, `%serverip%`, `%serverport%`, `%datetime%`, `%players%` (`%players%` expands to the current online player names separated by commas, or an empty string when not connected).
## Example ## Example