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