Skip to content

Commit eff96e2

Browse files
authored
Add ReflectFromWorld and replace the FromWorld requirement on ReflectComponent and ReflectBundle with FromReflect (#9623)
# Objective - `FromType<T>` for `ReflectComponent` and `ReflectBundle` currently require `T: FromWorld` for two reasons: - they include a `from_world` method; - they create dummy `T`s using `FromWorld` and then `apply` a `&dyn Reflect` to it to simulate `FromReflect`. - However `FromWorld`/`Default` may be difficult/weird/impractical to implement, while `FromReflect` is easier and also more natural for the job. - See also https://discord.com/channels/691052431525675048/1146022009554337792 ## Solution - Split `from_world` from `ReflectComponent` and `ReflectBundle` into its own `ReflectFromWorld` struct. - Replace the requirement on `FromWorld` in `ReflectComponent` and `ReflectBundle` with `FromReflect` --- ## Changelog - `ReflectComponent` and `ReflectBundle` no longer offer a `from_world` method. - `ReflectComponent` and `ReflectBundle`'s `FromType<T>` implementation no longer requires `T: FromWorld`, but now requires `FromReflect`. - `ReflectComponent::insert`, `ReflectComponent::apply_or_insert` and `ReflectComponent::copy` now take an extra `&TypeRegistry` parameter. - There is now a new `ReflectFromWorld` struct. ## Migration Guide - Existing uses of `ReflectComponent::from_world` and `ReflectBundle::from_world` will have to be changed to `ReflectFromWorld::from_world`. - Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` will need to also implement/derive `FromReflect`. - Users of `#[reflect(Component)]` and `#[reflect(Bundle)]` may now want to also add `FromWorld` to the list of reflected traits in case their `FromReflect` implementation may fail. - Users of `ReflectComponent` will now need to pass a `&TypeRegistry` to its `insert`, `apply_or_insert` and `copy` methods.
1 parent 83d6600 commit eff96e2

File tree

9 files changed

+266
-116
lines changed

9 files changed

+266
-116
lines changed

crates/bevy_ecs/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ pub use bevy_ptr as ptr;
2828
pub mod prelude {
2929
#[doc(hidden)]
3030
#[cfg(feature = "bevy_reflect")]
31-
pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource};
31+
pub use crate::reflect::{
32+
AppTypeRegistry, ReflectComponent, ReflectFromWorld, ReflectResource,
33+
};
3234
#[doc(hidden)]
3335
pub use crate::{
3436
bundle::Bundle,

crates/bevy_ecs/src/reflect/bundle.rs

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
//! Definitions for [`Bundle`] reflection.
2+
//! This allows inserting, updating and/or removing bundles whose type is only known at runtime.
23
//!
34
//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
45
//!
56
//! Same as [`super::component`], but for bundles.
67
use std::any::TypeId;
78

8-
use crate::{
9-
prelude::Bundle,
10-
world::{EntityWorldMut, FromWorld, World},
11-
};
12-
use bevy_reflect::{FromType, Reflect, ReflectRef, TypeRegistry};
9+
use crate::{prelude::Bundle, world::EntityWorldMut};
10+
use bevy_reflect::{FromReflect, FromType, Reflect, ReflectRef, TypeRegistry};
1311

1412
use super::ReflectComponent;
1513

16-
/// A struct used to operate on reflected [`Bundle`] of a type.
14+
/// A struct used to operate on reflected [`Bundle`] trait of a type.
1715
///
1816
/// A [`ReflectBundle`] for type `T` can be obtained via
1917
/// [`bevy_reflect::TypeRegistration::data`].
@@ -25,8 +23,6 @@ pub struct ReflectBundle(ReflectBundleFns);
2523
/// The also [`super::component::ReflectComponentFns`].
2624
#[derive(Clone)]
2725
pub struct ReflectBundleFns {
28-
/// Function pointer implementing [`ReflectBundle::from_world()`].
29-
pub from_world: fn(&mut World) -> Box<dyn Reflect>,
3026
/// Function pointer implementing [`ReflectBundle::insert()`].
3127
pub insert: fn(&mut EntityWorldMut, &dyn Reflect),
3228
/// Function pointer implementing [`ReflectBundle::apply()`].
@@ -43,17 +39,12 @@ impl ReflectBundleFns {
4339
///
4440
/// This is useful if you want to start with the default implementation before overriding some
4541
/// of the functions to create a custom implementation.
46-
pub fn new<T: Bundle + Reflect + FromWorld>() -> Self {
42+
pub fn new<T: Bundle + Reflect + FromReflect>() -> Self {
4743
<ReflectBundle as FromType<T>>::from_type().0
4844
}
4945
}
5046

5147
impl ReflectBundle {
52-
/// Constructs default reflected [`Bundle`] from world using [`from_world()`](FromWorld::from_world).
53-
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
54-
(self.0.from_world)(world)
55-
}
56-
5748
/// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert).
5849
pub fn insert(&self, entity: &mut EntityWorldMut, bundle: &dyn Reflect) {
5950
(self.0.insert)(entity, bundle);
@@ -123,49 +114,53 @@ impl ReflectBundle {
123114
}
124115
}
125116

126-
impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
117+
impl<B: Bundle + Reflect + FromReflect> FromType<B> for ReflectBundle {
127118
fn from_type() -> Self {
128119
ReflectBundle(ReflectBundleFns {
129-
from_world: |world| Box::new(B::from_world(world)),
130120
insert: |entity, reflected_bundle| {
131-
let mut bundle = entity.world_scope(|world| B::from_world(world));
132-
bundle.apply(reflected_bundle);
121+
let bundle = B::from_reflect(reflected_bundle).unwrap();
133122
entity.insert(bundle);
134123
},
135124
apply: |entity, reflected_bundle, registry| {
136-
let mut bundle = entity.world_scope(|world| B::from_world(world));
137-
bundle.apply(reflected_bundle);
138-
139-
match bundle.reflect_ref() {
140-
ReflectRef::Struct(bundle) => bundle
141-
.iter_fields()
142-
.for_each(|field| insert_field::<B>(entity, field, registry)),
143-
ReflectRef::Tuple(bundle) => bundle
144-
.iter_fields()
145-
.for_each(|field| insert_field::<B>(entity, field, registry)),
146-
_ => panic!(
147-
"expected bundle `{}` to be named struct or tuple",
148-
// FIXME: once we have unique reflect, use `TypePath`.
149-
std::any::type_name::<B>(),
150-
),
125+
if let Some(reflect_component) =
126+
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
127+
{
128+
reflect_component.apply(entity, reflected_bundle);
129+
} else {
130+
match reflected_bundle.reflect_ref() {
131+
ReflectRef::Struct(bundle) => bundle
132+
.iter_fields()
133+
.for_each(|field| insert_field(entity, field, registry)),
134+
ReflectRef::Tuple(bundle) => bundle
135+
.iter_fields()
136+
.for_each(|field| insert_field(entity, field, registry)),
137+
_ => panic!(
138+
"expected bundle `{}` to be named struct or tuple",
139+
// FIXME: once we have unique reflect, use `TypePath`.
140+
std::any::type_name::<B>(),
141+
),
142+
}
151143
}
152144
},
153145
apply_or_insert: |entity, reflected_bundle, registry| {
154-
let mut bundle = entity.world_scope(|world| B::from_world(world));
155-
bundle.apply(reflected_bundle);
156-
157-
match bundle.reflect_ref() {
158-
ReflectRef::Struct(bundle) => bundle
159-
.iter_fields()
160-
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
161-
ReflectRef::Tuple(bundle) => bundle
162-
.iter_fields()
163-
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
164-
_ => panic!(
165-
"expected bundle `{}` to be named struct or tuple",
166-
// FIXME: once we have unique reflect, use `TypePath`.
167-
std::any::type_name::<B>(),
168-
),
146+
if let Some(reflect_component) =
147+
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
148+
{
149+
reflect_component.apply_or_insert(entity, reflected_bundle, registry);
150+
} else {
151+
match reflected_bundle.reflect_ref() {
152+
ReflectRef::Struct(bundle) => bundle
153+
.iter_fields()
154+
.for_each(|field| apply_or_insert_field(entity, field, registry)),
155+
ReflectRef::Tuple(bundle) => bundle
156+
.iter_fields()
157+
.for_each(|field| apply_or_insert_field(entity, field, registry)),
158+
_ => panic!(
159+
"expected bundle `{}` to be named struct or tuple",
160+
// FIXME: once we have unique reflect, use `TypePath`.
161+
std::any::type_name::<B>(),
162+
),
163+
}
169164
}
170165
},
171166
remove: |entity| {
@@ -175,54 +170,58 @@ impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
175170
}
176171
}
177172

178-
fn insert_field<B: 'static>(
179-
entity: &mut EntityWorldMut,
180-
field: &dyn Reflect,
181-
registry: &TypeRegistry,
182-
) {
173+
fn insert_field(entity: &mut EntityWorldMut, field: &dyn Reflect, registry: &TypeRegistry) {
183174
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
184175
reflect_component.apply(entity, field);
185176
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
186177
reflect_bundle.apply(entity, field, registry);
187178
} else {
188-
entity.world_scope(|world| {
189-
if world.components().get_id(TypeId::of::<B>()).is_some() {
190-
panic!(
191-
"no `ReflectComponent` registration found for `{}`",
192-
field.reflect_type_path(),
193-
);
194-
};
195-
});
196-
197-
panic!(
198-
"no `ReflectBundle` registration found for `{}`",
199-
field.reflect_type_path(),
200-
)
179+
let is_component = entity
180+
.world()
181+
.components()
182+
.get_id(field.type_id())
183+
.is_some();
184+
185+
if is_component {
186+
panic!(
187+
"no `ReflectComponent` registration found for `{}`",
188+
field.reflect_type_path(),
189+
);
190+
} else {
191+
panic!(
192+
"no `ReflectBundle` registration found for `{}`",
193+
field.reflect_type_path(),
194+
)
195+
}
201196
}
202197
}
203198

204-
fn apply_or_insert_field<B: 'static>(
199+
fn apply_or_insert_field(
205200
entity: &mut EntityWorldMut,
206201
field: &dyn Reflect,
207202
registry: &TypeRegistry,
208203
) {
209204
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
210-
reflect_component.apply_or_insert(entity, field);
205+
reflect_component.apply_or_insert(entity, field, registry);
211206
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
212207
reflect_bundle.apply_or_insert(entity, field, registry);
213208
} else {
214-
entity.world_scope(|world| {
215-
if world.components().get_id(TypeId::of::<B>()).is_some() {
216-
panic!(
217-
"no `ReflectComponent` registration found for `{}`",
218-
field.reflect_type_path(),
219-
);
220-
};
221-
});
222-
223-
panic!(
224-
"no `ReflectBundle` registration found for `{}`",
225-
field.reflect_type_path(),
226-
)
209+
let is_component = entity
210+
.world()
211+
.components()
212+
.get_id(field.type_id())
213+
.is_some();
214+
215+
if is_component {
216+
panic!(
217+
"no `ReflectComponent` registration found for `{}`",
218+
field.reflect_type_path(),
219+
);
220+
} else {
221+
panic!(
222+
"no `ReflectBundle` registration found for `{}`",
223+
field.reflect_type_path(),
224+
)
225+
}
227226
}
228227
}

0 commit comments

Comments
 (0)