using System; 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 ProfileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubComponentRegistry subComponentRegistry) : StructuredComponent(dataTypes, itemPalette, subComponentRegistry) { public bool HasName { get; set; } public string? Name { get; set; } = null!; public bool HasUniqueId { get; set; } 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) Name = DataTypes.ReadNextString(data); HasUniqueId = DataTypes.ReadNextBool(data); if (HasUniqueId) Uuid = DataTypes.ReadNextUUID(data); NumberOfProperties = DataTypes.ReadNextVarInt(data); ProfileProperties = ReadProfileProperties(data, NumberOfProperties); } 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(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) 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)); } } 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); public enum ProfileSkinModel { Wide, Slim }