From ee02974abe37a54e91ac088ebab33b1470df1649 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Fri, 20 Mar 2026 01:29:18 +0800 Subject: [PATCH] fix: Explosion packet parsing and update attribute fallback for 1.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the Explosion packet handler that was truncating reads at the knockback fields, leaving BlockInteraction, particles, and SoundEvent bytes unconsumed for 1.20.4+. The old commented-out code had three bugs: conditional particle read (should always read both small and large), reading SoundEvent as a plain string (it's a Holder encoded as VarInt id + optional inline DIRECT_STREAM_CODEC), and an incorrect fixedRange version gate. Verified against decompiled ClientboundExplodePacket from both 1.20.6 and 1.21.1 — the wire format is identical across versions. Update LoadDefaultAttributes() fallback to match the 1.21.1 registry order (31 attributes), adding 9 new entries: burning_time, explosion_knockback_resistance, mining_efficiency, movement_efficiency, oxygen_bonus, sneaking_speed, submerged_mining_speed, sweeping_damage_ratio, and water_movement_efficiency. This fallback is only used when the server omits the attribute RegistryData packet. Made-with: Cursor --- MinecraftClient/Mapping/World.cs | 42 ++++++++++++------- .../Protocol/Handlers/Protocol18.cs | 31 +++++--------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index c0999761..0a83ff97 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -257,6 +257,9 @@ namespace MinecraftClient.Mapping private static void LoadDefaultAttributes() { + // Fallback for when the server doesn't send attribute registry via RegistryData. + // Matches 1.21.1 Attributes.java registration order. + // For 1.20.6+ servers, SetAttributeIdMap() overrides this with the actual registry. attributeIdMap = new Dictionary { { 0, "generic.armor" }, @@ -266,21 +269,30 @@ namespace MinecraftClient.Mapping { 4, "generic.attack_speed" }, { 5, "player.block_break_speed" }, { 6, "player.block_interaction_range" }, - { 7, "player.entity_interaction_range" }, - { 8, "generic.fall_damage_multiplier" }, - { 9, "generic.flying_speed" }, - { 10, "generic.follow_range" }, - { 11, "generic.gravity" }, - { 12, "generic.jump_strength" }, - { 13, "generic.knockback_resistance" }, - { 14, "generic.luck" }, - { 15, "generic.max_absorption" }, - { 16, "generic.max_health" }, - { 17, "generic.movement_speed" }, - { 18, "generic.safe_fall_distance" }, - { 19, "generic.scale" }, - { 20, "zombie.spawn_reinforcements" }, - { 21, "generic.step_height" } + { 7, "generic.burning_time" }, + { 8, "generic.explosion_knockback_resistance" }, + { 9, "player.entity_interaction_range" }, + { 10, "generic.fall_damage_multiplier" }, + { 11, "generic.flying_speed" }, + { 12, "generic.follow_range" }, + { 13, "generic.gravity" }, + { 14, "generic.jump_strength" }, + { 15, "generic.knockback_resistance" }, + { 16, "generic.luck" }, + { 17, "generic.max_absorption" }, + { 18, "generic.max_health" }, + { 19, "player.mining_efficiency" }, + { 20, "generic.movement_efficiency" }, + { 21, "generic.movement_speed" }, + { 22, "generic.oxygen_bonus" }, + { 23, "generic.safe_fall_distance" }, + { 24, "generic.scale" }, + { 25, "player.sneaking_speed" }, + { 26, "zombie.spawn_reinforcements" }, + { 27, "generic.step_height" }, + { 28, "player.submerged_mining_speed" }, + { 29, "player.sweeping_damage_ratio" }, + { 30, "generic.water_movement_efficiency" } }; } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 77dc845c..735ce498 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -2687,39 +2687,30 @@ namespace MinecraftClient.Protocol.Handlers for (var i = 0; i < explosionBlockCount; i++) dataTypes.ReadNextByteArray(packetData, 3); - // Maybe use in the future when the physics are implemented dataTypes.ReadNextFloat(packetData); // Player Motion X dataTypes.ReadNextFloat(packetData); // Player Motion Y dataTypes.ReadNextFloat(packetData); // Player Motion Z - // Cut off here, there is an issue, the code bllow crashes on sound name reading - // I am unable to figure out what part of the code is reading more bytes than it should - // TODO: Fix - handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); - break; - - /*if (protocolVersion >= MC_1_20_4_Version) + if (protocolVersion >= MC_1_20_4_Version) { - var blockInteraction = dataTypes.ReadNextVarInt(packetData); // Block Interaction - - if(explosionStrength >= 2.0 || blockInteraction != 0) - dataTypes.ReadParticleData(packetData, itemPalette); // Large Explosion Particles - else - dataTypes.ReadParticleData(packetData, itemPalette); // Small Explosion Particles + dataTypes.ReadNextVarInt(packetData); // Block Interaction (enum ordinal) + dataTypes.ReadParticleData(packetData, itemPalette); // Small Explosion Particles + dataTypes.ReadParticleData(packetData, itemPalette); // Large Explosion Particles - // Explosion Sound - dataTypes.ReadNextString(packetData); // Sound Name - - if (protocolVersion < MC_1_21_Version) + // Explosion Sound: Holder via ByteBufCodecs.holder() + // VarInt id: 0 = inline (read DIRECT_STREAM_CODEC), >0 = registry ref (id-1) + var soundHolderId = dataTypes.ReadNextVarInt(packetData); + if (soundHolderId == 0) { + dataTypes.ReadNextString(packetData); // Sound ResourceLocation var hasFixedRange = dataTypes.ReadNextBool(packetData); if (hasFixedRange) - dataTypes.ReadNextFloat(packetData); // Range + dataTypes.ReadNextFloat(packetData); // Fixed range } } handler.OnExplosion(explosionLocation, explosionStrength, explosionBlockCount); - break;*/ + break; case PacketTypesIn.HeldItemChange: handler.OnHeldItemChange(dataTypes.ReadNextByte(packetData)); // Slot break;