From af405e56327a3ee54cff027504a96a8a132806cb Mon Sep 17 00:00:00 2001 From: Anon Date: Sun, 22 Mar 2026 21:53:13 +0100 Subject: [PATCH] Fixed Profile Component crashing on 1.21.9 --- .../Protocol/Handlers/DataTypes.cs | 7 +- .../Components/1_20_6/ProfileComponent.cs | 206 +++++++++++++++--- 2 files changed, 186 insertions(+), 27 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index 3af09266..509c52ef 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -31,6 +31,11 @@ namespace MinecraftClient.Protocol.Handlers protocolversion = protocol; } + /// + /// Protocol version used to adjust wire encodings. + /// + public int ProtocolVersion => protocolversion; + /// /// Read some data from a cache of bytes and remove it from the cache /// @@ -1898,4 +1903,4 @@ namespace MinecraftClient.Protocol.Handlers return fields.ToArray(); } } -} \ No newline at end of file +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs index 17853fbd..cafacfec 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/1_20_6/ProfileComponent.cs @@ -13,12 +13,52 @@ public class ProfileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubC public Guid Uuid { get; set; } public int NumberOfProperties { get; set; } public List ProfileProperties { get; set; } = []; + public bool IsFullProfile { get; set; } + public string? BodyAssetId { get; set; } + public string? CapeAssetId { get; set; } + public string? ElytraAssetId { get; set; } + public ProfileSkinModel? Model { get; set; } public override void Parse(Queue data) + { + ResetState(); + + if (dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version) + { + ParseResolvableProfile(data); + return; + } + + ParseLegacyProfile(data); + } + + public override Queue Serialize() + { + return dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version + ? SerializeResolvableProfile() + : SerializeLegacyProfile(); + } + + private void ResetState() + { + HasName = false; + Name = null; + HasUniqueId = false; + Uuid = Guid.Empty; + NumberOfProperties = 0; + ProfileProperties = []; + IsFullProfile = false; + BodyAssetId = null; + CapeAssetId = null; + ElytraAssetId = null; + Model = null; + } + + private void ParseLegacyProfile(Queue data) { HasName = dataTypes.ReadNextBool(data); - - if(HasName) + + if (HasName) Name = dataTypes.ReadNextString(data); HasUniqueId = dataTypes.ReadNextBool(data); @@ -27,51 +67,165 @@ public class ProfileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubC Uuid = dataTypes.ReadNextUUID(data); NumberOfProperties = dataTypes.ReadNextVarInt(data); - for (var i = 0; i < NumberOfProperties; i++) - { - var propertyName = dataTypes.ReadNextString(data); - var propertyValue = dataTypes.ReadNextString(data); - var hasSignature = dataTypes.ReadNextBool(data); - var signature = hasSignature ? dataTypes.ReadNextString(data) : null; - - ProfileProperties.Add(new ProfileProperty(propertyName, propertyValue, hasSignature, signature)); - } + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); } - public override Queue Serialize() + private void ParseResolvableProfile(Queue data) + { + IsFullProfile = dataTypes.ReadNextBool(data); + + if (IsFullProfile) + { + HasUniqueId = true; + Uuid = dataTypes.ReadNextUUID(data); + HasName = true; + Name = dataTypes.ReadNextString(data); + NumberOfProperties = dataTypes.ReadNextVarInt(data); + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); + } + else + { + HasName = dataTypes.ReadNextBool(data); + if (HasName) + Name = dataTypes.ReadNextString(data); + + HasUniqueId = dataTypes.ReadNextBool(data); + if (HasUniqueId) + Uuid = dataTypes.ReadNextUUID(data); + + NumberOfProperties = dataTypes.ReadNextVarInt(data); + ProfileProperties = ReadProfileProperties(data, NumberOfProperties); + } + + BodyAssetId = ReadOptionalResourceLocation(data); + CapeAssetId = ReadOptionalResourceLocation(data); + ElytraAssetId = ReadOptionalResourceLocation(data); + + if (dataTypes.ReadNextBool(data)) + Model = dataTypes.ReadNextBool(data) ? ProfileSkinModel.Slim : ProfileSkinModel.Wide; + } + + private Queue SerializeLegacyProfile() { var data = new List(); - + NumberOfProperties = ProfileProperties.Count; + data.AddRange(DataTypes.GetBool(HasName)); if (HasName) { if (string.IsNullOrEmpty(Name)) throw new NullReferenceException("Can't serialize the ProfileComponent because the Name is null/empty!"); - + data.AddRange(DataTypes.GetString(Name)); } - + data.AddRange(DataTypes.GetBool(HasUniqueId)); if (HasUniqueId) data.AddRange(DataTypes.GetUUID(Uuid)); - data.AddRange(DataTypes.GetVarInt(ProfileProperties.Count)); + data.AddRange(DataTypes.GetVarInt(NumberOfProperties)); + SerializeProfileProperties(data); + + return new Queue(data); + } + + private Queue SerializeResolvableProfile() + { + var data = new List(); + NumberOfProperties = ProfileProperties.Count; + + data.AddRange(DataTypes.GetBool(IsFullProfile)); + if (IsFullProfile) + { + if (!HasUniqueId) + throw new NullReferenceException("Can't serialize the ProfileComponent because a full profile requires a UUID!"); + + if (!HasName || string.IsNullOrEmpty(Name)) + throw new NullReferenceException("Can't serialize the ProfileComponent because a full profile requires a name!"); + + data.AddRange(DataTypes.GetUUID(Uuid)); + data.AddRange(DataTypes.GetString(Name)); + } + else + { + data.AddRange(DataTypes.GetBool(HasName)); + if (HasName) + { + if (string.IsNullOrEmpty(Name)) + throw new NullReferenceException("Can't serialize the ProfileComponent because HasName is true, but the Name is null/empty!"); + + data.AddRange(DataTypes.GetString(Name)); + } + + data.AddRange(DataTypes.GetBool(HasUniqueId)); + if (HasUniqueId) + data.AddRange(DataTypes.GetUUID(Uuid)); + } + + data.AddRange(DataTypes.GetVarInt(NumberOfProperties)); + SerializeProfileProperties(data); + + SerializeOptionalResourceLocation(data, BodyAssetId); + SerializeOptionalResourceLocation(data, CapeAssetId); + SerializeOptionalResourceLocation(data, ElytraAssetId); + + data.AddRange(DataTypes.GetBool(Model.HasValue)); + if (Model.HasValue) + data.AddRange(dataTypes.GetBool(Model.Value == ProfileSkinModel.Slim)); + + return new Queue(data); + } + + private List ReadProfileProperties(Queue data, int count) + { + var properties = new List(count); + for (var i = 0; i < count; i++) + { + var propertyName = dataTypes.ReadNextString(data); + var propertyValue = dataTypes.ReadNextString(data); + var hasSignature = dataTypes.ReadNextBool(data); + var signature = hasSignature ? dataTypes.ReadNextString(data) : null; + + properties.Add(new ProfileProperty(propertyName, propertyValue, hasSignature, signature)); + } + + return properties; + } + + private void SerializeProfileProperties(List data) + { foreach (var profileProperty in ProfileProperties) { data.AddRange(DataTypes.GetString(profileProperty.Name)); data.AddRange(DataTypes.GetString(profileProperty.Value)); data.AddRange(DataTypes.GetBool(profileProperty.HasSignature)); - if (profileProperty.HasSignature) - { - if(string.IsNullOrEmpty(profileProperty.Signature)) - throw new NullReferenceException("Can't serialize the ProfileComponent because HasSignature is true, but the Signature is null/empty!"); - - data.AddRange(DataTypes.GetString(profileProperty.Signature)); - } + if (!profileProperty.HasSignature) + continue; + + if (string.IsNullOrEmpty(profileProperty.Signature)) + throw new NullReferenceException("Can't serialize the ProfileComponent because HasSignature is true, but the Signature is null/empty!"); + + data.AddRange(DataTypes.GetString(profileProperty.Signature)); } - - return new Queue(data); + } + + private string? ReadOptionalResourceLocation(Queue data) + { + return dataTypes.ReadNextBool(data) ? dataTypes.ReadNextString(data) : null; + } + + private void SerializeOptionalResourceLocation(List data, string? resourceLocation) + { + data.AddRange(DataTypes.GetBool(!string.IsNullOrEmpty(resourceLocation))); + if (!string.IsNullOrEmpty(resourceLocation)) + data.AddRange(DataTypes.GetString(resourceLocation)); } } -public record ProfileProperty(string Name, string Value, bool HasSignature, string? Signature); \ No newline at end of file +public record ProfileProperty(string Name, string Value, bool HasSignature, string? Signature); + +public enum ProfileSkinModel +{ + Wide, + Slim +}