fix: Explosion packet parsing and update attribute fallback for 1.21

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<SoundEvent> 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
This commit is contained in:
BruceChen 2026-03-20 01:29:18 +08:00
parent c23c229eb2
commit ee02974abe
2 changed files with 38 additions and 35 deletions

View file

@ -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<int, string>
{
{ 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" }
};
}

View file

@ -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<SoundEvent> 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;