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}