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
+}