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

Make from_reflect_or_world also try ReflectDefault and improve some comments and panic messages #12499

Merged
merged 2 commits into from Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 14 additions & 7 deletions crates/bevy_ecs/src/reflect/bundle.rs
Expand Up @@ -9,7 +9,7 @@ use std::any::TypeId;
use crate::{prelude::Bundle, world::EntityWorldMut};
use bevy_reflect::{FromReflect, FromType, Reflect, ReflectRef, TypeRegistry};

use super::ReflectComponent;
use super::{from_reflect_with_fallback, ReflectComponent};

/// A struct used to operate on reflected [`Bundle`] trait of a type.
///
Expand All @@ -24,7 +24,7 @@ pub struct ReflectBundle(ReflectBundleFns);
#[derive(Clone)]
pub struct ReflectBundleFns {
/// Function pointer implementing [`ReflectBundle::insert()`].
pub insert: fn(&mut EntityWorldMut, &dyn Reflect),
pub insert: fn(&mut EntityWorldMut, &dyn Reflect, &TypeRegistry),
/// Function pointer implementing [`ReflectBundle::apply()`].
pub apply: fn(&mut EntityWorldMut, &dyn Reflect, &TypeRegistry),
/// Function pointer implementing [`ReflectBundle::apply_or_insert()`].
Expand All @@ -46,8 +46,13 @@ impl ReflectBundleFns {

impl ReflectBundle {
/// 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);
pub fn insert(
&self,
entity: &mut EntityWorldMut,
bundle: &dyn Reflect,
registry: &TypeRegistry,
) {
(self.0.insert)(entity, bundle, registry);
}

/// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value.
Expand Down Expand Up @@ -114,11 +119,13 @@ impl ReflectBundle {
}
}

impl<B: Bundle + Reflect + FromReflect> FromType<B> for ReflectBundle {
impl<B: Bundle + Reflect> FromType<B> for ReflectBundle {
fn from_type() -> Self {
ReflectBundle(ReflectBundleFns {
insert: |entity, reflected_bundle| {
let bundle = B::from_reflect(reflected_bundle).unwrap();
insert: |entity, reflected_bundle, registry| {
let bundle = entity.world_scope(|world| {
from_reflect_with_fallback::<B>(reflected_bundle, world, registry)
});
entity.insert(bundle);
},
apply: |entity, reflected_bundle, registry| {
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ecs/src/reflect/component.rs
Expand Up @@ -57,7 +57,7 @@
//!
//! [`get_type_registration`]: bevy_reflect::GetTypeRegistration::get_type_registration

use super::from_reflect_or_world;
use super::from_reflect_with_fallback;
use crate::{
change_detection::Mut,
component::Component,
Expand Down Expand Up @@ -253,12 +253,12 @@ impl ReflectComponent {
}
}

impl<C: Component + Reflect + FromReflect> FromType<C> for ReflectComponent {
impl<C: Component + Reflect> FromType<C> for ReflectComponent {
fn from_type() -> Self {
ReflectComponent(ReflectComponentFns {
insert: |entity, reflected_component, registry| {
let component = entity.world_scope(|world| {
from_reflect_or_world::<C>(reflected_component, world, registry)
from_reflect_with_fallback::<C>(reflected_component, world, registry)
});
entity.insert(component);
},
Expand All @@ -271,7 +271,7 @@ impl<C: Component + Reflect + FromReflect> FromType<C> for ReflectComponent {
component.apply(reflected_component);
} else {
let component = entity.world_scope(|world| {
from_reflect_or_world::<C>(reflected_component, world, registry)
from_reflect_with_fallback::<C>(reflected_component, world, registry)
});
entity.insert(component);
}
Expand All @@ -283,7 +283,7 @@ impl<C: Component + Reflect + FromReflect> FromType<C> for ReflectComponent {
copy: |source_world, destination_world, source_entity, destination_entity, registry| {
let source_component = source_world.get::<C>(source_entity).unwrap();
let destination_component =
from_reflect_or_world::<C>(source_component, destination_world, registry);
from_reflect_with_fallback::<C>(source_component, destination_world, registry);
destination_world
.entity_mut(destination_entity)
.insert(destination_component);
Expand Down
72 changes: 53 additions & 19 deletions crates/bevy_ecs/src/reflect/mod.rs
Expand Up @@ -5,7 +5,8 @@ use std::ops::{Deref, DerefMut};

use crate as bevy_ecs;
use crate::{system::Resource, world::World};
use bevy_reflect::{FromReflect, Reflect, TypeRegistry, TypeRegistryArc};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::{Reflect, ReflectFromReflect, TypeRegistry, TypeRegistryArc};

mod bundle;
mod component;
Expand Down Expand Up @@ -44,35 +45,68 @@ impl DerefMut for AppTypeRegistry {

/// Creates a `T` from a `&dyn Reflect`.
///
/// The first approach uses `T`'s implementation of `FromReflect`.
/// If this fails, it falls back to default-initializing a new instance of `T` using its
/// `ReflectFromWorld` data from the `world`'s `AppTypeRegistry` and `apply`ing the
/// `&dyn Reflect` on it.
/// This will try the following strategies, in this order:
///
/// Panics if both approaches fail.
fn from_reflect_or_world<T: FromReflect>(
/// - use the reflected `FromReflect`, if it's present and doesn't fail;
/// - use the reflected `Default`, if it's present, and then call `apply` on the result;
/// - use the reflected `FromWorld`, just like the `Default`.
///
/// The first one that is present and doesn't fail will be used.
///
/// # Panics
///
/// If any strategy produces a `Box<dyn Reflect>` that doesn't store a value of type `T`
/// this method will panic.
///
/// If none of the strategies succeed, this method will panic.
fn from_reflect_with_fallback<T: Reflect>(
reflected: &dyn Reflect,
world: &mut World,
registry: &TypeRegistry,
) -> T {
if let Some(value) = T::from_reflect(reflected) {
return value;
}

// Clone the `ReflectFromWorld` because it's cheap and "frees"
// the borrow of `world` so that it can be passed to `from_world`.
let Some(reflect_from_world) = registry.get_type_data::<ReflectFromWorld>(TypeId::of::<T>())
else {
fn different_type_error<T>(reflected: &str) -> ! {
panic!(
"`FromReflect` failed and no `ReflectFromWorld` registration found for `{}`",
"The registration for the reflected `{}` trait for the type `{}` produced \
a value of a different type",
reflected,
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<T>(),
SkiFire13 marked this conversation as resolved.
Show resolved Hide resolved
);
};
}

// First, try `FromReflect`. This is handled differently from the others because
// it doesn't need a subsequent `apply` and may fail.
if let Some(reflect_from_reflect) =
registry.get_type_data::<ReflectFromReflect>(TypeId::of::<T>())
{
// If it fails it's ok, we can continue checking `Default` and `FromWorld`.
if let Some(value) = reflect_from_reflect.from_reflect(reflected) {
return value
.take::<T>()
.unwrap_or_else(|_| different_type_error::<T>("FromReflect"));
}
}

let Ok(mut value) = reflect_from_world.from_world(world).take::<T>() else {
// Create an instance of `T` using either the reflected `Default` or `FromWorld`.
let mut value = if let Some(reflect_default) =
registry.get_type_data::<ReflectDefault>(TypeId::of::<T>())
{
reflect_default
.default()
.take::<T>()
.unwrap_or_else(|_| different_type_error::<T>("Default"))
} else if let Some(reflect_from_world) =
registry.get_type_data::<ReflectFromWorld>(TypeId::of::<T>())
{
reflect_from_world
.from_world(world)
.take::<T>()
.unwrap_or_else(|_| different_type_error::<T>("FromWorld"))
} else {
panic!(
"the `ReflectFromWorld` registration for `{}` produced a value of a different type",
"Couldn't create an instance of `{}` using the reflected `FromReflect`, \
`Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \
or `#[reflect(FromWorld)]`?",
// FIXME: once we have unique reflect, use `TypePath`.
std::any::type_name::<T>(),
SkiFire13 marked this conversation as resolved.
Show resolved Hide resolved
);
Expand Down
9 changes: 5 additions & 4 deletions crates/bevy_ecs/src/reflect/resource.rs
Expand Up @@ -11,7 +11,7 @@ use crate::{
};
use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};

use super::from_reflect_or_world;
use super::from_reflect_with_fallback;

/// A struct used to operate on reflected [`Resource`] of a type.
///
Expand Down Expand Up @@ -180,7 +180,7 @@ impl<R: Resource + FromReflect> FromType<R> for ReflectResource {
fn from_type() -> Self {
ReflectResource(ReflectResourceFns {
insert: |world, reflected_resource, registry| {
let resource = from_reflect_or_world::<R>(reflected_resource, world, registry);
let resource = from_reflect_with_fallback::<R>(reflected_resource, world, registry);
world.insert_resource(resource);
},
apply: |world, reflected_resource| {
Expand All @@ -191,7 +191,8 @@ impl<R: Resource + FromReflect> FromType<R> for ReflectResource {
if let Some(mut resource) = world.get_resource_mut::<R>() {
resource.apply(reflected_resource);
} else {
let resource = from_reflect_or_world::<R>(reflected_resource, world, registry);
let resource =
from_reflect_with_fallback::<R>(reflected_resource, world, registry);
world.insert_resource(resource);
}
},
Expand All @@ -212,7 +213,7 @@ impl<R: Resource + FromReflect> FromType<R> for ReflectResource {
copy: |source_world, destination_world, registry| {
let source_resource = source_world.resource::<R>();
let destination_resource =
from_reflect_or_world::<R>(source_resource, destination_world, registry);
from_reflect_with_fallback::<R>(source_resource, destination_world, registry);
destination_world.insert_resource(destination_resource);
},
})
Expand Down