diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index f89302a61d..7f90501545 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -12,7 +12,7 @@ use crate::{ }; 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. /// @@ -27,7 +27,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(EntityMut, &dyn Reflect, &TypeRegistry), /// Function pointer implementing [`ReflectBundle::apply_or_insert()`]. @@ -49,8 +49,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. @@ -117,11 +122,13 @@ impl ReflectBundle { } } -impl FromType for ReflectBundle { +impl FromType 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::(reflected_bundle, world, registry) + }); entity.insert(bundle); }, apply: |mut entity, reflected_bundle, registry| { diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index fdc42141c5..a6b18839d5 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -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, @@ -253,12 +253,12 @@ impl ReflectComponent { } } -impl FromType for ReflectComponent { +impl FromType for ReflectComponent { fn from_type() -> Self { ReflectComponent(ReflectComponentFns { insert: |entity, reflected_component, registry| { let component = entity.world_scope(|world| { - from_reflect_or_world::(reflected_component, world, registry) + from_reflect_with_fallback::(reflected_component, world, registry) }); entity.insert(component); }, @@ -271,7 +271,7 @@ impl FromType for ReflectComponent { component.apply(reflected_component); } else { let component = entity.world_scope(|world| { - from_reflect_or_world::(reflected_component, world, registry) + from_reflect_with_fallback::(reflected_component, world, registry) }); entity.insert(component); } @@ -283,7 +283,7 @@ impl FromType for ReflectComponent { copy: |source_world, destination_world, source_entity, destination_entity, registry| { let source_component = source_world.get::(source_entity).unwrap(); let destination_component = - from_reflect_or_world::(source_component, destination_world, registry); + from_reflect_with_fallback::(source_component, destination_world, registry); destination_world .entity_mut(destination_entity) .insert(destination_component); diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index f51c6960f5..c1dfde5f02 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -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; @@ -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( +/// - 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` 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( 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::(TypeId::of::()) - else { + fn different_type_error(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::(), ); - }; + } - let Ok(mut value) = reflect_from_world.from_world(world).take::() else { + // 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::(TypeId::of::()) + { + // 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::() + .unwrap_or_else(|_| different_type_error::("FromReflect")); + } + } + + // Create an instance of `T` using either the reflected `Default` or `FromWorld`. + let mut value = if let Some(reflect_default) = + registry.get_type_data::(TypeId::of::()) + { + reflect_default + .default() + .take::() + .unwrap_or_else(|_| different_type_error::("Default")) + } else if let Some(reflect_from_world) = + registry.get_type_data::(TypeId::of::()) + { + reflect_from_world + .from_world(world) + .take::() + .unwrap_or_else(|_| different_type_error::("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::(), ); diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index bb109c1917..a537e3b55d 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -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. /// @@ -180,7 +180,7 @@ impl FromType for ReflectResource { fn from_type() -> Self { ReflectResource(ReflectResourceFns { insert: |world, reflected_resource, registry| { - let resource = from_reflect_or_world::(reflected_resource, world, registry); + let resource = from_reflect_with_fallback::(reflected_resource, world, registry); world.insert_resource(resource); }, apply: |world, reflected_resource| { @@ -191,7 +191,8 @@ impl FromType for ReflectResource { if let Some(mut resource) = world.get_resource_mut::() { resource.apply(reflected_resource); } else { - let resource = from_reflect_or_world::(reflected_resource, world, registry); + let resource = + from_reflect_with_fallback::(reflected_resource, world, registry); world.insert_resource(resource); } }, @@ -212,7 +213,7 @@ impl FromType for ReflectResource { copy: |source_world, destination_world, registry| { let source_resource = source_world.resource::(); let destination_resource = - from_reflect_or_world::(source_resource, destination_world, registry); + from_reflect_with_fallback::(source_resource, destination_world, registry); destination_world.insert_resource(destination_resource); }, })