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 bevy_reflect::{Reflect, ReflectFromPtr};
8use bevy_utils::prelude::DebugName;
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_valid_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.components().get_name(component_id);
81
82 return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
83 entity,
84 type_id,
85 component_id,
86 component_name,
87 });
88 };
89
90 let Some(type_registry) = self.get_resource::<AppTypeRegistry>().map(|atr| atr.read())
91 else {
92 return Err(GetComponentReflectError::MissingAppTypeRegistry);
93 };
94
95 let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {
96 return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(
97 type_id,
98 ));
99 };
100
101 // 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 present
105 unsafe {
106 assert_eq!(
107 reflect_from_ptr.type_id(),
108 type_id,
109 "Mismatch between Ptr's type_id and ReflectFromPtr's type_id",
110 );
111
112 Ok(reflect_from_ptr.as_reflect(comp_ptr))
113 }
114 }
115
116 /// Retrieves a mutable reference to the given `entity`'s [`Component`] of the given `type_id` using
117 /// 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 information
123 /// and an example.
124 ///
125 /// Just calling this method does not trigger [change detection](crate::change_detection).
126 ///
127 /// # Errors
128 ///
129 /// See [`GetComponentReflectError`] for the possible errors and their descriptions.
130 ///
131 /// # Example
132 ///
133 /// See the documentation for [`World::get_reflect`].
134 ///
135 /// # Note
136 /// Requires the feature `bevy_reflect` (included in the default features).
137 ///
138 /// [`Reflect`]: bevy_reflect::Reflect
139 #[inline]
140 pub fn get_reflect_mut(
141 &mut self,
142 entity: Entity,
143 type_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 a
146 // temporary (from read()) too early.
147 let Some(app_type_registry) = self.get_resource::<AppTypeRegistry>().cloned() else {
148 return Err(GetComponentReflectError::MissingAppTypeRegistry);
149 };
150 let type_registry = app_type_registry.read();
151
152 let Some(reflect_from_ptr) = type_registry.get_type_data::<ReflectFromPtr>(type_id) else {
153 return Err(GetComponentReflectError::MissingReflectFromPtrTypeData(
154 type_id,
155 ));
156 };
157
158 let Some(component_id) = self.components().get_valid_id(type_id) else {
159 return Err(GetComponentReflectError::NoCorrespondingComponentId(
160 type_id,
161 ));
162 };
163
164 // HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will
165 // already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it.
166 let component_name = self.components().get_name(component_id).clone();
167
168 let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else {
169 return Err(GetComponentReflectError::EntityDoesNotHaveComponent {
170 entity,
171 type_id,
172 component_id,
173 component_name,
174 });
175 };
176
177 // 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 present
181 let comp_mut_typed = comp_mut_untyped.map_unchanged(|ptr_mut| unsafe {
182 assert_eq!(
183 reflect_from_ptr.type_id(),
184 type_id,
185 "Mismatch between PtrMut's type_id and ReflectFromPtr's type_id",
186 );
187
188 reflect_from_ptr.as_reflect_mut(ptr_mut)
189 });
190
191 Ok(comp_mut_typed)
192 }
193}
194
195/// The error type returned by [`World::get_reflect`] and [`World::get_reflect_mut`].
196#[derive(Error, Debug)]
197pub enum GetComponentReflectError {
198 /// There is no [`ComponentId`] corresponding to the given [`TypeId`].
199 ///
200 /// This is usually handled by calling [`App::register_type`] for the type corresponding to
201 /// the given [`TypeId`].
202 ///
203 /// See the documentation for [`bevy_reflect`] for more information.
204 ///
205 /// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type
206 #[error("No `ComponentId` corresponding to {0:?} found (did you call App::register_type()?)")]
207 NoCorrespondingComponentId(TypeId),
208
209 /// The given [`Entity`] does not have a [`Component`] corresponding to the given [`TypeId`].
210 #[error("The given `Entity` {entity} does not have a `{component_name:?}` component ({component_id:?}, which corresponds to {type_id:?})")]
211 EntityDoesNotHaveComponent {
212 /// The given [`Entity`].
213 entity: Entity,
214 /// The given [`TypeId`].
215 type_id: TypeId,
216 /// The [`ComponentId`] corresponding to the given [`TypeId`].
217 component_id: ComponentId,
218 /// The name corresponding to the [`Component`] with the given [`TypeId`], or `None`
219 /// if not available.
220 component_name: Option<DebugName>,
221 },
222
223 /// The [`World`] was missing the [`AppTypeRegistry`] resource.
224 #[error("The `World` was missing the `AppTypeRegistry` resource")]
225 MissingAppTypeRegistry,
226
227 /// The [`World`]'s [`TypeRegistry`] did not contain [`TypeData`] for [`ReflectFromPtr`] for the given [`TypeId`].
228 ///
229 /// This is usually handled by calling [`App::register_type`] for the type corresponding to
230 /// the given [`TypeId`].
231 ///
232 /// See the documentation for [`bevy_reflect`] for more information.
233 ///
234 /// [`TypeData`]: bevy_reflect::TypeData
235 /// [`TypeRegistry`]: bevy_reflect::TypeRegistry
236 /// [`ReflectFromPtr`]: bevy_reflect::ReflectFromPtr
237 /// [`App::register_type`]: ../../../bevy_app/struct.App.html#method.register_type
238 #[error("The `World`'s `TypeRegistry` did not contain `TypeData` for `ReflectFromPtr` for the given {0:?} (did you call `App::register_type()`?)")]
239 MissingReflectFromPtrTypeData(TypeId),
240}
241
242#[cfg(test)]
243mod tests {
244 use core::any::TypeId;
245
246 use bevy_reflect::Reflect;
247
248 use crate::prelude::{AppTypeRegistry, Component, DetectChanges, World};
249
250 #[derive(Component, Reflect)]
251 struct RFoo(i32);
252
253 #[derive(Component)]
254 struct Bar;
255
256 #[test]
257 fn get_component_as_reflect() {
258 let mut world = World::new();
259 world.init_resource::<AppTypeRegistry>();
260
261 let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();
262 app_type_registry.write().register::<RFoo>();
263
264 {
265 let entity_with_rfoo = world.spawn(RFoo(42)).id();
266 let comp_reflect = world
267 .get_reflect(entity_with_rfoo, TypeId::of::<RFoo>())
268 .expect("Reflection of RFoo-component failed");
269
270 assert!(comp_reflect.is::<RFoo>());
271 }
272
273 {
274 let entity_without_rfoo = world.spawn_empty().id();
275 let reflect_opt = world.get_reflect(entity_without_rfoo, TypeId::of::<RFoo>());
276
277 assert!(reflect_opt.is_err());
278 }
279
280 {
281 let entity_with_bar = world.spawn(Bar).id();
282 let reflect_opt = world.get_reflect(entity_with_bar, TypeId::of::<Bar>());
283
284 assert!(reflect_opt.is_err());
285 }
286 }
287
288 #[test]
289 fn get_component_as_mut_reflect() {
290 let mut world = World::new();
291 world.init_resource::<AppTypeRegistry>();
292
293 let app_type_registry = world.get_resource_mut::<AppTypeRegistry>().unwrap();
294 app_type_registry.write().register::<RFoo>();
295
296 {
297 let entity_with_rfoo = world.spawn(RFoo(42)).id();
298 let mut comp_reflect = world
299 .get_reflect_mut(entity_with_rfoo, TypeId::of::<RFoo>())
300 .expect("Mutable reflection of RFoo-component failed");
301
302 let comp_rfoo_reflected = comp_reflect
303 .downcast_mut::<RFoo>()
304 .expect("Wrong type reflected (expected RFoo)");
305 assert_eq!(comp_rfoo_reflected.0, 42);
306 comp_rfoo_reflected.0 = 1337;
307
308 let rfoo_ref = world.entity(entity_with_rfoo).get_ref::<RFoo>().unwrap();
309 assert!(rfoo_ref.is_changed());
310 assert_eq!(rfoo_ref.0, 1337);
311 }
312
313 {
314 let entity_without_rfoo = world.spawn_empty().id();
315 let reflect_opt = world.get_reflect_mut(entity_without_rfoo, TypeId::of::<RFoo>());
316
317 assert!(reflect_opt.is_err());
318 }
319
320 {
321 let entity_with_bar = world.spawn(Bar).id();
322 let reflect_opt = world.get_reflect_mut(entity_with_bar, TypeId::of::<Bar>());
323
324 assert!(reflect_opt.is_err());
325 }
326 }
327}