bevy_ecs/world/
reflect.rs

1//! Provides additional functionality for [`World`] when the `bevy_reflect` feature is enabled.
2
3use core::any::TypeId;
4
5use derive_more::derive::{Display, Error};
6
7use bevy_reflect::{Reflect, ReflectFromPtr};
8
9use crate::{prelude::*, world::ComponentId};
10
11impl World {
12    /// Retrieves a reference to the given `entity`'s [`Component`] of the given `type_id` using
13    /// 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 get
19    /// the corresponding [`TypeId`].
20    ///
21    /// Also see the crate documentation for [`bevy_reflect`] for more information on
22    /// [`Reflect`] and bevy's reflection capabilities.
23    ///
24    /// # Errors
25    ///
26    /// See [`GetComponentReflectError`] for the possible errors and their descriptions.
27    ///
28    /// # Example
29    ///
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 it
36    /// #[derive(Component, Reflect)]
37    /// struct MyComponent;
38    ///
39    /// // create a `World` for this example
40    /// 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 type
53    /// assert!(comp_reflected.is::<MyComponent>());
54    /// ```
55    ///
56    /// # Note
57    /// Requires the `bevy_reflect` feature (included in the default features).
58    ///
59    /// [`Components::get_id`]: crate::component::Components::get_id
60    /// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr
61    /// [`TypeData`]: bevy_reflect::TypeData
62    /// [`Reflect`]: bevy_reflect::Reflect
63    /// [`App::register_type`]: ../../bevy_app/struct.App.html#method.register_type
64    /// [^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]
67    pub fn get_reflect(
68        &self,
69        entity: Entity,
70        type_id: TypeId,
71    ) -> Result<&dyn Reflect, GetComponentReflectError> {
72        let Some(component_id) = self.components().get_id(type_id) else {
73            return Err(GetComponentReflectError::NoCorrespondingComponentId(
74                type_id,
75            ));
76        };
77
78        let Some(comp_ptr) = self.get_by_id(entity, component_id) else {
79            let component_name = self
80                .components()
81                .get_name(component_id)
82                .map(ToString::to_string);
83
84            return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
85                entity,
86                type_id,
87                component_id,
88                component_name,
89            });
90        };
91
92        let Some(type_registry) = self.get_resource::<AppTypeRegistry>().map(|atr| atr.read())
93        else {
94            return Err(GetComponentReflectError::MissingAppTypeRegistry);
95        };
96
97        let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {
98            return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(
99                type_id,
100            ));
101        };
102
103        // SAFETY:
104        // - `comp_ptr` is guaranteed to point to an object of type `type_id`
105        // - `reflect_from_ptr` was constructed for type `type_id`
106        // - Assertion that checks this equality is present
107        unsafe {
108            assert_eq!(
109                reflect_from_ptr.type_id(),
110                type_id,
111                "Mismatch between Ptr's type_id and ReflectFromPtr's type_id",
112            );
113
114            Ok(reflect_from_ptr.as_reflect(comp_ptr))
115        }
116    }
117
118    /// Retrieves a mutable reference to the given `entity`'s [`Component`] of the given `type_id` using
119    /// reflection.
120    ///
121    /// Requires implementing [`Reflect`] for the [`Component`] (e.g., using [`#[derive(Reflect)`](derive@bevy_reflect::Reflect))
122    /// and `app.register_type::<TheComponent>()` to have been called.
123    ///
124    /// This is the mutable version of [`World::get_reflect`], see its docs for more information
125    /// and an example.
126    ///
127    /// Just calling this method does not trigger [change detection](crate::change_detection).
128    ///
129    /// # Errors
130    ///
131    /// See [`GetComponentReflectError`] for the possible errors and their descriptions.
132    ///
133    /// # Example
134    ///
135    /// See the documentation for [`World::get_reflect`].
136    ///
137    /// # Note
138    /// Requires the feature `bevy_reflect` (included in the default features).
139    ///
140    /// [`Reflect`]: bevy_reflect::Reflect
141    #[inline]
142    pub fn get_reflect_mut(
143        &mut self,
144        entity: Entity,
145        type_id: TypeId,
146    ) -> Result<Mut<'_, dyn Reflect>, GetComponentReflectError> {
147        // little clone() + read() dance so we a) don't keep a borrow of `self` and b) don't drop a
148        // temporary (from read()) too  early.
149        let Some(app_type_registry) = self.get_resource::<AppTypeRegistry>().cloned() else {
150            return Err(GetComponentReflectError::MissingAppTypeRegistry);
151        };
152        let type_registry = app_type_registry.read();
153
154        let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {
155            return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(
156                type_id,
157            ));
158        };
159
160        let Some(component_id) = self.components().get_id(type_id) else {
161            return Err(GetComponentReflectError::NoCorrespondingComponentId(
162                type_id,
163            ));
164        };
165
166        // HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will
167        // already be mutablyy borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.
168        let component_name = self
169            .components()
170            .get_name(component_id)
171            .map(ToString::to_string);
172
173        let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {
174            return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
175                entity,
176                type_id,
177                component_id,
178                component_name,
179            });
180        };
181
182        // SAFETY:
183        // - `comp_mut_untyped` is guaranteed to point to an object of type `type_id`
184        // - `reflect_from_ptr` was constructed for type `type_id`
185        // - Assertion that checks this equality is present
186        let comp_mut_typed = comp_mut_untyped.map_unchanged(|ptr_mut| unsafe {
187            assert_eq!(
188                reflect_from_ptr.type_id(),
189                type_id,
190                "Mismatch between PtrMut's type_id and ReflectFromPtr's type_id",
191            );
192
193            reflect_from_ptr.as_reflect_mut(ptr_mut)
194        });
195
196        Ok(comp_mut_typed)
197    }
198}
199
200/// The error type returned by [`World::get_reflect`] and [`World::get_reflect_mut`].
201#[derive(Error, Display, Debug)]
202pub enum GetComponentReflectError {
203    /// There is no [`ComponentId`] corresponding to the given [`TypeId`].
204    ///
205    /// This is usually handled by calling [`App::register_type`] for the type corresponding to
206    /// the given [`TypeId`].
207    ///
208    /// See the documentation for [`bevy_reflect`] for more information.
209    ///
210    /// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type
211    #[display(
212        "No `ComponentId` corresponding to {_0:?} found (did you call App::register_type()?)"
213    )]
214    #[error(ignore)]
215    NoCorrespondingComponentId(TypeId),
216
217    /// The given [`Entity`] does not have a [`Component`] corresponding to the given [`TypeId`].
218    #[display("The given `Entity` {entity:?} does not have a `{component_name:?}` component ({component_id:?}, which corresponds to {type_id:?})")]
219    EntityDoesNotHaveComponent {
220        /// The given [`Entity`].
221        entity: Entity,
222        /// The given [`TypeId`].
223        type_id: TypeId,
224        /// The [`ComponentId`] corresponding to the given [`TypeId`].
225        component_id: ComponentId,
226        /// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`
227        /// if not available.
228        component_name: Option<String>,
229    },
230
231    /// The [`World`] was missing the [`AppTypeRegistry`] resource.
232    #[display("The `World` was missing the `AppTypeRegistry` resource")]
233    MissingAppTypeRegistry,
234
235    /// The [`World`]'s [`TypeRegistry`] did not contain [`TypeData`] for [`ReflectFromPtr`] for the given [`TypeId`].
236    ///
237    /// This is usually handled by calling [`App::register_type`] for the type corresponding to
238    /// the given [`TypeId`].
239    ///
240    /// See the documentation for [`bevy_reflect`] for more information.
241    ///
242    /// [`TypeData`]: bevy_reflect::TypeData
243    /// [`TypeRegistry`]: bevy_reflect::TypeRegistry
244    /// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr
245    /// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type
246    #[display("The `World`'s `TypeRegistry` did not contain `TypeData` for `ReflectFromPtr` for the given {_0:?} (did you call `App::register_type()`?)")]
247    #[error(ignore)]
248    MissingReflectFromPtrTypeData(TypeId),
249}
250
251#[cfg(test)]
252mod tests {
253    use core::any::TypeId;
254
255    use bevy_reflect::Reflect;
256
257    use crate::{
258        // For bevy_ecs_macros
259        self as bevy_ecs,
260        prelude::{AppTypeRegistry, Component, DetectChanges, World},
261    };
262
263    #[derive(Component, Reflect)]
264    struct RFoo(i32);
265
266    #[derive(Component)]
267    struct Bar;
268
269    #[test]
270    fn get_component_as_reflect() {
271        let mut world = World::new();
272        world.init_resource::<AppTypeRegistry>();
273
274        let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();
275        app_type_registry.write().register::<RFoo>();
276
277        {
278            let entity_with_rfoo = world.spawn(RFoo(42)).id();
279            let comp_reflect = world
280                .get_reflect(entity_with_rfoo, TypeId::of::<RFoo>())
281                .expect("Reflection of RFoo-component failed");
282
283            assert!(comp_reflect.is::<RFoo>());
284        }
285
286        {
287            let entity_without_rfoo = world.spawn_empty().id();
288            let reflect_opt = world.get_reflect(entity_without_rfoo, TypeId::of::<RFoo>());
289
290            assert!(reflect_opt.is_err());
291        }
292
293        {
294            let entity_with_bar = world.spawn(Bar).id();
295            let reflect_opt = world.get_reflect(entity_with_bar, TypeId::of::<Bar>());
296
297            assert!(reflect_opt.is_err());
298        }
299    }
300
301    #[test]
302    fn get_component_as_mut_reflect() {
303        let mut world = World::new();
304        world.init_resource::<AppTypeRegistry>();
305
306        let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();
307        app_type_registry.write().register::<RFoo>();
308
309        {
310            let entity_with_rfoo = world.spawn(RFoo(42)).id();
311            let mut comp_reflect = world
312                .get_reflect_mut(entity_with_rfoo, TypeId::of::<RFoo>())
313                .expect("Mutable reflection of RFoo-component failed");
314
315            let comp_rfoo_reflected = comp_reflect
316                .downcast_mut::<RFoo>()
317                .expect("Wrong type reflected (expected RFoo)");
318            assert_eq!(comp_rfoo_reflected.0, 42);
319            comp_rfoo_reflected.0 = 1337;
320
321            let rfoo_ref = world.entity(entity_with_rfoo).get_ref::<RFoo>().unwrap();
322            assert!(rfoo_ref.is_changed());
323            assert_eq!(rfoo_ref.0, 1337);
324        }
325
326        {
327            let entity_without_rfoo = world.spawn_empty().id();
328            let reflect_opt = world.get_reflect_mut(entity_without_rfoo, TypeId::of::<RFoo>());
329
330            assert!(reflect_opt.is_err());
331        }
332
333        {
334            let entity_with_bar = world.spawn(Bar).id();
335            let reflect_opt = world.get_reflect_mut(entity_with_bar, TypeId::of::<Bar>());
336
337            assert!(reflect_opt.is_err());
338        }
339    }
340}