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