From 1c3af190057a017720ab8b95040791c97bbf0a94 Mon Sep 17 00:00:00 2001 From: Garett Cooper Date: Sat, 25 Jun 2022 20:41:54 +0000 Subject: [PATCH] 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::(&self)` function to both `World` and `Components` to retrieve the `ComponentId` associated with `T` from a immutable reference. --- ## Changelog - Added `World::component_id::()` and `Components::component_id::()` to retrieve a `Component`'s corresponding `ComponentId` if it exists. --- crates/bevy_ecs/src/component.rs | 44 ++++++++++++++++++++++++++++++++ crates/bevy_ecs/src/world/mod.rs | 35 +++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 81246e186ce604..3feb6f8ab70677 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -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)] pub struct ComponentId(usize); @@ -422,11 +439,38 @@ impl Components { self.components.get_unchecked(id.0) } + /// Type-erased equivalent of [`Components::component_id`]. #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { 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::(); + /// + /// assert_eq!(component_a_id, world.components().component_id::().unwrap()) + /// ``` + #[inline] + pub fn component_id(&self) -> Option { + self.get_id(TypeId::of::()) + } + #[inline] pub fn get_resource_id(&self, type_id: TypeId) -> Option { self.resource_indices diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index b6e447e8dcd84c..73d67a3174a86e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -177,10 +177,20 @@ impl World { WorldCell::new(self) } + /// Initializes a new [`Component`] type and returns the [`ComponentId`] created for it. pub fn init_component(&mut self) -> ComponentId { self.components.init_component::(&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( &mut self, descriptor: ComponentDescriptor, @@ -189,6 +199,31 @@ impl World { .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::(); + /// + /// assert_eq!(component_a_id, world.component_id::().unwrap()) + /// ``` + #[inline] + pub fn component_id(&self) -> Option { + self.components.component_id::() + } + /// 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 /// to check for entity existence instead of implicitly panic-ing.