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}