Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ReflectFromWorld and replace the FromWorld requirement on ReflectComponent and ReflectBundle with FromReflect #9623

Merged
merged 12 commits into from
Jan 19, 2024
Merged
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ pub use bevy_ptr as ptr;
pub mod prelude {
#[doc(hidden)]
#[cfg(feature = "bevy_reflect")]
pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource};
pub use crate::reflect::{
AppTypeRegistry, ReflectComponent, ReflectFromWorld, ReflectResource,
};
#[doc(hidden)]
pub use crate::{
bundle::Bundle,
Expand Down
161 changes: 80 additions & 81 deletions crates/bevy_ecs/src/reflect/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
//! Definitions for [`Bundle`] reflection.
//! This allows inserting, updating and/or removing bundles whose type is only known at runtime.
//!
//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
//!
//! Same as [`super::component`], but for bundles.
use std::any::TypeId;

use crate::{
prelude::Bundle,
world::{EntityWorldMut, FromWorld, World},
};
use bevy_reflect::{FromType, Reflect, ReflectRef, TypeRegistry};
use crate::{prelude::Bundle, world::EntityWorldMut};
use bevy_reflect::{FromReflect, FromType, Reflect, ReflectRef, TypeRegistry};

use super::ReflectComponent;

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

impl ReflectBundle {
/// Constructs default reflected [`Bundle`] from world using [`from_world()`](FromWorld::from_world).
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
(self.0.from_world)(world)
}

/// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert).
pub fn insert(&self, entity: &mut EntityWorldMut, bundle: &dyn Reflect) {
(self.0.insert)(entity, bundle);
Expand Down Expand Up @@ -123,49 +114,53 @@ impl ReflectBundle {
}
}

impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
impl<B: Bundle + Reflect + FromReflect> FromType<B> for ReflectBundle {
fn from_type() -> Self {
ReflectBundle(ReflectBundleFns {
from_world: |world| Box::new(B::from_world(world)),
insert: |entity, reflected_bundle| {
let mut bundle = entity.world_scope(|world| B::from_world(world));
bundle.apply(reflected_bundle);
let bundle = B::from_reflect(reflected_bundle).unwrap();
entity.insert(bundle);
},
apply: |entity, reflected_bundle, registry| {
let mut bundle = entity.world_scope(|world| B::from_world(world));
bundle.apply(reflected_bundle);

match bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle
.iter_fields()
.for_each(|field| insert_field::<B>(entity, field, registry)),
ReflectRef::Tuple(bundle) => bundle
.iter_fields()
.for_each(|field| insert_field::<B>(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
if let Some(reflect_component) =
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
{
reflect_component.apply(entity, reflected_bundle);
} else {
Comment on lines +125 to +129
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reworking insert_field I realized it would make sense to check if the Bundle being inserted is a single Component, in which case it shouldn't insert each field as if it was itself a bundle/component (which would be wrong). I guess usually ReflectComponent should be checked before ReflectBundle, but if someone made a mistake we should catch it. This won't work though if someone only registers ReflectBundle and not ReflectComponent. It's also a technically a change in behaviour (though I would argue any case where a change is observable was likely behaving wrongly before).

match reflected_bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle
.iter_fields()
.for_each(|field| insert_field(entity, field, registry)),
ReflectRef::Tuple(bundle) => bundle
.iter_fields()
.for_each(|field| insert_field(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
}
}
},
apply_or_insert: |entity, reflected_bundle, registry| {
let mut bundle = entity.world_scope(|world| B::from_world(world));
bundle.apply(reflected_bundle);

match bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle
.iter_fields()
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
ReflectRef::Tuple(bundle) => bundle
.iter_fields()
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
if let Some(reflect_component) =
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
{
reflect_component.apply_or_insert(entity, reflected_bundle, registry);
} else {
match reflected_bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle
.iter_fields()
.for_each(|field| apply_or_insert_field(entity, field, registry)),
ReflectRef::Tuple(bundle) => bundle
.iter_fields()
.for_each(|field| apply_or_insert_field(entity, field, registry)),
_ => panic!(
"expected bundle `{}` to be named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<B>(),
),
}
}
},
remove: |entity| {
Expand All @@ -175,54 +170,58 @@ impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
}
}

fn insert_field<B: 'static>(
entity: &mut EntityWorldMut,
field: &dyn Reflect,
registry: &TypeRegistry,
) {
fn insert_field(entity: &mut EntityWorldMut, field: &dyn Reflect, registry: &TypeRegistry) {
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
reflect_component.apply(entity, field);
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
reflect_bundle.apply(entity, field, registry);
} else {
entity.world_scope(|world| {
if world.components().get_id(TypeId::of::<B>()).is_some() {
panic!(
"no `ReflectComponent` registration found for `{}`",
field.reflect_type_path(),
);
};
});

panic!(
"no `ReflectBundle` registration found for `{}`",
field.reflect_type_path(),
)
let is_component = entity
.world()
.components()
.get_id(field.type_id())
.is_some();

if is_component {
panic!(
"no `ReflectComponent` registration found for `{}`",
field.reflect_type_path(),
);
} else {
panic!(
"no `ReflectBundle` registration found for `{}`",
field.reflect_type_path(),
)
}
}
}

fn apply_or_insert_field<B: 'static>(
fn apply_or_insert_field(
entity: &mut EntityWorldMut,
field: &dyn Reflect,
registry: &TypeRegistry,
) {
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
reflect_component.apply_or_insert(entity, field);
reflect_component.apply_or_insert(entity, field, registry);
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
reflect_bundle.apply_or_insert(entity, field, registry);
} else {
entity.world_scope(|world| {
if world.components().get_id(TypeId::of::<B>()).is_some() {
panic!(
"no `ReflectComponent` registration found for `{}`",
field.reflect_type_path(),
);
};
});

panic!(
"no `ReflectBundle` registration found for `{}`",
field.reflect_type_path(),
)
let is_component = entity
.world()
.components()
.get_id(field.type_id())
.is_some();

if is_component {
panic!(
"no `ReflectComponent` registration found for `{}`",
field.reflect_type_path(),
);
} else {
panic!(
"no `ReflectBundle` registration found for `{}`",
field.reflect_type_path(),
)
}
}
}
Loading