Skip to main content

steel_registry/data_components/vanilla_components/
mod.rs

1//! Vanilla data component definitions and registration.
2//!
3//! This module defines all vanilla Minecraft data components and provides
4//! the registration function to add them to the registry.
5use simdnbt::FromNbtTag as _;
6use steel_utils::{Identifier, nbt::NbtNumeric as _};
7use text_components::TextComponent;
8
9use super::component_data::ComponentData;
10use super::registry::DataComponentRegistry;
11pub use super::registry::DataComponentType;
12pub use crate::attribute::AttributeModifierOperation;
13pub use crate::equipment::{EquipmentSlot, EquipmentSlotGroup};
14
15// Re-export component types for convenience
16pub use super::components::{
17    ArmorTrim, AttackRange, BannerPatternLayer, BannerPatternLayers, BeehiveOccupant, Bees,
18    BlockEntityData, BlockItemStateProperties, BlocksAttacks, BundleContents, ChargedProjectiles,
19    Consumable, CustomData, CustomModelData, DamageReduction, DamageResistant, DamageTypeComponent,
20    DeathProtection, DebugStickProperty, DebugStickState, DyedItemColor, Enchantable, EntityData,
21    Equippable, EquippableAllowedEntities, Filterable, FireworkExplosion, FireworkExplosionShape,
22    Fireworks, FoodProperties, GlobalPos, InstrumentComponent, InvalidEnchantableValue,
23    ItemAttributeModifierDisplay, ItemAttributeModifierEntry, ItemAttributeModifiers,
24    ItemContainerContents, ItemDamageFunction, ItemEnchantments, ItemLore, ItemLoreTooLong,
25    ItemUseAnimation, JukeboxPlayable, KineticWeapon, KineticWeaponCondition, LodestoneTracker,
26    MapDecorationEntry, MapDecorations, MapId, MapItemColor, MapPostProcessing,
27    OminousBottleAmplifier, PaintingVariantComponent, PiercingWeapon, PotDecorations,
28    PotionContents, ProvidesBannerPatterns, ProvidesTrimMaterial, Rarity, Recipes, Repairable,
29    SeededContainerLoot, SulfurCubeContent, SuspiciousStewEffect, SuspiciousStewEffects,
30    SwingAnimation, SwingAnimationType, Tool, ToolRule, ToolRuleBlocks, TooltipDisplay,
31    UseCooldown, UseEffects, UseRemainder, Weapon, WritableBookContent, WrittenBookContent,
32};
33pub use crate::ItemStackTemplate;
34pub use crate::cat_sound_variant::CatSoundVariant;
35pub use crate::cat_variant::CatVariant;
36pub use crate::chicken_sound_variant::ChickenSoundVariant;
37pub use crate::chicken_variant::ChickenVariant;
38pub use crate::cow_sound_variant::CowSoundVariant;
39pub use crate::cow_variant::CowVariant;
40pub use crate::frog_variant::FrogVariant;
41pub use crate::item_predicate::{AdventureModePredicate, BlockPredicate, ItemPredicate, LockCode};
42pub use crate::pig_sound_variant::PigSoundVariant;
43pub use crate::pig_variant::PigVariant;
44pub use crate::resolvable_profile::{
45    PartialProfile, PlayerModelType, PlayerSkinPatch, ProfileProperty, ResolvableProfile,
46    ResolvableProfileContents, StoredGameProfile,
47};
48pub use crate::sound_event::SoundEventHolder;
49pub use crate::villager_type::VillagerType;
50pub use crate::wolf_sound_variant::WolfSoundVariant;
51pub use crate::wolf_variant::WolfVariant;
52pub use crate::zombie_nautilus_variant::ZombieNautilusVariant;
53pub use crate::{
54    AxolotlVariant, DyeColor, FoxVariant, HorseVariant, LlamaVariant, MooshroomVariant,
55    ParrotVariant, RabbitVariant, RegistryReference, SalmonVariant, TropicalFishPattern,
56};
57
58pub const MAX_STACK_SIZE: DataComponentType<i32> =
59    DataComponentType::new(Identifier::vanilla_static("max_stack_size"));
60
61pub const MAX_DAMAGE: DataComponentType<i32> =
62    DataComponentType::new(Identifier::vanilla_static("max_damage"));
63
64pub const CUSTOM_NAME: DataComponentType<TextComponent> =
65    DataComponentType::new(Identifier::vanilla_static("custom_name"));
66
67pub const ITEM_NAME: DataComponentType<TextComponent> =
68    DataComponentType::new(Identifier::vanilla_static("item_name"));
69
70pub const DAMAGE: DataComponentType<i32> =
71    DataComponentType::new_ignoring_swap_animation(Identifier::vanilla_static("damage"));
72
73pub const REPAIR_COST: DataComponentType<i32> =
74    DataComponentType::new(Identifier::vanilla_static("repair_cost"));
75
76pub const UNBREAKABLE: DataComponentType<()> =
77    DataComponentType::new(Identifier::vanilla_static("unbreakable"));
78
79pub const TOOL: DataComponentType<Tool> =
80    DataComponentType::new(Identifier::vanilla_static("tool"));
81
82pub const WEAPON: DataComponentType<Weapon> =
83    DataComponentType::new(Identifier::vanilla_static("weapon"));
84
85pub const ATTACK_RANGE: DataComponentType<AttackRange> =
86    DataComponentType::new(Identifier::vanilla_static("attack_range"));
87
88pub const EQUIPPABLE: DataComponentType<Equippable> =
89    DataComponentType::new(Identifier::vanilla_static("equippable"));
90
91pub const GLIDER: DataComponentType<()> =
92    DataComponentType::new(Identifier::vanilla_static("glider"));
93
94pub const CREATIVE_SLOT_LOCK: DataComponentType<()> =
95    DataComponentType::new(Identifier::vanilla_static("creative_slot_lock"));
96
97pub const INTANGIBLE_PROJECTILE: DataComponentType<()> =
98    DataComponentType::new(Identifier::vanilla_static("intangible_projectile"));
99
100pub const ENCHANTMENT_GLINT_OVERRIDE: DataComponentType<bool> =
101    DataComponentType::new(Identifier::vanilla_static("enchantment_glint_override"));
102
103pub const POTION_DURATION_SCALE: DataComponentType<f32> =
104    DataComponentType::new(Identifier::vanilla_static("potion_duration_scale"));
105
106pub const CUSTOM_DATA: DataComponentType<CustomData> =
107    DataComponentType::new(Identifier::vanilla_static("custom_data"));
108
109pub const USE_EFFECTS: DataComponentType<UseEffects> =
110    DataComponentType::new(Identifier::vanilla_static("use_effects"));
111
112pub const MINIMUM_ATTACK_CHARGE: DataComponentType<f32> =
113    DataComponentType::new(Identifier::vanilla_static("minimum_attack_charge"));
114
115pub const DAMAGE_TYPE: DataComponentType<DamageTypeComponent> =
116    DataComponentType::new(Identifier::vanilla_static("damage_type"));
117
118pub const ITEM_MODEL: DataComponentType<Identifier> =
119    DataComponentType::new(Identifier::vanilla_static("item_model"));
120
121pub const LORE: DataComponentType<ItemLore> =
122    DataComponentType::new(Identifier::vanilla_static("lore"));
123
124pub const RARITY: DataComponentType<Rarity> =
125    DataComponentType::new(Identifier::vanilla_static("rarity"));
126
127pub const ENCHANTMENTS: DataComponentType<ItemEnchantments> =
128    DataComponentType::new(Identifier::vanilla_static("enchantments"));
129
130pub const CAN_PLACE_ON: DataComponentType<AdventureModePredicate> =
131    DataComponentType::new(Identifier::vanilla_static("can_place_on"));
132
133pub const CAN_BREAK: DataComponentType<AdventureModePredicate> =
134    DataComponentType::new(Identifier::vanilla_static("can_break"));
135
136pub const ATTRIBUTE_MODIFIERS: DataComponentType<ItemAttributeModifiers> =
137    DataComponentType::new(Identifier::vanilla_static("attribute_modifiers"));
138
139pub const CUSTOM_MODEL_DATA: DataComponentType<CustomModelData> =
140    DataComponentType::new(Identifier::vanilla_static("custom_model_data"));
141
142pub const TOOLTIP_DISPLAY: DataComponentType<TooltipDisplay> =
143    DataComponentType::new(Identifier::vanilla_static("tooltip_display"));
144
145pub const TOOLTIP_STYLE: DataComponentType<Identifier> =
146    DataComponentType::new(Identifier::vanilla_static("tooltip_style"));
147
148pub const NOTE_BLOCK_SOUND: DataComponentType<Identifier> =
149    DataComponentType::new(Identifier::vanilla_static("note_block_sound"));
150
151pub const FOOD: DataComponentType<FoodProperties> =
152    DataComponentType::new(Identifier::vanilla_static("food"));
153
154pub const CONSUMABLE: DataComponentType<Consumable> =
155    DataComponentType::new(Identifier::vanilla_static("consumable"));
156
157pub const USE_REMAINDER: DataComponentType<UseRemainder> =
158    DataComponentType::new(Identifier::vanilla_static("use_remainder"));
159
160pub const USE_COOLDOWN: DataComponentType<UseCooldown> =
161    DataComponentType::new(Identifier::vanilla_static("use_cooldown"));
162
163pub const DAMAGE_RESISTANT: DataComponentType<DamageResistant> =
164    DataComponentType::new(Identifier::vanilla_static("damage_resistant"));
165
166pub const ENCHANTABLE: DataComponentType<Enchantable> =
167    DataComponentType::new(Identifier::vanilla_static("enchantable"));
168
169pub const REPAIRABLE: DataComponentType<Repairable> =
170    DataComponentType::new(Identifier::vanilla_static("repairable"));
171
172pub const DEATH_PROTECTION: DataComponentType<DeathProtection> =
173    DataComponentType::new(Identifier::vanilla_static("death_protection"));
174
175pub const BLOCKS_ATTACKS: DataComponentType<BlocksAttacks> =
176    DataComponentType::new(Identifier::vanilla_static("blocks_attacks"));
177
178pub const PIERCING_WEAPON: DataComponentType<PiercingWeapon> =
179    DataComponentType::new(Identifier::vanilla_static("piercing_weapon"));
180
181pub const KINETIC_WEAPON: DataComponentType<KineticWeapon> =
182    DataComponentType::new(Identifier::vanilla_static("kinetic_weapon"));
183
184pub const SWING_ANIMATION: DataComponentType<SwingAnimation> =
185    DataComponentType::new(Identifier::vanilla_static("swing_animation"));
186
187pub const ADDITIONAL_TRADE_COST: DataComponentType<i32> =
188    DataComponentType::new(Identifier::vanilla_static("additional_trade_cost"));
189
190pub const STORED_ENCHANTMENTS: DataComponentType<ItemEnchantments> =
191    DataComponentType::new(Identifier::vanilla_static("stored_enchantments"));
192
193pub const DYE: DataComponentType<DyeColor> =
194    DataComponentType::new(Identifier::vanilla_static("dye"));
195
196pub const DYED_COLOR: DataComponentType<DyedItemColor> =
197    DataComponentType::new(Identifier::vanilla_static("dyed_color"));
198
199pub const MAP_COLOR: DataComponentType<MapItemColor> =
200    DataComponentType::new(Identifier::vanilla_static("map_color"));
201
202pub const MAP_ID: DataComponentType<MapId> =
203    DataComponentType::new(Identifier::vanilla_static("map_id"));
204
205pub const MAP_DECORATIONS: DataComponentType<MapDecorations> =
206    DataComponentType::new(Identifier::vanilla_static("map_decorations"));
207
208pub const MAP_POST_PROCESSING: DataComponentType<MapPostProcessing> =
209    DataComponentType::new(Identifier::vanilla_static("map_post_processing"));
210
211pub const CHARGED_PROJECTILES: DataComponentType<ChargedProjectiles> =
212    DataComponentType::new(Identifier::vanilla_static("charged_projectiles"));
213
214pub const BUNDLE_CONTENTS: DataComponentType<BundleContents> =
215    DataComponentType::new(Identifier::vanilla_static("bundle_contents"));
216
217pub const POTION_CONTENTS: DataComponentType<PotionContents> =
218    DataComponentType::new(Identifier::vanilla_static("potion_contents"));
219
220pub const SUSPICIOUS_STEW_EFFECTS: DataComponentType<SuspiciousStewEffects> =
221    DataComponentType::new(Identifier::vanilla_static("suspicious_stew_effects"));
222
223pub const WRITABLE_BOOK_CONTENT: DataComponentType<WritableBookContent> =
224    DataComponentType::new(Identifier::vanilla_static("writable_book_content"));
225
226pub const WRITTEN_BOOK_CONTENT: DataComponentType<WrittenBookContent> =
227    DataComponentType::new(Identifier::vanilla_static("written_book_content"));
228
229pub const TRIM: DataComponentType<ArmorTrim> =
230    DataComponentType::new(Identifier::vanilla_static("trim"));
231
232pub const DEBUG_STICK_STATE: DataComponentType<DebugStickState> =
233    DataComponentType::new(Identifier::vanilla_static("debug_stick_state"));
234
235pub const ENTITY_DATA: DataComponentType<EntityData> =
236    DataComponentType::new(Identifier::vanilla_static("entity_data"));
237
238pub const BUCKET_ENTITY_DATA: DataComponentType<CustomData> =
239    DataComponentType::new(Identifier::vanilla_static("bucket_entity_data"));
240
241pub const BLOCK_ENTITY_DATA: DataComponentType<BlockEntityData> =
242    DataComponentType::new(Identifier::vanilla_static("block_entity_data"));
243
244pub const INSTRUMENT: DataComponentType<InstrumentComponent> =
245    DataComponentType::new(Identifier::vanilla_static("instrument"));
246
247pub const PROVIDES_TRIM_MATERIAL: DataComponentType<ProvidesTrimMaterial> =
248    DataComponentType::new(Identifier::vanilla_static("provides_trim_material"));
249
250pub const OMINOUS_BOTTLE_AMPLIFIER: DataComponentType<OminousBottleAmplifier> =
251    DataComponentType::new(Identifier::vanilla_static("ominous_bottle_amplifier"));
252
253pub const JUKEBOX_PLAYABLE: DataComponentType<JukeboxPlayable> =
254    DataComponentType::new(Identifier::vanilla_static("jukebox_playable"));
255
256pub const PROVIDES_BANNER_PATTERNS: DataComponentType<ProvidesBannerPatterns> =
257    DataComponentType::new(Identifier::vanilla_static("provides_banner_patterns"));
258
259pub const RECIPES: DataComponentType<Recipes> =
260    DataComponentType::new(Identifier::vanilla_static("recipes"));
261
262pub const LODESTONE_TRACKER: DataComponentType<LodestoneTracker> =
263    DataComponentType::new(Identifier::vanilla_static("lodestone_tracker"));
264
265pub const FIREWORK_EXPLOSION: DataComponentType<FireworkExplosion> =
266    DataComponentType::new(Identifier::vanilla_static("firework_explosion"));
267
268pub const FIREWORKS: DataComponentType<Fireworks> =
269    DataComponentType::new(Identifier::vanilla_static("fireworks"));
270
271pub const PROFILE: DataComponentType<ResolvableProfile> =
272    DataComponentType::new(Identifier::vanilla_static("profile"));
273
274pub const BANNER_PATTERNS: DataComponentType<BannerPatternLayers> =
275    DataComponentType::new(Identifier::vanilla_static("banner_patterns"));
276
277pub const BASE_COLOR: DataComponentType<DyeColor> =
278    DataComponentType::new(Identifier::vanilla_static("base_color"));
279
280pub const POT_DECORATIONS: DataComponentType<PotDecorations> =
281    DataComponentType::new(Identifier::vanilla_static("pot_decorations"));
282
283pub const CONTAINER: DataComponentType<ItemContainerContents> =
284    DataComponentType::new(Identifier::vanilla_static("container"));
285
286pub const BLOCK_STATE: DataComponentType<BlockItemStateProperties> =
287    DataComponentType::new(Identifier::vanilla_static("block_state"));
288
289pub const BEES: DataComponentType<Bees> =
290    DataComponentType::new(Identifier::vanilla_static("bees"));
291
292pub const SULFUR_CUBE_CONTENT: DataComponentType<SulfurCubeContent> =
293    DataComponentType::new(Identifier::vanilla_static("sulfur_cube_content"));
294
295pub const LOCK: DataComponentType<LockCode> =
296    DataComponentType::new(Identifier::vanilla_static("lock"));
297
298pub const CONTAINER_LOOT: DataComponentType<SeededContainerLoot> =
299    DataComponentType::new(Identifier::vanilla_static("container_loot"));
300
301pub const BREAK_SOUND: DataComponentType<SoundEventHolder> =
302    DataComponentType::new(Identifier::vanilla_static("break_sound"));
303
304// Entity variant components
305pub const VILLAGER_VARIANT: DataComponentType<RegistryReference<VillagerType>> =
306    DataComponentType::new(Identifier::vanilla_static("villager/variant"));
307
308pub const WOLF_VARIANT: DataComponentType<RegistryReference<WolfVariant>> =
309    DataComponentType::new(Identifier::vanilla_static("wolf/variant"));
310
311pub const WOLF_SOUND_VARIANT: DataComponentType<RegistryReference<WolfSoundVariant>> =
312    DataComponentType::new(Identifier::vanilla_static("wolf/sound_variant"));
313
314pub const WOLF_COLLAR: DataComponentType<DyeColor> =
315    DataComponentType::new(Identifier::vanilla_static("wolf/collar"));
316
317pub const FOX_VARIANT: DataComponentType<FoxVariant> =
318    DataComponentType::new(Identifier::vanilla_static("fox/variant"));
319
320pub const SALMON_SIZE: DataComponentType<SalmonVariant> =
321    DataComponentType::new(Identifier::vanilla_static("salmon/size"));
322
323pub const PARROT_VARIANT: DataComponentType<ParrotVariant> =
324    DataComponentType::new(Identifier::vanilla_static("parrot/variant"));
325
326pub const TROPICAL_FISH_PATTERN: DataComponentType<TropicalFishPattern> =
327    DataComponentType::new(Identifier::vanilla_static("tropical_fish/pattern"));
328
329pub const TROPICAL_FISH_BASE_COLOR: DataComponentType<DyeColor> =
330    DataComponentType::new(Identifier::vanilla_static("tropical_fish/base_color"));
331
332pub const TROPICAL_FISH_PATTERN_COLOR: DataComponentType<DyeColor> =
333    DataComponentType::new(Identifier::vanilla_static("tropical_fish/pattern_color"));
334
335pub const MOOSHROOM_VARIANT: DataComponentType<MooshroomVariant> =
336    DataComponentType::new(Identifier::vanilla_static("mooshroom/variant"));
337
338pub const RABBIT_VARIANT: DataComponentType<RabbitVariant> =
339    DataComponentType::new(Identifier::vanilla_static("rabbit/variant"));
340
341pub const PIG_VARIANT: DataComponentType<RegistryReference<PigVariant>> =
342    DataComponentType::new(Identifier::vanilla_static("pig/variant"));
343
344pub const PIG_SOUND_VARIANT: DataComponentType<RegistryReference<PigSoundVariant>> =
345    DataComponentType::new(Identifier::vanilla_static("pig/sound_variant"));
346
347pub const COW_VARIANT: DataComponentType<RegistryReference<CowVariant>> =
348    DataComponentType::new(Identifier::vanilla_static("cow/variant"));
349
350pub const COW_SOUND_VARIANT: DataComponentType<RegistryReference<CowSoundVariant>> =
351    DataComponentType::new(Identifier::vanilla_static("cow/sound_variant"));
352
353pub const CHICKEN_VARIANT: DataComponentType<RegistryReference<ChickenVariant>> =
354    DataComponentType::new(Identifier::vanilla_static("chicken/variant"));
355
356pub const CHICKEN_SOUND_VARIANT: DataComponentType<RegistryReference<ChickenSoundVariant>> =
357    DataComponentType::new(Identifier::vanilla_static("chicken/sound_variant"));
358
359pub const ZOMBIE_NAUTILUS_VARIANT: DataComponentType<RegistryReference<ZombieNautilusVariant>> =
360    DataComponentType::new(Identifier::vanilla_static("zombie_nautilus/variant"));
361
362pub const FROG_VARIANT: DataComponentType<RegistryReference<FrogVariant>> =
363    DataComponentType::new(Identifier::vanilla_static("frog/variant"));
364
365pub const HORSE_VARIANT: DataComponentType<HorseVariant> =
366    DataComponentType::new(Identifier::vanilla_static("horse/variant"));
367
368pub const PAINTING_VARIANT: DataComponentType<PaintingVariantComponent> =
369    DataComponentType::new(Identifier::vanilla_static("painting/variant"));
370
371pub const LLAMA_VARIANT: DataComponentType<LlamaVariant> =
372    DataComponentType::new(Identifier::vanilla_static("llama/variant"));
373
374pub const AXOLOTL_VARIANT: DataComponentType<AxolotlVariant> =
375    DataComponentType::new(Identifier::vanilla_static("axolotl/variant"));
376
377pub const CAT_VARIANT: DataComponentType<RegistryReference<CatVariant>> =
378    DataComponentType::new(Identifier::vanilla_static("cat/variant"));
379
380pub const CAT_SOUND_VARIANT: DataComponentType<RegistryReference<CatSoundVariant>> =
381    DataComponentType::new(Identifier::vanilla_static("cat/sound_variant"));
382
383pub const CAT_COLLAR: DataComponentType<DyeColor> =
384    DataComponentType::new(Identifier::vanilla_static("cat/collar"));
385
386pub const SHEEP_COLOR: DataComponentType<DyeColor> =
387    DataComponentType::new(Identifier::vanilla_static("sheep/color"));
388
389pub const SHULKER_COLOR: DataComponentType<DyeColor> =
390    DataComponentType::new(Identifier::vanilla_static("shulker/color"));
391
392/// Network reader for VarInt-encoded i32 components.
393fn varint_reader(cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
394    use steel_utils::{codec::VarInt, serial::ReadFrom};
395    let value = VarInt::read(cursor)?;
396    Ok(ComponentData::new(value.0))
397}
398
399/// Network writer for VarInt-encoded i32 components.
400fn varint_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
401    use steel_utils::{codec::VarInt, serial::WriteTo};
402    if let Some(v) = data.downcast_ref::<i32>() {
403        VarInt(*v).write(writer)
404    } else {
405        Err(std::io::Error::other("Component type mismatch"))
406    }
407}
408
409fn float_reader(cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
410    use steel_utils::serial::ReadFrom;
411    Ok(ComponentData::new(f32::read(cursor)?))
412}
413
414fn float_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
415    use steel_utils::serial::WriteTo;
416    let Some(value) = data.downcast_ref::<f32>() else {
417        return Err(std::io::Error::other("Component type mismatch"));
418    };
419    value.write(writer)
420}
421
422fn bool_reader(cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
423    use steel_utils::serial::ReadFrom;
424    Ok(ComponentData::new(bool::read(cursor)?))
425}
426
427fn bool_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
428    use steel_utils::serial::WriteTo;
429    let Some(value) = data.downcast_ref::<bool>() else {
430        return Err(std::io::Error::other("Component type mismatch"));
431    };
432    value.write(writer)
433}
434
435fn text_component_network_reader(
436    cursor: &mut std::io::Cursor<&[u8]>,
437) -> std::io::Result<ComponentData> {
438    use steel_utils::serial::ReadFrom as _;
439    TextComponent::read(cursor).map(ComponentData::new)
440}
441
442fn text_component_network_writer(
443    data: &ComponentData,
444    writer: &mut Vec<u8>,
445) -> std::io::Result<()> {
446    use steel_utils::serial::WriteTo as _;
447    let Some(value) = data.downcast_ref::<TextComponent>() else {
448        return Err(std::io::Error::other("Component type mismatch"));
449    };
450    value.write(writer)
451}
452
453fn text_component_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
454    use simdnbt::FromNbtTag as _;
455    TextComponent::from_nbt_tag(tag).map(ComponentData::new)
456}
457
458fn text_component_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
459    let Some(value) = data.downcast_ref::<TextComponent>() else {
460        return Err(std::io::Error::other("Component type mismatch"));
461    };
462    Ok(value.to_codec_nbt())
463}
464
465fn custom_data_codec_reader(cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
466    CustomData::read_codec_network(cursor).map(ComponentData::new)
467}
468
469fn custom_data_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
470    use steel_utils::serial::WriteTo as _;
471    let Some(value) = data.downcast_ref::<CustomData>() else {
472        return Err(std::io::Error::other("Component type mismatch"));
473    };
474    value.write(writer)
475}
476
477#[expect(
478    clippy::unnecessary_wraps,
479    reason = "network reader function pointers return io::Result"
480)]
481fn unit_reader(_cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
482    Ok(ComponentData::new(()))
483}
484
485fn unit_writer(data: &ComponentData, _writer: &mut Vec<u8>) -> std::io::Result<()> {
486    if data.downcast_ref::<()>().is_some() {
487        Ok(())
488    } else {
489        Err(std::io::Error::other("Component type mismatch"))
490    }
491}
492
493fn codec_unit_network_reader(
494    cursor: &mut std::io::Cursor<&[u8]>,
495) -> std::io::Result<ComponentData> {
496    let tag = simdnbt::owned::read_tag(cursor)
497        .map_err(|error| std::io::Error::other(format!("Invalid NBT: {error:?}")))?;
498    tag.compound()
499        .map(|_| ComponentData::new(()))
500        .ok_or_else(|| std::io::Error::other("Unit codec network value is not a compound"))
501}
502
503fn codec_unit_network_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
504    unit_nbt_writer(data)?.write(writer);
505    Ok(())
506}
507
508fn ranged_i32_nbt_reader<const MIN: i32, const MAX: i32>(
509    tag: simdnbt::borrow::NbtTag,
510) -> Option<ComponentData> {
511    let value = tag.codec_i32()?;
512    (MIN..=MAX)
513        .contains(&value)
514        .then(|| ComponentData::new(value))
515}
516
517fn ranged_i32_nbt_writer<const MIN: i32, const MAX: i32>(
518    data: &ComponentData,
519) -> std::io::Result<simdnbt::owned::NbtTag> {
520    let Some(value) = data.downcast_ref::<i32>() else {
521        return Err(std::io::Error::other("Component type mismatch"));
522    };
523    if !(MIN..=MAX).contains(value) {
524        return Err(std::io::Error::other(format!(
525            "Value {value} outside of range [{MIN}:{MAX}]"
526        )));
527    }
528    Ok(simdnbt::owned::NbtTag::Int(*value))
529}
530
531fn minimum_attack_charge_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
532    let value = tag.codec_f32()?;
533    (value.is_finite() && !value.is_sign_negative() && value <= 1.0)
534        .then(|| ComponentData::new(value))
535}
536
537fn potion_duration_scale_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
538    let value = tag.codec_f32()?;
539    (value.is_finite() && !value.is_sign_negative()).then(|| ComponentData::new(value))
540}
541
542fn minimum_attack_charge_nbt_writer(
543    data: &ComponentData,
544) -> std::io::Result<simdnbt::owned::NbtTag> {
545    let Some(value) = data.downcast_ref::<f32>() else {
546        return Err(std::io::Error::other("Component type mismatch"));
547    };
548    if !value.is_finite() || value.is_sign_negative() || *value > 1.0 {
549        return Err(std::io::Error::other(format!(
550            "Value {value} outside of range [0:1]"
551        )));
552    }
553    Ok(simdnbt::owned::NbtTag::Float(*value))
554}
555
556fn potion_duration_scale_nbt_writer(
557    data: &ComponentData,
558) -> std::io::Result<simdnbt::owned::NbtTag> {
559    let Some(value) = data.downcast_ref::<f32>() else {
560        return Err(std::io::Error::other("Component type mismatch"));
561    };
562    if !value.is_finite() || value.is_sign_negative() {
563        return Err(std::io::Error::other(format!(
564            "Value {value} must be non-negative and finite"
565        )));
566    }
567    Ok(simdnbt::owned::NbtTag::Float(*value))
568}
569
570fn bool_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
571    tag.codec_bool().map(ComponentData::new)
572}
573
574fn bool_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
575    let Some(value) = data.downcast_ref::<bool>() else {
576        return Err(std::io::Error::other("Component type mismatch"));
577    };
578    Ok(simdnbt::owned::NbtTag::Byte(i8::from(*value)))
579}
580
581fn unit_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
582    tag.compound().map(|_| ComponentData::new(()))
583}
584
585fn unit_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
586    if data.downcast_ref::<()>().is_none() {
587        return Err(std::io::Error::other("Component type mismatch"));
588    }
589    Ok(simdnbt::owned::NbtTag::Compound(
590        simdnbt::owned::NbtCompound::new(),
591    ))
592}
593
594fn jukebox_playable_network_reader(
595    cursor: &mut std::io::Cursor<&[u8]>,
596) -> std::io::Result<ComponentData> {
597    use steel_utils::serial::ReadFrom;
598    JukeboxPlayable::read(cursor).map(ComponentData::new)
599}
600
601fn jukebox_playable_network_writer(
602    data: &ComponentData,
603    writer: &mut Vec<u8>,
604) -> std::io::Result<()> {
605    use steel_utils::serial::WriteTo;
606    let Some(value) = data.downcast_ref::<JukeboxPlayable>() else {
607        return Err(std::io::Error::other("Component type mismatch"));
608    };
609    value.write(writer)
610}
611
612fn jukebox_playable_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
613    JukeboxPlayable::from_persistent_nbt(tag).map(ComponentData::new)
614}
615
616fn jukebox_playable_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
617    let Some(value) = data.downcast_ref::<JukeboxPlayable>() else {
618        return Err(std::io::Error::other("Component type mismatch"));
619    };
620    value.to_persistent_nbt()
621}
622
623fn fireworks_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
624    Fireworks::from_nbt_tag(tag).map(ComponentData::new)
625}
626
627fn fireworks_network_reader(cursor: &mut std::io::Cursor<&[u8]>) -> std::io::Result<ComponentData> {
628    use steel_utils::serial::ReadFrom as _;
629    Fireworks::read(cursor).map(ComponentData::new)
630}
631
632fn fireworks_network_writer(data: &ComponentData, writer: &mut Vec<u8>) -> std::io::Result<()> {
633    use steel_utils::serial::WriteTo as _;
634    let Some(value) = data.downcast_ref::<Fireworks>() else {
635        return Err(std::io::Error::other("Component type mismatch"));
636    };
637    value.write(writer)
638}
639
640fn fireworks_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
641    let Some(value) = data.downcast_ref::<Fireworks>() else {
642        return Err(std::io::Error::other("Component type mismatch"));
643    };
644    value.try_to_persistent_nbt()
645}
646
647fn painting_variant_network_reader(
648    cursor: &mut std::io::Cursor<&[u8]>,
649) -> std::io::Result<ComponentData> {
650    use steel_utils::serial::ReadFrom as _;
651    PaintingVariantComponent::read(cursor).map(ComponentData::new)
652}
653
654fn painting_variant_network_writer(
655    data: &ComponentData,
656    writer: &mut Vec<u8>,
657) -> std::io::Result<()> {
658    use steel_utils::serial::WriteTo as _;
659    let Some(value) = data.downcast_ref::<PaintingVariantComponent>() else {
660        return Err(std::io::Error::other("Component type mismatch"));
661    };
662    value.write(writer)
663}
664
665fn painting_variant_nbt_reader(tag: simdnbt::borrow::NbtTag) -> Option<ComponentData> {
666    PaintingVariantComponent::from_nbt_tag(tag).map(ComponentData::new)
667}
668
669fn painting_variant_nbt_writer(data: &ComponentData) -> std::io::Result<simdnbt::owned::NbtTag> {
670    let Some(value) = data.downcast_ref::<PaintingVariantComponent>() else {
671        return Err(std::io::Error::other("Component type mismatch"));
672    };
673    value.try_to_persistent_nbt()
674}
675
676macro_rules! register_ranged_i32 {
677    ($registry:expr, $component:expr, $min:expr, $max:expr) => {
678        $registry.register_with_codecs(
679            $component,
680            varint_reader,
681            varint_writer,
682            ranged_i32_nbt_reader::<{ $min }, { $max }>,
683            ranged_i32_nbt_writer::<{ $min }, { $max }>,
684        );
685    };
686}
687
688macro_rules! register_stream_unit {
689    ($registry:expr, $component:expr) => {
690        $registry.register_with_codecs(
691            $component,
692            unit_reader,
693            unit_writer,
694            unit_nbt_reader,
695            unit_nbt_writer,
696        );
697    };
698}
699
700/// Registers all vanilla data components.
701///
702/// IMPORTANT: The registration order MUST match vanilla's DataComponents.java exactly,
703/// as the component's network ID is determined by its registration order.
704pub fn register_vanilla_data_components(registry: &mut DataComponentRegistry) {
705    // Order must match vanilla's DataComponents.java exactly!
706    // 0: custom_data
707    registry.register_custom_network(CUSTOM_DATA, custom_data_codec_reader, custom_data_writer);
708    // 1: max_stack_size
709    register_ranged_i32!(registry, MAX_STACK_SIZE, 1, 99);
710    // 2: max_damage
711    register_ranged_i32!(registry, MAX_DAMAGE, 1, i32::MAX);
712    // 3: damage
713    register_ranged_i32!(registry, DAMAGE, 0, i32::MAX);
714    // 4: unbreakable
715    register_stream_unit!(registry, UNBREAKABLE);
716    // 5: use_effects
717    registry.register(USE_EFFECTS);
718    // 6: custom_name
719    registry.register_with_codecs(
720        CUSTOM_NAME,
721        text_component_network_reader,
722        text_component_network_writer,
723        text_component_nbt_reader,
724        text_component_nbt_writer,
725    );
726    // 7: minimum_attack_charge
727    registry.register_with_codecs(
728        MINIMUM_ATTACK_CHARGE,
729        float_reader,
730        float_writer,
731        minimum_attack_charge_nbt_reader,
732        minimum_attack_charge_nbt_writer,
733    );
734    // 8: damage_type
735    registry.register(DAMAGE_TYPE);
736    // 9: item_name
737    registry.register_with_codecs(
738        ITEM_NAME,
739        text_component_network_reader,
740        text_component_network_writer,
741        text_component_nbt_reader,
742        text_component_nbt_writer,
743    );
744    // 10: item_model
745    registry.register(ITEM_MODEL);
746    // 11: lore
747    registry.register(LORE);
748    // 12: rarity
749    registry.register(RARITY);
750    // 13: enchantments
751    registry.register(ENCHANTMENTS);
752    // 14: can_place_on
753    registry.register(CAN_PLACE_ON);
754    // 15: can_break
755    registry.register(CAN_BREAK);
756    // 16: attribute_modifiers
757    registry.register(ATTRIBUTE_MODIFIERS);
758    // 17: custom_model_data
759    registry.register(CUSTOM_MODEL_DATA);
760    // 18: tooltip_display
761    registry.register(TOOLTIP_DISPLAY);
762    // 19: repair_cost
763    register_ranged_i32!(registry, REPAIR_COST, 0, i32::MAX);
764    // 20: creative_slot_lock
765    registry.register_transient(CREATIVE_SLOT_LOCK);
766    // 21: enchantment_glint_override
767    registry.register_with_codecs(
768        ENCHANTMENT_GLINT_OVERRIDE,
769        bool_reader,
770        bool_writer,
771        bool_nbt_reader,
772        bool_nbt_writer,
773    );
774    // 22: intangible_projectile
775    registry.register_with_codecs(
776        INTANGIBLE_PROJECTILE,
777        codec_unit_network_reader,
778        codec_unit_network_writer,
779        unit_nbt_reader,
780        unit_nbt_writer,
781    );
782    // 23: food
783    registry.register(FOOD);
784    // 24: consumable
785    registry.register(CONSUMABLE);
786    // 25: use_remainder
787    registry.register_validated(USE_REMAINDER);
788    // 26: use_cooldown
789    registry.register(USE_COOLDOWN);
790    // 27: damage_resistant
791    registry.register(DAMAGE_RESISTANT);
792    // 28: tool
793    registry.register(TOOL);
794    // 29: weapon
795    registry.register(WEAPON);
796    // 30: attack_range
797    registry.register(ATTACK_RANGE);
798    // 31: enchantable
799    registry.register(ENCHANTABLE);
800    // 32: equippable
801    registry.register(EQUIPPABLE);
802    // 33: repairable
803    registry.register(REPAIRABLE);
804    // 34: glider
805    register_stream_unit!(registry, GLIDER);
806    // 35: tooltip_style
807    registry.register(TOOLTIP_STYLE);
808    // 36: death_protection
809    registry.register(DEATH_PROTECTION);
810    // 37: blocks_attacks
811    registry.register(BLOCKS_ATTACKS);
812    // 38: piercing_weapon
813    registry.register(PIERCING_WEAPON);
814    // 39: kinetic_weapon
815    registry.register(KINETIC_WEAPON);
816    // 40: swing_animation
817    registry.register(SWING_ANIMATION);
818    // 41: additional_trade_cost
819    registry.register_transient_with_codecs(ADDITIONAL_TRADE_COST, varint_reader, varint_writer);
820    // 42: stored_enchantments
821    registry.register(STORED_ENCHANTMENTS);
822    // 43: dye
823    registry.register(DYE);
824    // 44: dyed_color
825    registry.register(DYED_COLOR);
826    // 45: map_color
827    registry.register(MAP_COLOR);
828    // 46: map_id
829    registry.register(MAP_ID);
830    // 47: map_decorations
831    registry.register(MAP_DECORATIONS);
832    // 48: map_post_processing
833    registry.register_transient(MAP_POST_PROCESSING);
834    // 49: charged_projectiles
835    registry.register_validated(CHARGED_PROJECTILES);
836    // 50: bundle_contents
837    registry.register_validated(BUNDLE_CONTENTS);
838    // 51: potion_contents
839    registry.register(POTION_CONTENTS);
840    // 52: potion_duration_scale
841    registry.register_with_codecs(
842        POTION_DURATION_SCALE,
843        float_reader,
844        float_writer,
845        potion_duration_scale_nbt_reader,
846        potion_duration_scale_nbt_writer,
847    );
848    // 53: suspicious_stew_effects
849    registry.register(SUSPICIOUS_STEW_EFFECTS);
850    // 54: writable_book_content
851    registry.register(WRITABLE_BOOK_CONTENT);
852    // 55: written_book_content
853    registry.register(WRITTEN_BOOK_CONTENT);
854    // 56: trim
855    registry.register(TRIM);
856    // 57: debug_stick_state
857    registry.register(DEBUG_STICK_STATE);
858    // 58: entity_data
859    registry.register(ENTITY_DATA);
860    // 59: bucket_entity_data
861    registry.register(BUCKET_ENTITY_DATA);
862    // 60: block_entity_data
863    registry.register(BLOCK_ENTITY_DATA);
864    // 61: instrument
865    registry.register(INSTRUMENT);
866    // 62: provides_trim_material
867    registry.register(PROVIDES_TRIM_MATERIAL);
868    // 63: ominous_bottle_amplifier
869    registry.register(OMINOUS_BOTTLE_AMPLIFIER);
870    // 64: jukebox_playable
871    registry.register_with_codecs(
872        JUKEBOX_PLAYABLE,
873        jukebox_playable_network_reader,
874        jukebox_playable_network_writer,
875        jukebox_playable_nbt_reader,
876        jukebox_playable_nbt_writer,
877    );
878    // 65: provides_banner_patterns
879    registry.register(PROVIDES_BANNER_PATTERNS);
880    // 66: recipes
881    registry.register(RECIPES);
882    // 67: lodestone_tracker
883    registry.register(LODESTONE_TRACKER);
884    // 68: firework_explosion
885    registry.register(FIREWORK_EXPLOSION);
886    // 69: fireworks
887    registry.register_with_codecs(
888        FIREWORKS,
889        fireworks_network_reader,
890        fireworks_network_writer,
891        fireworks_nbt_reader,
892        fireworks_nbt_writer,
893    );
894    // 70: profile
895    registry.register(PROFILE);
896    // 71: note_block_sound
897    registry.register(NOTE_BLOCK_SOUND);
898    // 72: banner_patterns
899    registry.register(BANNER_PATTERNS);
900    // 73: base_color
901    registry.register(BASE_COLOR);
902    // 74: pot_decorations
903    registry.register(POT_DECORATIONS);
904    // 75: container
905    registry.register_validated(CONTAINER);
906    // 76: block_state
907    registry.register(BLOCK_STATE);
908    // 77: bees
909    registry.register(BEES);
910    // 78: sulfur_cube_content
911    registry.register_validated(SULFUR_CUBE_CONTENT);
912    // 79: lock
913    registry.register(LOCK);
914    // 80: container_loot
915    registry.register(CONTAINER_LOOT);
916    // 81: break_sound
917    registry.register(BREAK_SOUND);
918    // 82: villager/variant
919    registry.register(VILLAGER_VARIANT);
920    // 83: wolf/variant
921    registry.register(WOLF_VARIANT);
922    // 84: wolf/sound_variant
923    registry.register(WOLF_SOUND_VARIANT);
924    // 85: wolf/collar
925    registry.register(WOLF_COLLAR);
926    // 86: fox/variant
927    registry.register(FOX_VARIANT);
928    // 87: salmon/size
929    registry.register(SALMON_SIZE);
930    // 88: parrot/variant
931    registry.register(PARROT_VARIANT);
932    // 89: tropical_fish/pattern
933    registry.register(TROPICAL_FISH_PATTERN);
934    // 90: tropical_fish/base_color
935    registry.register(TROPICAL_FISH_BASE_COLOR);
936    // 91: tropical_fish/pattern_color
937    registry.register(TROPICAL_FISH_PATTERN_COLOR);
938    // 92: mooshroom/variant
939    registry.register(MOOSHROOM_VARIANT);
940    // 93: rabbit/variant
941    registry.register(RABBIT_VARIANT);
942    // 94: pig/variant
943    registry.register(PIG_VARIANT);
944    // 95: pig/sound_variant
945    registry.register(PIG_SOUND_VARIANT);
946    // 96: cow/variant
947    registry.register(COW_VARIANT);
948    // 97: cow/sound_variant
949    registry.register(COW_SOUND_VARIANT);
950    // 98: chicken/variant
951    registry.register(CHICKEN_VARIANT);
952    // 99: chicken/sound_variant
953    registry.register(CHICKEN_SOUND_VARIANT);
954    // 100: zombie_nautilus/variant
955    registry.register(ZOMBIE_NAUTILUS_VARIANT);
956    // 101: frog/variant
957    registry.register(FROG_VARIANT);
958    // 102: horse/variant
959    registry.register(HORSE_VARIANT);
960    // 103: painting/variant
961    registry.register_with_codecs(
962        PAINTING_VARIANT,
963        painting_variant_network_reader,
964        painting_variant_network_writer,
965        painting_variant_nbt_reader,
966        painting_variant_nbt_writer,
967    );
968    // 104: llama/variant
969    registry.register(LLAMA_VARIANT);
970    // 105: axolotl/variant
971    registry.register(AXOLOTL_VARIANT);
972    // 106: cat/variant
973    registry.register(CAT_VARIANT);
974    // 107: cat/sound_variant
975    registry.register(CAT_SOUND_VARIANT);
976    // 108: cat/collar
977    registry.register(CAT_COLLAR);
978    // 109: sheep/color
979    registry.register(SHEEP_COLOR);
980    // 110: shulker/color
981    registry.register(SHULKER_COLOR);
982}
983
984#[cfg(test)]
985mod tests;