//! Provides additional functionality for [`World`] when the `bevy_reflect` feature is enabled.12use alloc::boxed::Box;3use core::any::TypeId;45use thiserror::Error;67use bevy_reflect::{PartialReflect, Reflect, ReflectFromPtr};8use bevy_utils::prelude::DebugName;910use crate::{prelude::*, world::ComponentId};1112impl World {13/// Retrieves a reference to the given `entity`'s [`Component`] of the given `type_id` using14/// reflection.15///16/// Requires implementing [`Reflect`] for the [`Component`] (e.g., using [`#[derive(Reflect)`](derive@bevy_reflect::Reflect))17/// and `app.register_type::<TheComponent>()` to have been called[^note-reflect-impl].18///19/// If you want to call this with a [`ComponentId`], see [`World::components`] and [`Components::get_id`] to get20/// the corresponding [`TypeId`].21///22/// Also see the crate documentation for [`bevy_reflect`] for more information on23/// [`Reflect`] and bevy's reflection capabilities.24///25/// # Errors26///27/// See [`GetComponentReflectError`] for the possible errors and their descriptions.28///29/// # Example30///31/// ```32/// use bevy_ecs::prelude::*;33/// use bevy_reflect::Reflect;34/// use std::any::TypeId;35///36/// // define a `Component` and derive `Reflect` for it37/// #[derive(Component, Reflect)]38/// struct MyComponent;39///40/// // create a `World` for this example41/// let mut world = World::new();42///43/// // Note: This is usually handled by `App::register_type()`, but this example cannot use `App`.44/// world.init_resource::<AppTypeRegistry>();45/// world.get_resource_mut::<AppTypeRegistry>().unwrap().write().register::<MyComponent>();46///47/// // spawn an entity with a `MyComponent`48/// let entity = world.spawn(MyComponent).id();49///50/// // retrieve a reflected reference to the entity's `MyComponent`51/// let comp_reflected: &dyn Reflect = world.get_reflect(entity, TypeId::of::<MyComponent>()).unwrap();52///53/// // make sure we got the expected type54/// assert!(comp_reflected.is::<MyComponent>());55/// ```56///57/// # Note58/// Requires the `bevy_reflect` feature (included in the default features).59///60/// [`Components::get_id`]: crate::component::Components::get_id61/// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr62/// [`TypeData`]: bevy_reflect::TypeData63/// [`Reflect`]: bevy_reflect::Reflect64/// [`App::register_type`]: ../../bevy_app/struct.App.html#method.register_type65/// [^note-reflect-impl]: More specifically: Requires [`TypeData`] for [`ReflectFromPtr`] to be registered for the given `type_id`,66/// which is automatically handled when deriving [`Reflect`] and calling [`App::register_type`].67#[inline]68pub fn get_reflect(69&self,70entity: Entity,71type_id: TypeId,72) -> Result<&dyn Reflect, GetComponentReflectError> {73let Some(component_id) = self.components().get_valid_id(type_id) else {74return Err(GetComponentReflectError::NoCorrespondingComponentId(75type_id,76));77};7879let Some(comp_ptr) = self.get_by_id(entity, component_id) else {80let component_name = self.components().get_name(component_id);8182return Err(GetComponentReflectError::EntityDoesNotHaveComponent {83entity,84type_id,85component_id,86component_name,87});88};8990let Some(type_registry) = self.get_resource::<AppTypeRegistry>().map(|atr| atr.read())91else {92return Err(GetComponentReflectError::MissingAppTypeRegistry);93};9495let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {96return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(97type_id,98));99};100101// SAFETY:102// - `comp_ptr` is guaranteed to point to an object of type `type_id`103// - `reflect_from_ptr` was constructed for type `type_id`104// - Assertion that checks this equality is present105unsafe {106assert_eq!(107reflect_from_ptr.type_id(),108type_id,109"Mismatch between Ptr's type_id and ReflectFromPtr's type_id",110);111112Ok(reflect_from_ptr.as_reflect(comp_ptr))113}114}115116/// Retrieves a mutable reference to the given `entity`'s [`Component`] of the given `type_id` using117/// reflection.118///119/// Requires implementing [`Reflect`] for the [`Component`] (e.g., using [`#[derive(Reflect)`](derive@bevy_reflect::Reflect))120/// and `app.register_type::<TheComponent>()` to have been called.121///122/// This is the mutable version of [`World::get_reflect`], see its docs for more information123/// and an example.124///125/// Just calling this method does not trigger [change detection](crate::change_detection).126///127/// # Errors128///129/// See [`GetComponentReflectError`] for the possible errors and their descriptions.130///131/// # Example132///133/// See the documentation for [`World::get_reflect`].134///135/// # Note136/// Requires the feature `bevy_reflect` (included in the default features).137///138/// [`Reflect`]: bevy_reflect::Reflect139#[inline]140pub fn get_reflect_mut(141&mut self,142entity: Entity,143type_id: TypeId,144) -> Result<Mut<'_, dyn Reflect>, GetComponentReflectError> {145// little clone() + read() dance so we a) don't keep a borrow of `self` and b) don't drop a146// temporary (from read()) too early.147let Some(app_type_registry) = self.get_resource::<AppTypeRegistry>().cloned() else {148return Err(GetComponentReflectError::MissingAppTypeRegistry);149};150let type_registry = app_type_registry.read();151152let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {153return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(154type_id,155));156};157158let Some(component_id) = self.components().get_valid_id(type_id) else {159return Err(GetComponentReflectError::NoCorrespondingComponentId(160type_id,161));162};163164// HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will165// already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.166let component_name = self.components().get_name(component_id).clone();167168let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {169return Err(GetComponentReflectError::EntityDoesNotHaveComponent {170entity,171type_id,172component_id,173component_name,174});175};176177// SAFETY:178// - `comp_mut_untyped` is guaranteed to point to an object of type `type_id`179// - `reflect_from_ptr` was constructed for type `type_id`180// - Assertion that checks this equality is present181let comp_mut_typed = comp_mut_untyped.map_unchanged(|ptr_mut| unsafe {182assert_eq!(183reflect_from_ptr.type_id(),184type_id,185"Mismatch between PtrMut's type_id and ReflectFromPtr's type_id",186);187188reflect_from_ptr.as_reflect_mut(ptr_mut)189});190191Ok(comp_mut_typed)192}193194/// Inserts a reflected resource into the world. If the resource already exists, it is overwritten.195#[inline]196pub fn insert_reflect_resource(197&mut self,198resource_id: ComponentId,199reflected_resource: Box<dyn PartialReflect>,200) {201if let Some(entity) = self.resource_entities().get(resource_id) {202self.entity_mut(*entity).insert_reflect(reflected_resource);203} else {204self.spawn_empty().insert_reflect(reflected_resource);205}206}207}208209/// The error type returned by [`World::get_reflect`] and [`World::get_reflect_mut`].210#[derive(Error, Debug)]211pub enum GetComponentReflectError {212/// There is no [`ComponentId`] corresponding to the given [`TypeId`].213///214/// This is usually handled by calling [`App::register_type`] for the type corresponding to215/// the given [`TypeId`].216///217/// See the documentation for [`bevy_reflect`] for more information.218///219/// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type220#[error("No `ComponentId` corresponding to {0:?} found (did you call App::register_type()?)")]221NoCorrespondingComponentId(TypeId),222223/// The given [`Entity`] does not have a [`Component`] corresponding to the given [`TypeId`].224#[error("The given `Entity` {entity} does not have a `{component_name:?}` component ({component_id:?}, which corresponds to {type_id:?})")]225EntityDoesNotHaveComponent {226/// The given [`Entity`].227entity: Entity,228/// The given [`TypeId`].229type_id: TypeId,230/// The [`ComponentId`] corresponding to the given [`TypeId`].231component_id: ComponentId,232/// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`233/// if not available.234component_name: Option<DebugName>,235},236237/// The [`World`] was missing the [`AppTypeRegistry`] resource.238#[error("The `World` was missing the `AppTypeRegistry` resource")]239MissingAppTypeRegistry,240241/// The [`World`]'s [`TypeRegistry`] did not contain [`TypeData`] for [`ReflectFromPtr`] for the given [`TypeId`].242///243/// This is usually handled by calling [`App::register_type`] for the type corresponding to244/// the given [`TypeId`].245///246/// See the documentation for [`bevy_reflect`] for more information.247///248/// [`TypeData`]: bevy_reflect::TypeData249/// [`TypeRegistry`]: bevy_reflect::TypeRegistry250/// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr251/// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type252#[error("The `World`'s `TypeRegistry` did not contain `TypeData` for `ReflectFromPtr` for the given {0:?} (did you call `App::register_type()`?)")]253MissingReflectFromPtrTypeData(TypeId),254}255256#[cfg(test)]257mod tests {258use core::any::TypeId;259260use bevy_reflect::Reflect;261262use crate::prelude::{AppTypeRegistry, Component, DetectChanges, World};263264#[derive(Component, Reflect)]265struct RFoo(i32);266267#[derive(Component)]268struct Bar;269270#[test]271fn get_component_as_reflect() {272let mut world = World::new();273world.init_resource::<AppTypeRegistry>();274275let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();276app_type_registry.write().register::<RFoo>();277278{279let entity_with_rfoo = world.spawn(RFoo(42)).id();280let comp_reflect = world281.get_reflect(entity_with_rfoo, TypeId::of::<RFoo>())282.expect("Reflection of RFoo-component failed");283284assert!(comp_reflect.is::<RFoo>());285}286287{288let entity_without_rfoo = world.spawn_empty().id();289let reflect_opt = world.get_reflect(entity_without_rfoo, TypeId::of::<RFoo>());290291assert!(reflect_opt.is_err());292}293294{295let entity_with_bar = world.spawn(Bar).id();296let reflect_opt = world.get_reflect(entity_with_bar, TypeId::of::<Bar>());297298assert!(reflect_opt.is_err());299}300}301302#[test]303fn get_component_as_mut_reflect() {304let mut world = World::new();305world.init_resource::<AppTypeRegistry>();306307let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();308app_type_registry.write().register::<RFoo>();309310{311let entity_with_rfoo = world.spawn(RFoo(42)).id();312let mut comp_reflect = world313.get_reflect_mut(entity_with_rfoo, TypeId::of::<RFoo>())314.expect("Mutable reflection of RFoo-component failed");315316let comp_rfoo_reflected = comp_reflect317.downcast_mut::<RFoo>()318.expect("Wrong type reflected (expected RFoo)");319assert_eq!(comp_rfoo_reflected.0, 42);320comp_rfoo_reflected.0 = 1337;321322let rfoo_ref = world.entity(entity_with_rfoo).get_ref::<RFoo>().unwrap();323assert!(rfoo_ref.is_changed());324assert_eq!(rfoo_ref.0, 1337);325}326327{328let entity_without_rfoo = world.spawn_empty().id();329let reflect_opt = world.get_reflect_mut(entity_without_rfoo, TypeId::of::<RFoo>());330331assert!(reflect_opt.is_err());332}333334{335let entity_with_bar = world.spawn(Bar).id();336let reflect_opt = world.get_reflect_mut(entity_with_bar, TypeId::of::<Bar>());337338assert!(reflect_opt.is_err());339}340}341}342343344