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