Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/component/info.rs
9299 views
1
use alloc::{borrow::Cow, vec::Vec};
2
use bevy_platform::{hash::FixedHasher, sync::PoisonError};
3
use bevy_ptr::OwningPtr;
4
#[cfg(feature = "bevy_reflect")]
5
use bevy_reflect::Reflect;
6
use bevy_utils::{prelude::DebugName, TypeIdMap};
7
use core::{
8
alloc::Layout,
9
any::{Any, TypeId},
10
fmt::Debug,
11
mem::needs_drop,
12
};
13
use indexmap::IndexSet;
14
15
use 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)]
30
pub 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
40
impl 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
)]
179
pub struct ComponentId(pub(super) usize);
180
181
impl 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
198
impl 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)]
212
pub 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
232
impl 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
247
impl 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)]
362
pub 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
370
impl 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
}
750
751