Skip to main content

bevy_ecs/world/
reflect.rs

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