Add a nullable `Nbt` field to `MccInventorySnapshotSlot`,
`MccInventorySearchMatch`, and `MccItemStackSnapshot` so that callers
can access the full item metadata beyond just material and count.
For 1.20.6+ servers the field is a map of component name
(e.g. "minecraft:custom_data") to the serialized component object.
For legacy servers it is the raw NBT dictionary. The field is omitted
entirely for vanilla items that carry no extra data, so there is no
noise for plain items like Stone or Dirt.
Implementation:
- `StructuredComponent`: add `ComponentName` property (set by the
registry during parse) and mark both it and `TypeId` with
`[JsonIgnore]` so they are excluded from component serialization.
- `StructuredComponentRegistry.ParseComponent`: assign `ComponentName`
after instantiation.
- `MccGameCommon.BuildNbt`: new helper that serializes components by
runtime type (fixing the polymorphism issue with System.Text.Json)
keyed by component name, falling back to the legacy NBT dictionary.
- Snapshot and search query builders updated to call `BuildNbt`.
Convert remaining == null to is null and != null to is not null
across 17 files in CommandHandler/ArgumentType, StructuredComponents,
and DeclareCommands for idiomatic C# style.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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
In 1.20.6+, items use structured components instead of NBT for metadata.
Previously, ReadNextItemSlot parsed the components but never stored them
on the Item instance, leaving DisplayName/Lores/Damage/Enchantments all
empty. GetItemSlot also still used the pre-1.20.6 format (bool + VarInt +
byte + NBT), causing the server to reject any item operation packets.
Changes:
Item.cs:
- Add List<StructuredComponent>? Components field to hold the raw
component list for round-trip serialization
- DisplayName property: read from CustomNameComponent (with
ItemNameComponent as fallback) when Components is present
- Lores property: read from LoreNameComponent1206 when Components is
present
- Damage property: read from DamageComponent when Components is present
- Add EnchantmentList property: read from EnchantmentsComponent (covers
both normal and StoredEnchantmentsComponent for enchanted books)
- ToFullString(): use EnchantmentList with EnchantmentMapping for display
when available, fall back to NBT path for older versions
- Add CloneWithCount() method that preserves both NBT and Components
DataTypes.cs - ReadNextItemSlot:
- Assign parsed strcturedComponentsToAdd to item.Components
DataTypes.cs - GetItemSlot:
- Add 1.20.6+ branch: write VarInt(count) + VarInt(itemId) + component
counts + serialized components (using each component's TypeId and
Serialize() method)
- Empty slot sends VarInt(0) per the 1.20.6 protocol spec
StructuredComponent.cs:
- Add int TypeId property (default -1) to store the registry type ID
assigned during parsing, enabling round-trip serialization
StructuredComponentRegistry.cs:
- Set component.TypeId = id after instantiation in ParseComponent()
McClient.cs:
- Replace manual Item constructor calls (new Item(type, count, nbt))
with Item.CloneWithCount() to preserve Components during inventory
operations like slot moves, stack splits, and right-click placement
Made-with: Cursor