fix: StructuredComponents batch 2 audit — BlockPredicate and PropertySubComponent serialization

Audited all 8 batch-2 components (enchantments, stored_enchantments,
can_place_on, can_break, lodestone_tracker, firework_explosion, fireworks,
banner_patterns, suspicious_stew_effects, bees) against official 1.20.6
decompiled STREAM_CODEC definitions.

Found and fixed 3 bugs in BlockPredicate/PropertySubComponent:

1. BlockPredicateSubcomponent.Serialize(): missing HasNbt bool write.
   Parse reads the bool but Serialize skipped writing it, causing all
   subsequent fields to be offset by one byte.

2. BlockPredicateSubcomponent.Serialize(): missing Properties list count
   VarInt write. Parse reads VarInt count before iterating, but Serialize
   only wrote the elements without the preceding count.

3. PropertySubComponent: RangedMatcher min/max values must use Optional
   encoding (Bool prefix + conditional String), matching the official
   ByteBufCodecs.either(ExactMatcher, RangedMatcher) where RangedMatcher
   uses ByteBufCodecs.optional(STRING_UTF8) for both min and max fields.
   Previously read/wrote plain Strings unconditionally.

Remaining 6 components (enchantments, stored_enchantments, lodestone_tracker,
firework_explosion, fireworks, banner_patterns, suspicious_stew_effects, bees)
verified correct — no changes needed.

Made-with: Cursor
This commit is contained in:
BruceChen 2026-03-20 00:19:08 +08:00
parent a36ba23ba6
commit 4944497f5a
2 changed files with 15 additions and 9 deletions

View file

@ -50,18 +50,20 @@ public class BlockPredicateSubcomponent(DataTypes dataTypes, SubComponentRegistr
data.AddRange(BlockSet.Serialize());
}
// Properites
// Properties
data.AddRange(DataTypes.GetBool(HasProperities));
if (HasProperities)
{
if(Properties == null || Properties.Count == 0)
throw new ArgumentNullException($"Can not serialize a BlockPredicate when the Properties is empty but HasProperties is true!");
data.AddRange(DataTypes.GetVarInt(Properties.Count));
foreach (var property in Properties)
data.AddRange(property.Serialize());
}
// NBT
data.AddRange(DataTypes.GetBool(HasNbt));
if (HasNbt)
{
if(Nbt == null)

View file

@ -18,11 +18,13 @@ public class PropertySubComponent(DataTypes dataTypes, SubComponentRegistry subC
IsExactMatch = dataTypes.ReadNextBool(data);
if (IsExactMatch)
ExactValue = dataTypes.ReadNextString(data);
else // Ranged Match
{
MinValue = dataTypes.ReadNextString(data);
MaxValue = dataTypes.ReadNextString(data);
ExactValue = dataTypes.ReadNextString(data);
}
else
{
MinValue = dataTypes.ReadNextBool(data) ? dataTypes.ReadNextString(data) : null;
MaxValue = dataTypes.ReadNextBool(data) ? dataTypes.ReadNextString(data) : null;
}
}
@ -45,11 +47,13 @@ public class PropertySubComponent(DataTypes dataTypes, SubComponentRegistry subC
}
else
{
if (string.IsNullOrEmpty(MinValue?.Trim()) || string.IsNullOrEmpty(MaxValue?.Trim()))
throw new ArgumentNullException($"Can not serialize a Property sub-component if the MinValue or MaxValue is null or empty when the type is not Exact Match!");
data.AddRange(DataTypes.GetBool(MinValue != null));
if (MinValue != null)
data.AddRange(DataTypes.GetString(MinValue));
data.AddRange(DataTypes.GetString(MinValue));
data.AddRange(DataTypes.GetString(MaxValue));
data.AddRange(DataTypes.GetBool(MaxValue != null));
if (MaxValue != null)
data.AddRange(DataTypes.GetString(MaxValue));
}
return new Queue<byte>(data);