Add component_id function to World and Components (#5066)

# Objective

- Simplify the process of obtaining a `ComponentId` instance corresponding to a `Component`.
- Resolves #5060.

## Solution

- Add a `component_id::<T: Component>(&self)` function to both `World` and `Components` to retrieve the `ComponentId` associated with `T` from a immutable reference.

---

## Changelog

- Added `World::component_id::<C>()` and `Components::component_id::<C>()` to retrieve a `Component`'s corresponding `ComponentId` if it exists.
This commit is contained in:
Garett Cooper 2022-06-25 20:41:54 +00:00
parent f8fa229465
commit fa56a5cd51
2 changed files with 79 additions and 0 deletions

View file

@ -214,6 +214,23 @@ impl ComponentInfo {
} }
} }
/// A semi-opaque value which uniquely identifies the type of a [`Component`] within a
/// [`World`](crate::world::World).
///
/// Each time a new `Component` type is registered within a `World` using
/// [`World::init_component`](crate::world::World::init_component) or
/// [`World::init_component_with_descriptor`](crate::world::World::init_component_with_descriptor),
/// a corresponding `ComponentId` is created to track it.
///
/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them
/// into two separate but related concepts allows components to exist outside of Rust's type system.
/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional
/// `ComponentId`s may exist in a `World` to track components which cannot be
/// represented as Rust types for scripting or other advanced use-cases.
///
/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from
/// one `World` to access the metadata of a `Component` in a different `World` is undefined behaviour
/// and must not be attempted.
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ComponentId(usize); pub struct ComponentId(usize);
@ -422,11 +439,38 @@ impl Components {
self.components.get_unchecked(id.0) self.components.get_unchecked(id.0)
} }
/// Type-erased equivalent of [`Components::component_id`].
#[inline] #[inline]
pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> { pub fn get_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.indices.get(&type_id).map(|index| ComponentId(*index)) self.indices.get(&type_id).map(|index| ComponentId(*index))
} }
/// Returns the [`ComponentId`] of the given [`Component`] type `T`.
///
/// The returned `ComponentId` is specific to the `Components` instance
/// it was retrieved from and should not be used with another `Components`
/// instance.
///
/// Returns [`None`] if the `Component` type has not
/// yet been initialized using [`Components::init_component`].
///
/// ```rust
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Component)]
/// struct ComponentA;
///
/// let component_a_id = world.init_component::<ComponentA>();
///
/// assert_eq!(component_a_id, world.components().component_id::<ComponentA>().unwrap())
/// ```
#[inline]
pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
self.get_id(TypeId::of::<T>())
}
#[inline] #[inline]
pub fn get_resource_id(&self, type_id: TypeId) -> Option<ComponentId> { pub fn get_resource_id(&self, type_id: TypeId) -> Option<ComponentId> {
self.resource_indices self.resource_indices

View file

@ -177,10 +177,20 @@ impl World {
WorldCell::new(self) WorldCell::new(self)
} }
/// Initializes a new [`Component`] type and returns the [`ComponentId`] created for it.
pub fn init_component<T: Component>(&mut self) -> ComponentId { pub fn init_component<T: Component>(&mut self) -> ComponentId {
self.components.init_component::<T>(&mut self.storages) self.components.init_component::<T>(&mut self.storages)
} }
/// Initializes a new [`Component`] type and returns the [`ComponentId`] created for it.
///
/// This method differs from [`World::init_component`] in that it uses a [`ComponentDescriptor`]
/// to initialize the new component type instead of statically available type information. This
/// enables the dynamic initialization of new component definitions at runtime for advanced use cases.
///
/// While the option to initialize a component from a descriptor is useful in type-erased
/// contexts, the standard `World::init_component` function should always be used instead
/// when type information is available at compile time.
pub fn init_component_with_descriptor( pub fn init_component_with_descriptor(
&mut self, &mut self,
descriptor: ComponentDescriptor, descriptor: ComponentDescriptor,
@ -189,6 +199,31 @@ impl World {
.init_component_with_descriptor(&mut self.storages, descriptor) .init_component_with_descriptor(&mut self.storages, descriptor)
} }
/// Returns the [`ComponentId`] of the given [`Component`] type `T`.
///
/// The returned `ComponentId` is specific to the `World` instance
/// it was retrieved from and should not be used with another `World` instance.
///
/// Returns [`None`] if the `Component` type has not yet been initialized within
/// the `World` using [`World::init_component`].
///
/// ```rust
/// use bevy_ecs::prelude::*;
///
/// let mut world = World::new();
///
/// #[derive(Component)]
/// struct ComponentA;
///
/// let component_a_id = world.init_component::<ComponentA>();
///
/// assert_eq!(component_a_id, world.component_id::<ComponentA>().unwrap())
/// ```
#[inline]
pub fn component_id<T: Component>(&self) -> Option<ComponentId> {
self.components.component_id::<T>()
}
/// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`. /// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`.
/// This will panic if the `entity` does not exist. Use [`World::get_entity`] if you want /// This will panic if the `entity` does not exist. Use [`World::get_entity`] if you want
/// to check for entity existence instead of implicitly panic-ing. /// to check for entity existence instead of implicitly panic-ing.