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}