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}