Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/component/info.rs
6600 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
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)]
29
pub 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
39
impl 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
)]
173
pub struct ComponentId(pub(super) usize);
174
175
impl 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
192
impl 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)]
206
pub 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
225
impl 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
239
impl 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)]
348
pub 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
356
impl 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
}
736
737