Skip to main content

steel_registry/data_components/
registry.rs

1//! Data component registry and storage types.
2//!
3//! This module provides:
4//! - [`DataComponentRegistry`] - Registry of all component types with their serialization functions
5//! - [`DataComponentMap`] - Storage for component values on items/entities
6//! - [`DataComponentPatch`] - Diff representation for network/storage
7//! - [`DataComponentType`] - Type-safe handle for accessing components
8
9use rustc_hash::FxHashMap;
10use simdnbt::{
11    FromNbtTag, ToNbtTag,
12    borrow::NbtTag as BorrowedNbtTag,
13    owned::{NbtCompound, NbtTag as OwnedNbtTag},
14};
15use std::{
16    fmt::Debug,
17    io::{Cursor, Result, Write},
18    marker::PhantomData,
19};
20
21use steel_utils::{
22    Identifier,
23    codec::VarInt,
24    serial::{ReadFrom, WriteTo},
25};
26
27use super::component_data::{Component, ComponentData, ComponentDataDiscriminant};
28use super::components::ItemEnchantments;
29use super::vanilla_components::{
30    ATTRIBUTE_MODIFIERS, BREAK_SOUND, ENCHANTMENTS, LORE, MAX_STACK_SIZE, RARITY, REPAIR_COST,
31    TOOLTIP_DISPLAY,
32};
33
34/// A typed handle for a data component.
35///
36/// This provides compile-time type safety when getting/setting components.
37/// The actual storage uses [`ComponentData`] for ABI stability.
38///
39/// # Example
40/// ```ignore
41/// pub const DAMAGE: DataComponentType<Damage> =
42///     DataComponentType::new(Identifier::vanilla_static("damage"));
43///
44/// // Type-safe access
45/// let damage: Option<Damage> = components.get(DAMAGE);
46/// components.set(DAMAGE, Damage(10));
47/// ```
48pub struct DataComponentType<T> {
49    pub key: Identifier,
50    _phantom: PhantomData<T>,
51}
52
53impl<T> Clone for DataComponentType<T> {
54    fn clone(&self) -> Self {
55        Self {
56            key: self.key.clone(),
57            _phantom: PhantomData,
58        }
59    }
60}
61
62impl<T> DataComponentType<T> {
63    #[must_use]
64    pub const fn new(key: Identifier) -> Self {
65        Self {
66            key,
67            _phantom: PhantomData,
68        }
69    }
70}
71
72/// Reader function for deserializing a component from network format.
73pub type NetworkReader = fn(&mut Cursor<&[u8]>) -> Result<ComponentData>;
74
75/// Writer function for serializing a component to network format.
76pub type NetworkWriter = fn(&ComponentData, &mut Vec<u8>) -> Result<()>;
77
78/// Reader function for deserializing a component from NBT format.
79pub type NbtReader = fn(BorrowedNbtTag) -> Option<ComponentData>;
80
81/// Writer function for serializing a component to NBT format.
82pub type NbtWriter = fn(&ComponentData) -> OwnedNbtTag;
83
84/// Metadata for a registered component type.
85///
86/// Contains the component's key and all serialization functions needed
87/// to read/write the component for network and persistent storage.
88pub struct ComponentEntry {
89    /// The component's identifier (e.g., "minecraft:damage")
90    pub key: Identifier,
91    /// Expected discriminant for this component type
92    pub expected_discriminant: ComponentDataDiscriminant,
93    /// Network protocol reader
94    pub network_reader: NetworkReader,
95    /// Network protocol writer
96    pub network_writer: NetworkWriter,
97    /// NBT storage reader
98    pub nbt_reader: NbtReader,
99    /// NBT storage writer
100    pub nbt_writer: NbtWriter,
101}
102
103impl ComponentEntry {
104    /// Creates a new component entry with all serialization functions.
105    #[must_use]
106    pub fn new(
107        key: Identifier,
108        expected_discriminant: ComponentDataDiscriminant,
109        network_reader: NetworkReader,
110        network_writer: NetworkWriter,
111        nbt_reader: NbtReader,
112        nbt_writer: NbtWriter,
113    ) -> Self {
114        Self {
115            key,
116            expected_discriminant,
117            network_reader,
118            network_writer,
119            nbt_reader,
120            nbt_writer,
121        }
122    }
123
124    /// Validates that a `ComponentData` value matches the expected type for this component.
125    ///
126    /// Returns `true` if the data is valid for this component type, `false` otherwise.
127    /// This prevents plugins from setting wrong types on vanilla components.
128    #[must_use]
129    pub fn validates(&self, data: &ComponentData) -> bool {
130        data.discriminant() == self.expected_discriminant
131    }
132}
133
134pub type ComponentEntryRef = &'static ComponentEntry;
135
136/// Registry of all data component types.
137///
138/// Stores metadata about each component type including how to serialize/deserialize
139/// them for network and persistent storage.
140pub struct DataComponentRegistry {
141    /// Component entries indexed by network ID
142    entries: Vec<ComponentEntryRef>,
143    /// Map from component key to network ID
144    by_key: FxHashMap<Identifier, usize>,
145    /// Whether registration is still allowed
146    allows_registering: bool,
147}
148
149impl Default for DataComponentRegistry {
150    fn default() -> Self {
151        Self::new()
152    }
153}
154
155impl DataComponentRegistry {
156    #[must_use]
157    pub fn new() -> Self {
158        Self {
159            entries: Vec::new(),
160            by_key: FxHashMap::default(),
161            allows_registering: true,
162        }
163    }
164
165    /// Registers a vanilla component type.
166    ///
167    /// The component type `T` must implement the necessary serialization traits.
168    /// This creates the appropriate reader/writer functions automatically.
169    pub fn register<T>(
170        &mut self,
171        component: DataComponentType<T>,
172        expected_discriminant: ComponentDataDiscriminant,
173    ) where
174        T: 'static + Component + WriteTo + ReadFrom + ToNbtTag + FromNbtTag,
175    {
176        assert!(
177            self.allows_registering,
178            "Cannot register data components after the registry has been frozen"
179        );
180
181        // Create reader/writer functions that handle the ComponentData conversion
182        fn make_network_reader<T>() -> NetworkReader
183        where
184            T: 'static + Component + ReadFrom,
185        {
186            |cursor| {
187                let value = T::read(cursor)?;
188                Ok(value.into_data())
189            }
190        }
191
192        fn make_network_writer<T>() -> NetworkWriter
193        where
194            T: 'static + Component + WriteTo,
195        {
196            |data, writer| {
197                if let Some(value) = T::from_data_ref(data) {
198                    value.write(writer)
199                } else {
200                    Err(std::io::Error::other("Component type mismatch"))
201                }
202            }
203        }
204
205        fn make_nbt_reader<T>() -> NbtReader
206        where
207            T: 'static + Component + FromNbtTag,
208        {
209            |tag| {
210                let value = T::from_nbt_tag(tag)?;
211                Some(value.into_data())
212            }
213        }
214
215        fn make_nbt_writer<T>() -> NbtWriter
216        where
217            T: 'static + Component + ToNbtTag + Clone,
218        {
219            |data| {
220                if let Some(value) = T::from_data_ref(data) {
221                    value.clone().to_nbt_tag()
222                } else {
223                    // Fallback: empty compound
224                    OwnedNbtTag::Compound(NbtCompound::new())
225                }
226            }
227        }
228
229        let entry = Box::leak(Box::new(ComponentEntry::new(
230            component.key.clone(),
231            expected_discriminant,
232            make_network_reader::<T>(),
233            make_network_writer::<T>(),
234            make_nbt_reader::<T>(),
235            make_nbt_writer::<T>(),
236        )));
237
238        let id = self.entries.len();
239        self.by_key.insert(component.key.clone(), id);
240        self.entries.push(entry);
241    }
242
243    /// Registers a component with custom network reader/writer functions.
244    ///
245    /// Use this when the default `WriteTo`/`ReadFrom` implementations don't match
246    /// the network encoding (e.g., VarInt-encoded i32 components).
247    /// NBT serialization still uses the type's `ToNbtTag`/`FromNbtTag` impls.
248    pub fn register_custom_network<T>(
249        &mut self,
250        component: DataComponentType<T>,
251        expected_discriminant: ComponentDataDiscriminant,
252        network_reader: NetworkReader,
253        network_writer: NetworkWriter,
254    ) where
255        T: 'static + Component + ToNbtTag + FromNbtTag,
256    {
257        assert!(
258            self.allows_registering,
259            "Cannot register data components after the registry has been frozen"
260        );
261
262        fn make_nbt_reader<T>() -> NbtReader
263        where
264            T: 'static + Component + FromNbtTag,
265        {
266            |tag| {
267                let value = T::from_nbt_tag(tag)?;
268                Some(value.into_data())
269            }
270        }
271
272        fn make_nbt_writer<T>() -> NbtWriter
273        where
274            T: 'static + Component + ToNbtTag + Clone,
275        {
276            |data| {
277                if let Some(value) = T::from_data_ref(data) {
278                    value.clone().to_nbt_tag()
279                } else {
280                    OwnedNbtTag::Compound(NbtCompound::new())
281                }
282            }
283        }
284
285        let entry = Box::leak(Box::new(ComponentEntry::new(
286            component.key.clone(),
287            expected_discriminant,
288            network_reader,
289            network_writer,
290            make_nbt_reader::<T>(),
291            make_nbt_writer::<T>(),
292        )));
293
294        let id = self.entries.len();
295        self.by_key.insert(component.key.clone(), id);
296        self.entries.push(entry);
297    }
298
299    /// Registers a dynamic/plugin component type.
300    ///
301    /// Plugin components use the `ComponentData::Other` variant and handle
302    /// their own serialization. The provided functions read/write raw bytes.
303    pub fn register_dynamic(
304        &mut self,
305        key: Identifier,
306        expected_discriminant: ComponentDataDiscriminant,
307        network_reader: NetworkReader,
308        network_writer: NetworkWriter,
309        nbt_reader: NbtReader,
310        nbt_writer: NbtWriter,
311    ) -> usize {
312        assert!(
313            self.allows_registering,
314            "Cannot register data components after the registry has been frozen"
315        );
316
317        let entry = Box::leak(Box::new(ComponentEntry::new(
318            key.clone(),
319            expected_discriminant,
320            network_reader,
321            network_writer,
322            nbt_reader,
323            nbt_writer,
324        )));
325
326        let id = self.entries.len();
327        self.by_key.insert(key, id);
328        self.entries.push(entry);
329        id
330    }
331
332    /// Gets the network ID for a component type.
333    #[must_use]
334    pub fn get_id<T>(&self, component: DataComponentType<T>) -> Option<usize> {
335        self.by_key.get(&component.key).copied()
336    }
337
338    /// Gets the component key by network ID.
339    #[must_use]
340    pub fn get_key_by_id(&self, id: usize) -> Option<&Identifier> {
341        self.entries.get(id).map(|e| &e.key)
342    }
343}
344
345crate::impl_registry!(
346    DataComponentRegistry,
347    ComponentEntry,
348    entries,
349    by_key,
350    data_components
351);
352
353/// Storage for component values.
354///
355/// Maps component keys to their values. Used on items to store their data components.
356#[derive(Debug, Clone)]
357pub struct DataComponentMap {
358    map: FxHashMap<Identifier, ComponentData>,
359}
360
361impl Default for DataComponentMap {
362    fn default() -> Self {
363        Self::new()
364    }
365}
366
367impl DataComponentMap {
368    #[must_use]
369    pub fn new() -> Self {
370        Self {
371            map: FxHashMap::default(),
372        }
373    }
374
375    /// Creates a map with common item components pre-populated.
376    #[must_use]
377    pub fn common_item_components() -> Self {
378        let mut map = FxHashMap::default();
379        map.insert(MAX_STACK_SIZE.key.clone(), ComponentData::I32(64));
380        map.insert(LORE.key.clone(), ComponentData::Todo);
381        map.insert(
382            ENCHANTMENTS.key.clone(),
383            ComponentData::Enchantments(ItemEnchantments::empty()),
384        );
385        map.insert(REPAIR_COST.key.clone(), ComponentData::I32(0));
386        map.insert(ATTRIBUTE_MODIFIERS.key.clone(), ComponentData::Todo);
387        map.insert(RARITY.key.clone(), ComponentData::Todo);
388        map.insert(BREAK_SOUND.key.clone(), ComponentData::Todo);
389        map.insert(TOOLTIP_DISPLAY.key.clone(), ComponentData::Todo);
390        Self { map }
391    }
392
393    /// Sets a component value (builder pattern).
394    #[must_use]
395    pub fn builder_set<T: Component>(
396        mut self,
397        component: DataComponentType<T>,
398        value: Option<T>,
399    ) -> Self {
400        self.set(component, value);
401        self
402    }
403
404    /// Sets a component value, or removes it if `None`.
405    pub fn set<T: Component>(&mut self, component: DataComponentType<T>, value: Option<T>) {
406        if let Some(v) = value {
407            self.map.insert(component.key.clone(), v.into_data());
408        } else {
409            self.map.remove(&component.key);
410        }
411    }
412
413    /// Gets a component value by type.
414    #[must_use]
415    pub fn get<T: Component>(&self, component: DataComponentType<T>) -> Option<T> {
416        let data = self.map.get(&component.key)?;
417        T::from_data(data.clone())
418    }
419
420    /// Gets a reference to a component value.
421    #[must_use]
422    pub fn get_ref<T: Component>(&self, component: DataComponentType<T>) -> Option<&T> {
423        let data = self.map.get(&component.key)?;
424        T::from_data_ref(data)
425    }
426
427    /// Checks if a component is present.
428    #[must_use]
429    pub fn has<T>(&self, component: DataComponentType<T>) -> bool {
430        self.map.contains_key(&component.key)
431    }
432
433    /// Returns the number of components.
434    #[must_use]
435    pub fn len(&self) -> usize {
436        self.map.len()
437    }
438
439    /// Returns true if empty.
440    #[must_use]
441    pub fn is_empty(&self) -> bool {
442        self.map.is_empty()
443    }
444
445    /// Iterates over component keys.
446    pub fn keys(&self) -> impl Iterator<Item = &Identifier> {
447        self.map.keys()
448    }
449
450    /// Gets raw component data by key (for plugin use).
451    #[must_use]
452    pub fn get_raw(&self, key: &Identifier) -> Option<&ComponentData> {
453        self.map.get(key)
454    }
455
456    /// Sets raw component data (for plugin use).
457    ///
458    /// Returns `true` if the data was set successfully, `false` if the data type
459    /// doesn't match the registered component type (validation failed).
460    ///
461    /// This prevents plugins from setting invalid types on vanilla components.
462    pub fn set_raw(&mut self, key: Identifier, data: ComponentData) -> bool {
463        use crate::{REGISTRY, RegistryExt};
464
465        // Validate against registry if this component is registered
466        if let Some(entry) = REGISTRY.data_components.by_key(&key)
467            && !entry.validates(&data)
468        {
469            return false;
470        }
471
472        self.map.insert(key, data);
473        true
474    }
475
476    /// Removes a component by key.
477    pub fn remove(&mut self, key: &Identifier) -> Option<ComponentData> {
478        self.map.remove(key)
479    }
480}
481
482/// Entry in a component patch.
483#[derive(Debug, Clone)]
484pub enum ComponentPatchEntry {
485    /// Component is set to this value
486    Set(ComponentData),
487    /// Component is explicitly removed
488    Removed,
489}
490
491impl PartialEq for ComponentPatchEntry {
492    fn eq(&self, other: &Self) -> bool {
493        match (self, other) {
494            (Self::Removed, Self::Removed) => true,
495            (Self::Set(a), Self::Set(b)) => a == b,
496            _ => false,
497        }
498    }
499}
500
501/// A patch representing modifications to a [`DataComponentMap`].
502///
503/// Stores differences from a prototype:
504/// - Components that are added or overridden (`Set`)
505/// - Components that are explicitly removed (`Removed`)
506#[derive(Debug, Default, Clone, PartialEq)]
507pub struct DataComponentPatch {
508    entries: FxHashMap<Identifier, ComponentPatchEntry>,
509}
510
511impl DataComponentPatch {
512    #[must_use]
513    pub fn new() -> Self {
514        Self {
515            entries: FxHashMap::default(),
516        }
517    }
518
519    #[must_use]
520    pub fn is_empty(&self) -> bool {
521        self.entries.is_empty()
522    }
523
524    #[must_use]
525    pub fn len(&self) -> usize {
526        self.entries.len()
527    }
528
529    /// Sets a component value in the patch.
530    pub fn set<T: Component>(&mut self, component: DataComponentType<T>, value: T) {
531        self.entries.insert(
532            component.key.clone(),
533            ComponentPatchEntry::Set(value.into_data()),
534        );
535    }
536
537    /// Sets raw component data (for plugin use).
538    ///
539    /// Returns `true` if the data was set successfully, `false` if the data type
540    /// doesn't match the registered component type (validation failed).
541    ///
542    /// This prevents plugins from setting invalid types on vanilla components.
543    pub fn set_raw(&mut self, key: Identifier, data: ComponentData) -> bool {
544        use crate::{REGISTRY, RegistryExt};
545
546        // Validate against registry if this component is registered
547        if let Some(entry) = REGISTRY.data_components.by_key(&key)
548            && !entry.validates(&data)
549        {
550            return false;
551        }
552
553        self.entries.insert(key, ComponentPatchEntry::Set(data));
554        true
555    }
556
557    /// Marks a component as removed.
558    pub fn remove<T>(&mut self, component: DataComponentType<T>) {
559        self.entries
560            .insert(component.key.clone(), ComponentPatchEntry::Removed);
561    }
562
563    /// Clears any patch entry for a component.
564    pub fn clear<T>(&mut self, component: DataComponentType<T>) {
565        self.entries.remove(&component.key);
566    }
567
568    /// Gets the patch entry for a key.
569    #[must_use]
570    pub fn get_entry(&self, key: &Identifier) -> Option<&ComponentPatchEntry> {
571        self.entries.get(key)
572    }
573
574    /// Checks if a component is marked as removed.
575    #[must_use]
576    pub fn is_removed(&self, key: &Identifier) -> bool {
577        matches!(self.entries.get(key), Some(ComponentPatchEntry::Removed))
578    }
579
580    /// Counts set entries.
581    #[must_use]
582    pub fn count_set(&self) -> usize {
583        self.entries
584            .values()
585            .filter(|e| matches!(e, ComponentPatchEntry::Set(_)))
586            .count()
587    }
588
589    /// Counts removed entries.
590    #[must_use]
591    pub fn count_removed(&self) -> usize {
592        self.entries
593            .values()
594            .filter(|e| matches!(e, ComponentPatchEntry::Removed))
595            .count()
596    }
597
598    /// Iterates over all entries.
599    pub fn iter(&self) -> impl Iterator<Item = (&Identifier, &ComponentPatchEntry)> {
600        self.entries.iter()
601    }
602
603    /// Iterates over removed component keys.
604    pub fn iter_removed(&self) -> impl Iterator<Item = &Identifier> {
605        self.entries.iter().filter_map(|(k, v)| {
606            if matches!(v, ComponentPatchEntry::Removed) {
607                Some(k)
608            } else {
609                None
610            }
611        })
612    }
613
614    /// Converts this component patch to NBT without consuming it.
615    #[must_use]
616    pub fn to_nbt_tag_ref(&self) -> OwnedNbtTag {
617        use crate::{REGISTRY, RegistryExt};
618
619        let mut compound = NbtCompound::new();
620
621        for (key, entry) in &self.entries {
622            match entry {
623                ComponentPatchEntry::Set(data) => {
624                    if let Some(entry) = REGISTRY.data_components.by_key(key) {
625                        let nbt = (entry.nbt_writer)(data);
626                        compound.insert(key.to_string(), nbt);
627                    }
628                }
629                ComponentPatchEntry::Removed => {
630                    compound.insert(format!("!{key}"), NbtCompound::new());
631                }
632            }
633        }
634
635        OwnedNbtTag::Compound(compound)
636    }
637}
638
639impl WriteTo for DataComponentPatch {
640    fn write(&self, writer: &mut impl Write) -> Result<()> {
641        use crate::{REGISTRY, RegistryExt};
642
643        let mut added: Vec<(&Identifier, &ComponentData)> = Vec::new();
644        let mut removed: Vec<&Identifier> = Vec::new();
645
646        for (key, entry) in &self.entries {
647            match entry {
648                ComponentPatchEntry::Set(data) => added.push((key, data)),
649                ComponentPatchEntry::Removed => removed.push(key),
650            }
651        }
652
653        VarInt(added.len() as i32).write(writer)?;
654        VarInt(removed.len() as i32).write(writer)?;
655
656        // Write added components
657        for (key, data) in added {
658            let id = REGISTRY
659                .data_components
660                .id_from_key(key)
661                .ok_or_else(|| std::io::Error::other(format!("Unknown component key: {key:?}")))?;
662
663            let entry = REGISTRY
664                .data_components
665                .by_id(id)
666                .ok_or_else(|| std::io::Error::other(format!("No entry for component id: {id}")))?;
667
668            VarInt(id as i32).write(writer)?;
669
670            let mut buf = Vec::new();
671            (entry.network_writer)(data, &mut buf)?;
672            writer.write_all(&buf)?;
673        }
674
675        // Write removed component IDs
676        for key in removed {
677            let id = REGISTRY
678                .data_components
679                .id_from_key(key)
680                .ok_or_else(|| std::io::Error::other(format!("Unknown component key: {key:?}")))?;
681            VarInt(id as i32).write(writer)?;
682        }
683
684        Ok(())
685    }
686}
687
688impl ReadFrom for DataComponentPatch {
689    fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
690        use crate::{REGISTRY, RegistryExt};
691
692        let added_count = VarInt::read(data)?.0 as usize;
693        let removed_count = VarInt::read(data)?.0 as usize;
694
695        log::info!("Reading DataComponentPatch: added={added_count}, removed={removed_count}");
696
697        let mut patch = Self::new();
698
699        // Read added components
700        for i in 0..added_count {
701            let pos_before = data.position();
702            let type_id = VarInt::read(data)?.0 as usize;
703
704            let key = REGISTRY
705                .data_components
706                .get_key_by_id(type_id)
707                .ok_or_else(|| {
708                    std::io::Error::other(format!("Unknown component type ID: {type_id}"))
709                })?
710                .clone();
711
712            log::info!("  [{i}] Reading component {key} (id={type_id}) at pos {pos_before}");
713
714            let entry = REGISTRY
715                .data_components
716                .by_id(type_id)
717                .ok_or_else(|| std::io::Error::other(format!("No entry for component: {key}")))?;
718
719            let component_data = (entry.network_reader)(data).map_err(|e| {
720                log::error!("    Failed to read component {key}: {e}");
721                e
722            })?;
723
724            let pos_after = data.position();
725            log::info!("    Read {} bytes for {key}", pos_after - pos_before);
726
727            patch
728                .entries
729                .insert(key, ComponentPatchEntry::Set(component_data));
730        }
731
732        // Read removed component IDs
733        for _ in 0..removed_count {
734            let type_id = VarInt::read(data)?.0 as usize;
735
736            let key = REGISTRY
737                .data_components
738                .get_key_by_id(type_id)
739                .ok_or_else(|| {
740                    std::io::Error::other(format!("Unknown component type ID: {type_id}"))
741                })?
742                .clone();
743
744            patch.entries.insert(key, ComponentPatchEntry::Removed);
745        }
746
747        Ok(patch)
748    }
749}
750
751impl DataComponentPatch {
752    /// Reads a patch where each component value is prefixed with a VarInt byte length.
753    ///
754    /// Vanilla uses this for untrusted client packets (e.g., creative mode slot)
755    /// via `DataComponentPatch.DELIMITED_STREAM_CODEC`.
756    pub fn read_delimited(data: &mut Cursor<&[u8]>) -> Result<Self> {
757        use crate::{REGISTRY, RegistryExt};
758        use std::io::Read;
759
760        let added_count = VarInt::read(data)?.0 as usize;
761        let removed_count = VarInt::read(data)?.0 as usize;
762
763        const MAX_COMPONENTS: usize = 65_536;
764        const MAX_COMPONENT_BYTES: usize = 2 * 1024 * 1024;
765
766        if added_count.saturating_add(removed_count) > MAX_COMPONENTS {
767            return Err(std::io::Error::other(format!(
768                "Component patch too large: {added_count} added + {removed_count} removed > {MAX_COMPONENTS}"
769            )));
770        }
771
772        let mut patch = Self::new();
773
774        for _ in 0..added_count {
775            let type_id = VarInt::read(data)?.0 as usize;
776            let byte_len = VarInt::read(data)?.0 as usize;
777
778            if byte_len > MAX_COMPONENT_BYTES {
779                return Err(std::io::Error::other(format!(
780                    "Component data too large: {byte_len} bytes > {MAX_COMPONENT_BYTES}"
781                )));
782            }
783
784            let key = REGISTRY
785                .data_components
786                .get_key_by_id(type_id)
787                .ok_or_else(|| {
788                    std::io::Error::other(format!("Unknown component type ID: {type_id}"))
789                })?
790                .clone();
791
792            let entry = REGISTRY.data_components.by_id(type_id);
793
794            // Read the component bytes into a sub-buffer
795            let mut buf = vec![0u8; byte_len];
796            data.read_exact(&mut buf)?;
797
798            if let Some(entry) = entry {
799                let mut sub_cursor = Cursor::new(buf.as_slice());
800                match (entry.network_reader)(&mut sub_cursor) {
801                    Ok(component_data) => {
802                        patch
803                            .entries
804                            .insert(key, ComponentPatchEntry::Set(component_data));
805                    }
806                    Err(e) => {
807                        log::warn!("Failed to read delimited component {key}: {e}");
808                    }
809                }
810            }
811        }
812
813        for _ in 0..removed_count {
814            let type_id = VarInt::read(data)?.0 as usize;
815            let key = REGISTRY
816                .data_components
817                .get_key_by_id(type_id)
818                .ok_or_else(|| {
819                    std::io::Error::other(format!("Unknown component type ID: {type_id}"))
820                })?
821                .clone();
822            patch.entries.insert(key, ComponentPatchEntry::Removed);
823        }
824
825        Ok(patch)
826    }
827}
828
829impl ToNbtTag for DataComponentPatch {
830    fn to_nbt_tag(self) -> OwnedNbtTag {
831        self.to_nbt_tag_ref()
832    }
833}
834
835impl FromNbtTag for DataComponentPatch {
836    fn from_nbt_tag(tag: BorrowedNbtTag) -> Option<Self> {
837        use crate::{REGISTRY, RegistryExt};
838
839        let compound = tag.compound()?;
840        let mut patch = Self::new();
841
842        for (key, value) in compound.iter() {
843            let key_str = key.to_str();
844
845            if let Some(stripped) = key_str.strip_prefix('!') {
846                // Removed component
847                if let Ok(id) = stripped.parse::<Identifier>() {
848                    patch.entries.insert(id, ComponentPatchEntry::Removed);
849                }
850            } else {
851                // Set component
852                if let Ok(id) = key_str.parse::<Identifier>()
853                    && let Some(entry) = REGISTRY.data_components.by_key(&id)
854                    && let Some(component_data) = (entry.nbt_reader)(value)
855                {
856                    patch
857                        .entries
858                        .insert(id, ComponentPatchEntry::Set(component_data));
859                }
860            }
861        }
862
863        Some(patch)
864    }
865}
866
867/// Attempts to extract a typed component from `ComponentData`.
868#[must_use]
869pub fn component_try_into<T: Component>(
870    data: &ComponentData,
871    _component: DataComponentType<T>,
872) -> Option<&T> {
873    T::from_data_ref(data)
874}