bevy_ecs/component/
info.rs

1use alloc::{borrow::Cow, vec::Vec};
2use bevy_platform::{hash::FixedHasher, sync::PoisonError};
3use bevy_ptr::OwningPtr;
4#[cfg(feature = "bevy_reflect")]
5use bevy_reflect::Reflect;
6use bevy_utils::{prelude::DebugName, TypeIdMap};
7use core::{
8    alloc::Layout,
9    any::{Any, TypeId},
10    fmt::Debug,
11    mem::needs_drop,
12};
13use indexmap::IndexSet;
14
15use crate::{
16    archetype::ArchetypeFlags,
17    component::{
18        Component, ComponentCloneBehavior, ComponentMutability, QueuedComponents,
19        RequiredComponents, StorageType,
20    },
21    lifecycle::ComponentHooks,
22    query::DebugCheckedUnwrap as _,
23    resource::Resource,
24    storage::SparseSetIndex,
25};
26
27/// Stores metadata for a type of component or resource stored in a specific [`World`](crate::world::World).
28#[derive(Debug, Clone)]
29pub struct ComponentInfo {
30    pub(super) id: ComponentId,
31    pub(super) descriptor: ComponentDescriptor,
32    pub(super) hooks: ComponentHooks,
33    pub(super) required_components: RequiredComponents,
34    /// The set of components that require this components.
35    /// Invariant: components in this set always appear after the components that they require.
36    pub(super) required_by: IndexSet<ComponentId, FixedHasher>,
37}
38
39impl ComponentInfo {
40    /// Returns a value uniquely identifying the current component.
41    #[inline]
42    pub fn id(&self) -> ComponentId {
43        self.id
44    }
45
46    /// Returns the name of the current component.
47    #[inline]
48    pub fn name(&self) -> DebugName {
49        self.descriptor.name.clone()
50    }
51
52    /// Returns `true` if the current component is mutable.
53    #[inline]
54    pub fn mutable(&self) -> bool {
55        self.descriptor.mutable
56    }
57
58    /// Returns [`ComponentCloneBehavior`] of the current component.
59    #[inline]
60    pub fn clone_behavior(&self) -> &ComponentCloneBehavior {
61        &self.descriptor.clone_behavior
62    }
63
64    /// Returns the [`TypeId`] of the underlying component type.
65    /// Returns `None` if the component does not correspond to a Rust type.
66    #[inline]
67    pub fn type_id(&self) -> Option<TypeId> {
68        self.descriptor.type_id
69    }
70
71    /// Returns the layout used to store values of this component in memory.
72    #[inline]
73    pub fn layout(&self) -> Layout {
74        self.descriptor.layout
75    }
76
77    #[inline]
78    /// Get the function which should be called to clean up values of
79    /// the underlying component type. This maps to the
80    /// [`Drop`] implementation for 'normal' Rust components
81    ///
82    /// Returns `None` if values of the underlying component type don't
83    /// need to be dropped, e.g. as reported by [`needs_drop`].
84    pub fn drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
85        self.descriptor.drop
86    }
87
88    /// Returns a value indicating the storage strategy for the current component.
89    #[inline]
90    pub fn storage_type(&self) -> StorageType {
91        self.descriptor.storage_type
92    }
93
94    /// Returns `true` if the underlying component type can be freely shared between threads.
95    /// If this returns `false`, then extra care must be taken to ensure that components
96    /// are not accessed from the wrong thread.
97    #[inline]
98    pub fn is_send_and_sync(&self) -> bool {
99        self.descriptor.is_send_and_sync
100    }
101
102    /// Create a new [`ComponentInfo`].
103    pub(crate) fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
104        ComponentInfo {
105            id,
106            descriptor,
107            hooks: Default::default(),
108            required_components: Default::default(),
109            required_by: Default::default(),
110        }
111    }
112
113    /// Update the given flags to include any [`ComponentHook`](crate::component::ComponentHook) registered to self
114    #[inline]
115    pub(crate) fn update_archetype_flags(&self, flags: &mut ArchetypeFlags) {
116        if self.hooks().on_add.is_some() {
117            flags.insert(ArchetypeFlags::ON_ADD_HOOK);
118        }
119        if self.hooks().on_insert.is_some() {
120            flags.insert(ArchetypeFlags::ON_INSERT_HOOK);
121        }
122        if self.hooks().on_replace.is_some() {
123            flags.insert(ArchetypeFlags::ON_REPLACE_HOOK);
124        }
125        if self.hooks().on_remove.is_some() {
126            flags.insert(ArchetypeFlags::ON_REMOVE_HOOK);
127        }
128        if self.hooks().on_despawn.is_some() {
129            flags.insert(ArchetypeFlags::ON_DESPAWN_HOOK);
130        }
131    }
132
133    /// Provides a reference to the collection of hooks associated with this [`Component`]
134    pub fn hooks(&self) -> &ComponentHooks {
135        &self.hooks
136    }
137
138    /// Retrieves the [`RequiredComponents`] collection, which contains all required components (and their constructors)
139    /// needed by this component. This includes _recursive_ required components.
140    pub fn required_components(&self) -> &RequiredComponents {
141        &self.required_components
142    }
143}
144
145/// A value which uniquely identifies the type of a [`Component`] or [`Resource`] within a
146/// [`World`](crate::world::World).
147///
148/// Each time a new `Component` type is registered within a `World` using
149/// e.g. [`World::register_component`](crate::world::World::register_component) or
150/// [`World::register_component_with_descriptor`](crate::world::World::register_component_with_descriptor)
151/// or a Resource with e.g. [`World::init_resource`](crate::world::World::init_resource),
152/// a corresponding `ComponentId` is created to track it.
153///
154/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them
155/// into two separate but related concepts allows components to exist outside of Rust's type system.
156/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional
157/// `ComponentId`s may exist in a `World` to track components which cannot be
158/// represented as Rust types for scripting or other advanced use-cases.
159///
160/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from
161/// one `World` to access the metadata of a `Component` in a different `World` is undefined behavior
162/// and must not be attempted.
163///
164/// Given a type `T` which implements [`Component`], the `ComponentId` for `T` can be retrieved
165/// from a `World` using [`World::component_id()`](crate::world::World::component_id) or via [`Components::component_id()`].
166/// Access to the `ComponentId` for a [`Resource`] is available via [`Components::resource_id()`].
167#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
168#[cfg_attr(
169    feature = "bevy_reflect",
170    derive(Reflect),
171    reflect(Debug, Hash, PartialEq, Clone)
172)]
173pub struct ComponentId(pub(super) usize);
174
175impl ComponentId {
176    /// Creates a new [`ComponentId`].
177    ///
178    /// The `index` is a unique value associated with each type of component in a given world.
179    /// Usually, this value is taken from a counter incremented for each type of component registered with the world.
180    #[inline]
181    pub const fn new(index: usize) -> ComponentId {
182        ComponentId(index)
183    }
184
185    /// Returns the index of the current component.
186    #[inline]
187    pub fn index(self) -> usize {
188        self.0
189    }
190}
191
192impl SparseSetIndex for ComponentId {
193    #[inline]
194    fn sparse_set_index(&self) -> usize {
195        self.index()
196    }
197
198    #[inline]
199    fn get_sparse_set_index(value: usize) -> Self {
200        Self(value)
201    }
202}
203
204/// A value describing a component or resource, which may or may not correspond to a Rust type.
205#[derive(Clone)]
206pub struct ComponentDescriptor {
207    name: DebugName,
208    // SAFETY: This must remain private. It must match the statically known StorageType of the
209    // associated rust component type if one exists.
210    storage_type: StorageType,
211    // SAFETY: This must remain private. It must only be set to "true" if this component is
212    // actually Send + Sync
213    is_send_and_sync: bool,
214    type_id: Option<TypeId>,
215    layout: Layout,
216    // SAFETY: this function must be safe to call with pointers pointing to items of the type
217    // this descriptor describes.
218    // None if the underlying type doesn't need to be dropped
219    drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
220    mutable: bool,
221    clone_behavior: ComponentCloneBehavior,
222}
223
224// We need to ignore the `drop` field in our `Debug` impl
225impl Debug for ComponentDescriptor {
226    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
227        f.debug_struct("ComponentDescriptor")
228            .field("name", &self.name)
229            .field("storage_type", &self.storage_type)
230            .field("is_send_and_sync", &self.is_send_and_sync)
231            .field("type_id", &self.type_id)
232            .field("layout", &self.layout)
233            .field("mutable", &self.mutable)
234            .field("clone_behavior", &self.clone_behavior)
235            .finish()
236    }
237}
238
239impl ComponentDescriptor {
240    /// # Safety
241    ///
242    /// `x` must point to a valid value of type `T`.
243    unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
244        // SAFETY: Contract is required to be upheld by the caller.
245        unsafe {
246            x.drop_as::<T>();
247        }
248    }
249
250    /// Create a new `ComponentDescriptor` for the type `T`.
251    pub fn new<T: Component>() -> Self {
252        Self {
253            name: DebugName::type_name::<T>(),
254            storage_type: T::STORAGE_TYPE,
255            is_send_and_sync: true,
256            type_id: Some(TypeId::of::<T>()),
257            layout: Layout::new::<T>(),
258            drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
259            mutable: T::Mutability::MUTABLE,
260            clone_behavior: T::clone_behavior(),
261        }
262    }
263
264    /// Create a new `ComponentDescriptor`.
265    ///
266    /// # Safety
267    /// - the `drop` fn must be usable on a pointer with a value of the layout `layout`
268    /// - the component type must be safe to access from any thread (Send + Sync in rust terms)
269    pub unsafe fn new_with_layout(
270        name: impl Into<Cow<'static, str>>,
271        storage_type: StorageType,
272        layout: Layout,
273        drop: Option<for<'a> unsafe fn(OwningPtr<'a>)>,
274        mutable: bool,
275        clone_behavior: ComponentCloneBehavior,
276    ) -> Self {
277        Self {
278            name: name.into().into(),
279            storage_type,
280            is_send_and_sync: true,
281            type_id: None,
282            layout,
283            drop,
284            mutable,
285            clone_behavior,
286        }
287    }
288
289    /// Create a new `ComponentDescriptor` for a resource.
290    ///
291    /// The [`StorageType`] for resources is always [`StorageType::Table`].
292    pub fn new_resource<T: Resource>() -> Self {
293        Self {
294            name: DebugName::type_name::<T>(),
295            // PERF: `SparseStorage` may actually be a more
296            // reasonable choice as `storage_type` for resources.
297            storage_type: StorageType::Table,
298            is_send_and_sync: true,
299            type_id: Some(TypeId::of::<T>()),
300            layout: Layout::new::<T>(),
301            drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
302            mutable: true,
303            clone_behavior: ComponentCloneBehavior::Default,
304        }
305    }
306
307    pub(super) fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
308        Self {
309            name: DebugName::type_name::<T>(),
310            storage_type,
311            is_send_and_sync: false,
312            type_id: Some(TypeId::of::<T>()),
313            layout: Layout::new::<T>(),
314            drop: needs_drop::<T>().then_some(Self::drop_ptr::<T> as _),
315            mutable: true,
316            clone_behavior: ComponentCloneBehavior::Default,
317        }
318    }
319
320    /// Returns a value indicating the storage strategy for the current component.
321    #[inline]
322    pub fn storage_type(&self) -> StorageType {
323        self.storage_type
324    }
325
326    /// Returns the [`TypeId`] of the underlying component type.
327    /// Returns `None` if the component does not correspond to a Rust type.
328    #[inline]
329    pub fn type_id(&self) -> Option<TypeId> {
330        self.type_id
331    }
332
333    /// Returns the name of the current component.
334    #[inline]
335    pub fn name(&self) -> DebugName {
336        self.name.clone()
337    }
338
339    /// Returns whether this component is mutable.
340    #[inline]
341    pub fn mutable(&self) -> bool {
342        self.mutable
343    }
344}
345
346/// Stores metadata associated with each kind of [`Component`] in a given [`World`](crate::world::World).
347#[derive(Debug, Default)]
348pub struct Components {
349    pub(super) components: Vec<Option<ComponentInfo>>,
350    pub(super) indices: TypeIdMap<ComponentId>,
351    pub(super) resource_indices: TypeIdMap<ComponentId>,
352    // This is kept internal and local to verify that no deadlocks can occor.
353    pub(super) queued: bevy_platform::sync::RwLock<QueuedComponents>,
354}
355
356impl Components {
357    /// This registers any descriptor, component or resource.
358    ///
359    /// # Safety
360    ///
361    /// The id must have never been registered before. This must be a fresh registration.
362    #[inline]
363    pub(super) unsafe fn register_component_inner(
364        &mut self,
365        id: ComponentId,
366        descriptor: ComponentDescriptor,
367    ) {
368        let info = ComponentInfo::new(id, descriptor);
369        let least_len = id.0 + 1;
370        if self.components.len() < least_len {
371            self.components.resize_with(least_len, || None);
372        }
373        // SAFETY: We just extended the vec to make this index valid.
374        let slot = unsafe { self.components.get_mut(id.0).debug_checked_unwrap() };
375        // Caller ensures id is unique
376        debug_assert!(slot.is_none());
377        *slot = Some(info);
378    }
379
380    /// Returns the number of components registered or queued with this instance.
381    #[inline]
382    pub fn len(&self) -> usize {
383        self.num_queued() + self.num_registered()
384    }
385
386    /// Returns `true` if there are no components registered or queued with this instance. Otherwise, this returns `false`.
387    #[inline]
388    pub fn is_empty(&self) -> bool {
389        self.len() == 0
390    }
391
392    /// Returns the number of components registered with this instance.
393    #[inline]
394    pub fn num_queued(&self) -> usize {
395        let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
396        queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len()
397    }
398
399    /// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`.
400    #[inline]
401    pub fn any_queued(&self) -> bool {
402        self.num_queued() > 0
403    }
404
405    /// A faster version of [`Self::num_queued`].
406    #[inline]
407    pub fn num_queued_mut(&mut self) -> usize {
408        let queued = self
409            .queued
410            .get_mut()
411            .unwrap_or_else(PoisonError::into_inner);
412        queued.components.len() + queued.dynamic_registrations.len() + queued.resources.len()
413    }
414
415    /// A faster version of [`Self::any_queued`].
416    #[inline]
417    pub fn any_queued_mut(&mut self) -> bool {
418        self.num_queued_mut() > 0
419    }
420
421    /// Returns the number of components registered with this instance.
422    #[inline]
423    pub fn num_registered(&self) -> usize {
424        self.components.len()
425    }
426
427    /// Returns `true` if there are any components registered with this instance. Otherwise, this returns `false`.
428    #[inline]
429    pub fn any_registered(&self) -> bool {
430        self.num_registered() > 0
431    }
432
433    /// Gets the metadata associated with the given component, if it is registered.
434    /// This will return `None` if the id is not registered or is queued.
435    ///
436    /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
437    #[inline]
438    pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> {
439        self.components.get(id.0).and_then(|info| info.as_ref())
440    }
441
442    /// Gets the [`ComponentDescriptor`] of the component with this [`ComponentId`] if it is present.
443    /// This will return `None` only if the id is neither registered nor queued to be registered.
444    ///
445    /// Currently, the [`Cow`] will be [`Cow::Owned`] if and only if the component is queued. It will be [`Cow::Borrowed`] otherwise.
446    ///
447    /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
448    #[inline]
449    pub fn get_descriptor<'a>(&'a self, id: ComponentId) -> Option<Cow<'a, ComponentDescriptor>> {
450        self.components
451            .get(id.0)
452            .and_then(|info| info.as_ref().map(|info| Cow::Borrowed(&info.descriptor)))
453            .or_else(|| {
454                let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
455                // first check components, then resources, then dynamic
456                queued
457                    .components
458                    .values()
459                    .chain(queued.resources.values())
460                    .chain(queued.dynamic_registrations.iter())
461                    .find(|queued| queued.id == id)
462                    .map(|queued| Cow::Owned(queued.descriptor.clone()))
463            })
464    }
465
466    /// Gets the name of the component with this [`ComponentId`] if it is present.
467    /// This will return `None` only if the id is neither registered nor queued to be registered.
468    ///
469    /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
470    #[inline]
471    pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<DebugName> {
472        self.components
473            .get(id.0)
474            .and_then(|info| info.as_ref().map(|info| info.descriptor.name()))
475            .or_else(|| {
476                let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
477                // first check components, then resources, then dynamic
478                queued
479                    .components
480                    .values()
481                    .chain(queued.resources.values())
482                    .chain(queued.dynamic_registrations.iter())
483                    .find(|queued| queued.id == id)
484                    .map(|queued| queued.descriptor.name.clone())
485            })
486    }
487
488    /// Gets the metadata associated with the given component.
489    /// # Safety
490    ///
491    /// `id` must be a valid and fully registered [`ComponentId`].
492    #[inline]
493    pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo {
494        // SAFETY: The caller ensures `id` is valid.
495        unsafe {
496            self.components
497                .get(id.0)
498                .debug_checked_unwrap()
499                .as_ref()
500                .debug_checked_unwrap()
501        }
502    }
503
504    #[inline]
505    pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> {
506        self.components
507            .get_mut(id.0)
508            .and_then(|info| info.as_mut().map(|info| &mut info.hooks))
509    }
510
511    #[inline]
512    pub(crate) fn get_required_components(&self, id: ComponentId) -> Option<&RequiredComponents> {
513        self.components
514            .get(id.0)
515            .and_then(|info| info.as_ref().map(|info| &info.required_components))
516    }
517
518    #[inline]
519    pub(crate) fn get_required_components_mut(
520        &mut self,
521        id: ComponentId,
522    ) -> Option<&mut RequiredComponents> {
523        self.components
524            .get_mut(id.0)
525            .and_then(|info| info.as_mut().map(|info| &mut info.required_components))
526    }
527
528    #[inline]
529    pub(crate) fn get_required_by(
530        &self,
531        id: ComponentId,
532    ) -> Option<&IndexSet<ComponentId, FixedHasher>> {
533        self.components
534            .get(id.0)
535            .and_then(|info| info.as_ref().map(|info| &info.required_by))
536    }
537
538    #[inline]
539    pub(crate) fn get_required_by_mut(
540        &mut self,
541        id: ComponentId,
542    ) -> Option<&mut IndexSet<ComponentId, FixedHasher>> {
543        self.components
544            .get_mut(id.0)
545            .and_then(|info| info.as_mut().map(|info| &mut info.required_by))
546    }
547
548    /// Returns true if the [`ComponentId`] is fully registered and valid.
549    /// Ids may be invalid if they are still queued to be registered.
550    /// Those ids are still correct, but they are not usable in every context yet.
551    #[inline]
552    pub fn is_id_valid(&self, id: ComponentId) -> bool {
553        self.components.get(id.0).is_some_and(Option::is_some)
554    }
555
556    /// Type-erased equivalent of [`Components::valid_component_id()`].
557    #[inline]
558    pub fn get_valid_id(&self, type_id: TypeId) -> Option<ComponentId> {
559        self.indices.get(&type_id).copied()
560    }
561
562    /// Returns the [`ComponentId`] of the given [`Component`] type `T` if it is fully registered.
563    /// If you want to include queued registration, see [`Components::component_id()`].
564    ///
565    /// ```
566    /// use bevy_ecs::prelude::*;
567    ///
568    /// let mut world = World::new();
569    ///
570    /// #[derive(Component)]
571    /// struct ComponentA;
572    ///
573    /// let component_a_id = world.register_component::<ComponentA>();
574    ///
575    /// assert_eq!(component_a_id, world.components().valid_component_id::<ComponentA>().unwrap())
576    /// ```
577    ///
578    /// # See also
579    ///
580    /// * [`Components::get_valid_id()`]
581    /// * [`Components::valid_resource_id()`]
582    /// * [`World::component_id()`](crate::world::World::component_id)
583    #[inline]
584    pub fn valid_component_id<T: Component>(&self) -> Option<ComponentId> {
585        self.get_valid_id(TypeId::of::<T>())
586    }
587
588    /// Type-erased equivalent of [`Components::valid_resource_id()`].
589    #[inline]
590    pub fn get_valid_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
591        self.resource_indices.get(&type_id).copied()
592    }
593
594    /// Returns the [`ComponentId`] of the given [`Resource`] type `T` if it is fully registered.
595    /// If you want to include queued registration, see [`Components::resource_id()`].
596    ///
597    /// ```
598    /// use bevy_ecs::prelude::*;
599    ///
600    /// let mut world = World::new();
601    ///
602    /// #[derive(Resource, Default)]
603    /// struct ResourceA;
604    ///
605    /// let resource_a_id = world.init_resource::<ResourceA>();
606    ///
607    /// assert_eq!(resource_a_id, world.components().valid_resource_id::<ResourceA>().unwrap())
608    /// ```
609    ///
610    /// # See also
611    ///
612    /// * [`Components::valid_component_id()`]
613    /// * [`Components::get_resource_id()`]
614    #[inline]
615    pub fn valid_resource_id<T: Resource>(&self) -> Option<ComponentId> {
616        self.get_valid_resource_id(TypeId::of::<T>())
617    }
618
619    /// Type-erased equivalent of [`Components::component_id()`].
620    #[inline]
621    pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
622        self.indices.get(&type_id).copied().or_else(|| {
623            self.queued
624                .read()
625                .unwrap_or_else(PoisonError::into_inner)
626                .components
627                .get(&type_id)
628                .map(|queued| queued.id)
629        })
630    }
631
632    /// Returns the [`ComponentId`] of the given [`Component`] type `T`.
633    ///
634    /// The returned `ComponentId` is specific to the `Components` instance
635    /// it was retrieved from and should not be used with another `Components`
636    /// instance.
637    ///
638    /// Returns [`None`] if the `Component` type has not yet been initialized using
639    /// [`ComponentsRegistrator::register_component()`](super::ComponentsRegistrator::register_component) or
640    /// [`ComponentsQueuedRegistrator::queue_register_component()`](super::ComponentsQueuedRegistrator::queue_register_component).
641    ///
642    /// ```
643    /// use bevy_ecs::prelude::*;
644    ///
645    /// let mut world = World::new();
646    ///
647    /// #[derive(Component)]
648    /// struct ComponentA;
649    ///
650    /// let component_a_id = world.register_component::<ComponentA>();
651    ///
652    /// assert_eq!(component_a_id, world.components().component_id::<ComponentA>().unwrap())
653    /// ```
654    ///
655    /// # See also
656    ///
657    /// * [`ComponentIdFor`](super::ComponentIdFor)
658    /// * [`Components::get_id()`]
659    /// * [`Components::resource_id()`]
660    /// * [`World::component_id()`](crate::world::World::component_id)
661    #[inline]
662    pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
663        self.get_id(TypeId::of::<T>())
664    }
665
666    /// Type-erased equivalent of [`Components::resource_id()`].
667    #[inline]
668    pub fn get_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
669        self.resource_indices.get(&type_id).copied().or_else(|| {
670            self.queued
671                .read()
672                .unwrap_or_else(PoisonError::into_inner)
673                .resources
674                .get(&type_id)
675                .map(|queued| queued.id)
676        })
677    }
678
679    /// Returns the [`ComponentId`] of the given [`Resource`] type `T`.
680    ///
681    /// The returned `ComponentId` is specific to the `Components` instance
682    /// it was retrieved from and should not be used with another `Components`
683    /// instance.
684    ///
685    /// Returns [`None`] if the `Resource` type has not yet been initialized using
686    /// [`ComponentsRegistrator::register_resource()`](super::ComponentsRegistrator::register_resource) or
687    /// [`ComponentsQueuedRegistrator::queue_register_resource()`](super::ComponentsQueuedRegistrator::queue_register_resource).
688    ///
689    /// ```
690    /// use bevy_ecs::prelude::*;
691    ///
692    /// let mut world = World::new();
693    ///
694    /// #[derive(Resource, Default)]
695    /// struct ResourceA;
696    ///
697    /// let resource_a_id = world.init_resource::<ResourceA>();
698    ///
699    /// assert_eq!(resource_a_id, world.components().resource_id::<ResourceA>().unwrap())
700    /// ```
701    ///
702    /// # See also
703    ///
704    /// * [`Components::component_id()`]
705    /// * [`Components::get_resource_id()`]
706    #[inline]
707    pub fn resource_id<T: Resource>(&self) -> Option<ComponentId> {
708        self.get_resource_id(TypeId::of::<T>())
709    }
710
711    /// # Safety
712    ///
713    /// The [`ComponentDescriptor`] must match the [`TypeId`].
714    /// The [`ComponentId`] must be unique.
715    /// The [`TypeId`] and [`ComponentId`] must not be registered or queued.
716    #[inline]
717    pub(super) unsafe fn register_resource_unchecked(
718        &mut self,
719        type_id: TypeId,
720        component_id: ComponentId,
721        descriptor: ComponentDescriptor,
722    ) {
723        // SAFETY: ensured by caller
724        unsafe {
725            self.register_component_inner(component_id, descriptor);
726        }
727        let prev = self.resource_indices.insert(type_id, component_id);
728        debug_assert!(prev.is_none());
729    }
730
731    /// Gets an iterator over all components fully registered with this instance.
732    pub fn iter_registered(&self) -> impl Iterator<Item = &ComponentInfo> + '_ {
733        self.components.iter().filter_map(Option::as_ref)
734    }
735}