//! Provides additional functionality for [`World`] when the `bevy_reflect` feature is enabled.12use core::any::TypeId;34use thiserror::Error;56use bevy_reflect::{Reflect, ReflectFromPtr};7use bevy_utils::prelude::DebugName;89use crate::{prelude::*, world::ComponentId};1011impl World {12/// Retrieves a reference to the given `entity`'s [`Component`] of the given `type_id` using13/// reflection.14///15/// Requires implementing [`Reflect`] for the [`Component`] (e.g., using [`#[derive(Reflect)`](derive@bevy_reflect::Reflect))16/// and `app.register_type::<TheComponent>()` to have been called[^note-reflect-impl].17///18/// If you want to call this with a [`ComponentId`], see [`World::components`] and [`Components::get_id`] to get19/// the corresponding [`TypeId`].20///21/// Also see the crate documentation for [`bevy_reflect`] for more information on22/// [`Reflect`] and bevy's reflection capabilities.23///24/// # Errors25///26/// See [`GetComponentReflectError`] for the possible errors and their descriptions.27///28/// # Example29///30/// ```31/// use bevy_ecs::prelude::*;32/// use bevy_reflect::Reflect;33/// use std::any::TypeId;34///35/// // define a `Component` and derive `Reflect` for it36/// #[derive(Component, Reflect)]37/// struct MyComponent;38///39/// // create a `World` for this example40/// let mut world = World::new();41///42/// // Note: This is usually handled by `App::register_type()`, but this example cannot use `App`.43/// world.init_resource::<AppTypeRegistry>();44/// world.get_resource_mut::<AppTypeRegistry>().unwrap().write().register::<MyComponent>();45///46/// // spawn an entity with a `MyComponent`47/// let entity = world.spawn(MyComponent).id();48///49/// // retrieve a reflected reference to the entity's `MyComponent`50/// let comp_reflected: &dyn Reflect = world.get_reflect(entity, TypeId::of::<MyComponent>()).unwrap();51///52/// // make sure we got the expected type53/// assert!(comp_reflected.is::<MyComponent>());54/// ```55///56/// # Note57/// Requires the `bevy_reflect` feature (included in the default features).58///59/// [`Components::get_id`]: crate::component::Components::get_id60/// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr61/// [`TypeData`]: bevy_reflect::TypeData62/// [`Reflect`]: bevy_reflect::Reflect63/// [`App::register_type`]: ../../bevy_app/struct.App.html#method.register_type64/// [^note-reflect-impl]: More specifically: Requires [`TypeData`] for [`ReflectFromPtr`] to be registered for the given `type_id`,65/// which is automatically handled when deriving [`Reflect`] and calling [`App::register_type`].66#[inline]67pub fn get_reflect(68&self,69entity: Entity,70type_id: TypeId,71) -> Result<&dyn Reflect, GetComponentReflectError> {72let Some(component_id) = self.components().get_valid_id(type_id) else {73return Err(GetComponentReflectError::NoCorrespondingComponentId(74type_id,75));76};7778let Some(comp_ptr) = self.get_by_id(entity, component_id) else {79let component_name = self.components().get_name(component_id);8081return Err(GetComponentReflectError::EntityDoesNotHaveComponent {82entity,83type_id,84component_id,85component_name,86});87};8889let Some(type_registry) = self.get_resource::<AppTypeRegistry>().map(|atr| atr.read())90else {91return Err(GetComponentReflectError::MissingAppTypeRegistry);92};9394let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {95return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(96type_id,97));98};99100// SAFETY:101// - `comp_ptr` is guaranteed to point to an object of type `type_id`102// - `reflect_from_ptr` was constructed for type `type_id`103// - Assertion that checks this equality is present104unsafe {105assert_eq!(106reflect_from_ptr.type_id(),107type_id,108"Mismatch between Ptr's type_id and ReflectFromPtr's type_id",109);110111Ok(reflect_from_ptr.as_reflect(comp_ptr))112}113}114115/// Retrieves a mutable reference to the given `entity`'s [`Component`] of the given `type_id` using116/// reflection.117///118/// Requires implementing [`Reflect`] for the [`Component`] (e.g., using [`#[derive(Reflect)`](derive@bevy_reflect::Reflect))119/// and `app.register_type::<TheComponent>()` to have been called.120///121/// This is the mutable version of [`World::get_reflect`], see its docs for more information122/// and an example.123///124/// Just calling this method does not trigger [change detection](crate::change_detection).125///126/// # Errors127///128/// See [`GetComponentReflectError`] for the possible errors and their descriptions.129///130/// # Example131///132/// See the documentation for [`World::get_reflect`].133///134/// # Note135/// Requires the feature `bevy_reflect` (included in the default features).136///137/// [`Reflect`]: bevy_reflect::Reflect138#[inline]139pub fn get_reflect_mut(140&mut self,141entity: Entity,142type_id: TypeId,143) -> Result<Mut<'_, dyn Reflect>, GetComponentReflectError> {144// little clone() + read() dance so we a) don't keep a borrow of `self` and b) don't drop a145// temporary (from read()) too early.146let Some(app_type_registry) = self.get_resource::<AppTypeRegistry>().cloned() else {147return Err(GetComponentReflectError::MissingAppTypeRegistry);148};149let type_registry = app_type_registry.read();150151let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {152return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(153type_id,154));155};156157let Some(component_id) = self.components().get_valid_id(type_id) else {158return Err(GetComponentReflectError::NoCorrespondingComponentId(159type_id,160));161};162163// HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will164// already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.165let component_name = self.components().get_name(component_id).clone();166167let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {168return Err(GetComponentReflectError::EntityDoesNotHaveComponent {169entity,170type_id,171component_id,172component_name,173});174};175176// SAFETY:177// - `comp_mut_untyped` is guaranteed to point to an object of type `type_id`178// - `reflect_from_ptr` was constructed for type `type_id`179// - Assertion that checks this equality is present180let comp_mut_typed = comp_mut_untyped.map_unchanged(|ptr_mut| unsafe {181assert_eq!(182reflect_from_ptr.type_id(),183type_id,184"Mismatch between PtrMut's type_id and ReflectFromPtr's type_id",185);186187reflect_from_ptr.as_reflect_mut(ptr_mut)188});189190Ok(comp_mut_typed)191}192}193194/// The error type returned by [`World::get_reflect`] and [`World::get_reflect_mut`].195#[derive(Error, Debug)]196pub enum GetComponentReflectError {197/// There is no [`ComponentId`] corresponding to the given [`TypeId`].198///199/// This is usually handled by calling [`App::register_type`] for the type corresponding to200/// the given [`TypeId`].201///202/// See the documentation for [`bevy_reflect`] for more information.203///204/// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type205#[error("No `ComponentId` corresponding to {0:?} found (did you call App::register_type()?)")]206NoCorrespondingComponentId(TypeId),207208/// The given [`Entity`] does not have a [`Component`] corresponding to the given [`TypeId`].209#[error("The given `Entity` {entity} does not have a `{component_name:?}` component ({component_id:?}, which corresponds to {type_id:?})")]210EntityDoesNotHaveComponent {211/// The given [`Entity`].212entity: Entity,213/// The given [`TypeId`].214type_id: TypeId,215/// The [`ComponentId`] corresponding to the given [`TypeId`].216component_id: ComponentId,217/// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`218/// if not available.219component_name: Option<DebugName>,220},221222/// The [`World`] was missing the [`AppTypeRegistry`] resource.223#[error("The `World` was missing the `AppTypeRegistry` resource")]224MissingAppTypeRegistry,225226/// The [`World`]'s [`TypeRegistry`] did not contain [`TypeData`] for [`ReflectFromPtr`] for the given [`TypeId`].227///228/// This is usually handled by calling [`App::register_type`] for the type corresponding to229/// the given [`TypeId`].230///231/// See the documentation for [`bevy_reflect`] for more information.232///233/// [`TypeData`]: bevy_reflect::TypeData234/// [`TypeRegistry`]: bevy_reflect::TypeRegistry235/// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr236/// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type237#[error("The `World`'s `TypeRegistry` did not contain `TypeData` for `ReflectFromPtr` for the given {0:?} (did you call `App::register_type()`?)")]238MissingReflectFromPtrTypeData(TypeId),239}240241#[cfg(test)]242mod tests {243use core::any::TypeId;244245use bevy_reflect::Reflect;246247use crate::prelude::{AppTypeRegistry, Component, DetectChanges, World};248249#[derive(Component, Reflect)]250struct RFoo(i32);251252#[derive(Component)]253struct Bar;254255#[test]256fn get_component_as_reflect() {257let mut world = World::new();258world.init_resource::<AppTypeRegistry>();259260let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();261app_type_registry.write().register::<RFoo>();262263{264let entity_with_rfoo = world.spawn(RFoo(42)).id();265let comp_reflect = world266.get_reflect(entity_with_rfoo, TypeId::of::<RFoo>())267.expect("Reflection of RFoo-component failed");268269assert!(comp_reflect.is::<RFoo>());270}271272{273let entity_without_rfoo = world.spawn_empty().id();274let reflect_opt = world.get_reflect(entity_without_rfoo, TypeId::of::<RFoo>());275276assert!(reflect_opt.is_err());277}278279{280let entity_with_bar = world.spawn(Bar).id();281let reflect_opt = world.get_reflect(entity_with_bar, TypeId::of::<Bar>());282283assert!(reflect_opt.is_err());284}285}286287#[test]288fn get_component_as_mut_reflect() {289let mut world = World::new();290world.init_resource::<AppTypeRegistry>();291292let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();293app_type_registry.write().register::<RFoo>();294295{296let entity_with_rfoo = world.spawn(RFoo(42)).id();297let mut comp_reflect = world298.get_reflect_mut(entity_with_rfoo, TypeId::of::<RFoo>())299.expect("Mutable reflection of RFoo-component failed");300301let comp_rfoo_reflected = comp_reflect302.downcast_mut::<RFoo>()303.expect("Wrong type reflected (expected RFoo)");304assert_eq!(comp_rfoo_reflected.0, 42);305comp_rfoo_reflected.0 = 1337;306307let rfoo_ref = world.entity(entity_with_rfoo).get_ref::<RFoo>().unwrap();308assert!(rfoo_ref.is_changed());309assert_eq!(rfoo_ref.0, 1337);310}311312{313let entity_without_rfoo = world.spawn_empty().id();314let reflect_opt = world.get_reflect_mut(entity_without_rfoo, TypeId::of::<RFoo>());315316assert!(reflect_opt.is_err());317}318319{320let entity_with_bar = world.spawn(Bar).id();321let reflect_opt = world.get_reflect_mut(entity_with_bar, TypeId::of::<Bar>());322323assert!(reflect_opt.is_err());324}325}326}327328329