Fixed Profile Component crashing on 1.21.9

This commit is contained in:
Anon 2026-03-22 21:53:13 +01:00
parent 5c39d3b83d
commit af405e5632
2 changed files with 186 additions and 27 deletions

View file

@ -31,6 +31,11 @@ namespace MinecraftClient.Protocol.Handlers
protocolversion = protocol;
}
/// <summary>
/// Protocol version used to adjust wire encodings.
/// </summary>
public int ProtocolVersion => protocolversion;
/// <summary>
/// Read some data from a cache of bytes and remove it from the cache
/// </summary>
@ -1898,4 +1903,4 @@ namespace MinecraftClient.Protocol.Handlers
return fields.ToArray();
}
}
}
}

View file

@ -13,12 +13,52 @@ public class ProfileComponent(DataTypes dataTypes, ItemPalette itemPalette, SubC
public Guid Uuid { get; set; }
public int NumberOfProperties { get; set; }
public List<ProfileProperty> 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<byte> data)
{
ResetState();
if (dataTypes.ProtocolVersion >= Protocol18Handler.MC_1_21_9_Version)
{
ParseResolvableProfile(data);
return;
}
ParseLegacyProfile(data);
}
public override Queue<byte> 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<byte> 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<byte> Serialize()
private void ParseResolvableProfile(Queue<byte> 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<byte> SerializeLegacyProfile()
{
var data = new List<byte>();
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<byte>(data);
}
private Queue<byte> SerializeResolvableProfile()
{
var data = new List<byte>();
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<byte>(data);
}
private List<ProfileProperty> ReadProfileProperties(Queue<byte> data, int count)
{
var properties = new List<ProfileProperty>(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<byte> 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<byte>(data);
}
private string? ReadOptionalResourceLocation(Queue<byte> data)
{
return dataTypes.ReadNextBool(data) ? dataTypes.ReadNextString(data) : null;
}
private void SerializeOptionalResourceLocation(List<byte> 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 record ProfileProperty(string Name, string Value, bool HasSignature, string? Signature);
public enum ProfileSkinModel
{
Wide,
Slim
}