Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/world/reflect.rs
6604 views
1
//! Provides additional functionality for [`World`] when the `bevy_reflect` feature is enabled.
2
3
use core::any::TypeId;
4
5
use thiserror::Error;
6
7
use bevy_reflect::{Reflect, ReflectFromPtr};
8
use bevy_utils::prelude::DebugName;
9
10
use crate::{prelude::*, world::ComponentId};
11
12
impl 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)]
197
pub 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)]
243
mod 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
}
328
329