From c23c229eb2d0350cfd32bff07cbd4c919d3887f3 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Fri, 20 Mar 2026 01:22:25 +0800 Subject: [PATCH] feat: protocol 767 (1.21) packet handling and AttributeSubComponent update - Add AttributeSubComponent121 that uses ResourceLocation(string) instead of UUID+Name, matching the 1.21 attribute modifier wire format change. Register it in SubComponentRegistry121 via new ReplaceSubComponent method. - Add ProjectilePower packet handler: reads 1 double (accelerationPower) for 1.21+, or 3 doubles (xPower/yPower/zPower) for 1.20.6. - Add CustomReportDetails and ServerLinks packet handlers in both Play and Configuration phases, consuming all fields to prevent byte offset errors on 1.21 servers. Made-with: Cursor --- .../Protocol/Handlers/Protocol18.cs | 61 ++++++++++++++++++- .../1_21/AttributeSubComponent121.cs | 38 ++++++++++++ .../Core/SubComponentRegistry.cs | 5 ++ .../Subcomponents/SubComponentRegistry121.cs | 1 + 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index e34306b7..77dc845c 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -191,6 +191,7 @@ namespace MinecraftClient.Protocol.Handlers // Item palette > MC_1_21_Version when handler.GetInventoryEnabled() => throw new NotImplementedException(Translations.exception_palette_item), + >= MC_1_21_Version => new ItemPalette121(), >= MC_1_20_6_Version => new ItemPalette1206(), >= MC_1_20_4_Version => new ItemPalette1204(), >= MC_1_20_Version => new ItemPalette120(), @@ -550,6 +551,28 @@ namespace MinecraftClient.Protocol.Handlers SendKnownDataPacks(vanillaPacks); break; + case ConfigurationPacketTypesIn.CustomReportDetails: + var cfgDetailsCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < cfgDetailsCount; i++) + { + dataTypes.ReadNextString(packetData); // Title + dataTypes.ReadNextString(packetData); // Description + } + break; + + case ConfigurationPacketTypesIn.ServerLinks: + var cfgLinksCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < cfgLinksCount; i++) + { + var cfgIsBuiltIn = dataTypes.ReadNextBool(packetData); + if (cfgIsBuiltIn) + dataTypes.ReadNextVarInt(packetData); // Known type ID + else + dataTypes.ReadNextChat(packetData); // Component label + dataTypes.ReadNextString(packetData); // URL + } + break; + // Ignore other packets at this stage default: return true; @@ -2827,7 +2850,43 @@ namespace MinecraftClient.Protocol.Handlers McClient.Instance?.Transfer(host, port); break; - + + case PacketTypesIn.ProjectilePower: + dataTypes.ReadNextVarInt(packetData); // Entity ID + if (protocolVersion >= MC_1_21_Version) + { + dataTypes.ReadNextDouble(packetData); // Acceleration Power + } + else + { + dataTypes.ReadNextDouble(packetData); // X Power + dataTypes.ReadNextDouble(packetData); // Y Power + dataTypes.ReadNextDouble(packetData); // Z Power + } + break; + + case PacketTypesIn.CustomReportDetails: + var detailsCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < detailsCount; i++) + { + dataTypes.ReadNextString(packetData); // Title + dataTypes.ReadNextString(packetData); // Description + } + break; + + case PacketTypesIn.ServerLinks: + var linksCount = dataTypes.ReadNextVarInt(packetData); + for (var i = 0; i < linksCount; i++) + { + var isBuiltIn = dataTypes.ReadNextBool(packetData); + if (isBuiltIn) + dataTypes.ReadNextVarInt(packetData); // Known type ID + else + dataTypes.ReadNextChat(packetData); // Component label + dataTypes.ReadNextString(packetData); // URL + } + break; + default: return false; //Ignored packet } diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs new file mode 100644 index 00000000..6357e47f --- /dev/null +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Components/Subcomponents/1_21/AttributeSubComponent121.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Core; + +namespace MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_21; + +public class AttributeSubComponent121(DataTypes dataTypes, SubComponentRegistry subComponentRegistry) : SubComponent(dataTypes, subComponentRegistry) +{ + public int TypeId { get; set; } + public string? ResourceLocation { get; set; } + public double Value { get; set; } + public int Operation { get; set; } + public int Slot { get; set; } + + protected override void Parse(Queue data) + { + TypeId = dataTypes.ReadNextVarInt(data); + ResourceLocation = dataTypes.ReadNextString(data); + Value = dataTypes.ReadNextDouble(data); + Operation = dataTypes.ReadNextVarInt(data); + Slot = dataTypes.ReadNextVarInt(data); + } + + public override Queue Serialize() + { + var data = new List(); + data.AddRange(DataTypes.GetVarInt(TypeId)); + + if (string.IsNullOrEmpty(ResourceLocation?.Trim())) + throw new ArgumentNullException($"Can not serialize AttributeSubComponent121 due to ResourceLocation being null or empty!"); + + data.AddRange(DataTypes.GetString(ResourceLocation)); + data.AddRange(DataTypes.GetDouble(Value)); + data.AddRange(DataTypes.GetVarInt(Operation)); + data.AddRange(DataTypes.GetVarInt(Slot)); + return new Queue(data); + } +} diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs index 90123fe6..49665709 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Core/SubComponentRegistry.cs @@ -16,6 +16,11 @@ public abstract class SubComponentRegistry(DataTypes dataTypes) _subComponentParsers.Add(name, typeof(T)); } + protected void ReplaceSubComponent(string name) where T : SubComponent + { + _subComponentParsers[name] = typeof(T); + } + public SubComponent ParseSubComponent(string name, Queue data) { if(!_subComponentParsers.TryGetValue(name, out var subComponentParserType)) diff --git a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs index 7515020f..2afd2852 100644 --- a/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs +++ b/MinecraftClient/Protocol/Handlers/StructuredComponents/Registries/Subcomponents/SubComponentRegistry121.cs @@ -10,6 +10,7 @@ public class SubComponentRegistry121 : SubComponentRegistry1206 { public SubComponentRegistry121(DataTypes dataTypes) : base(dataTypes) { + ReplaceSubComponent(SubComponents.Attribute); RegisterSubComponent(SubComponents.SoundEvent); } } \ No newline at end of file