diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 9788123a4a8fe..de5a933131ca9 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -57,9 +57,7 @@ //! //! [`get_type_registration`]: bevy_reflect::GetTypeRegistration::get_type_registration -use std::any::TypeId; - -use super::ReflectFromWorld; +use super::from_reflect_or_world; use crate::{ change_detection::Mut, component::Component, @@ -310,47 +308,3 @@ impl FromType for ReflectComponent { }) } } - -/// 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. -/// -/// Panics if both approaches fail. -fn from_reflect_or_world( - 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 { - panic!( - "`FromReflect` failed and no `ReflectFromWorld` registration found for `{}`", - // FIXME: once we have unique reflect, use `TypePath`. - std::any::type_name::(), - ); - }; - - let Ok(mut value) = reflect_from_world - .from_world(world) - .into_any() - .downcast::() - else { - panic!( - "the `ReflectFromWorld` registration for `{}` produced a value of a different type", - // FIXME: once we have unique reflect, use `TypePath`. - std::any::type_name::(), - ); - }; - - value.apply(reflected); - *value -} diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index 720fe8dbf5ae6..f51c6960f5954 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -1,10 +1,11 @@ //! Types that enable reflection support. +use std::any::TypeId; use std::ops::{Deref, DerefMut}; use crate as bevy_ecs; -use crate::system::Resource; -use bevy_reflect::TypeRegistryArc; +use crate::{system::Resource, world::World}; +use bevy_reflect::{FromReflect, Reflect, TypeRegistry, TypeRegistryArc}; mod bundle; mod component; @@ -20,7 +21,7 @@ pub use from_world::{ReflectFromWorld, ReflectFromWorldFns}; pub use map_entities::ReflectMapEntities; pub use resource::{ReflectResource, ReflectResourceFns}; -/// A [`Resource`] storing [`TypeRegistry`](bevy_reflect::TypeRegistry) for +/// A [`Resource`] storing [`TypeRegistry`] for /// type registrations relevant to a whole app. #[derive(Resource, Clone, Default)] pub struct AppTypeRegistry(pub TypeRegistryArc); @@ -40,3 +41,43 @@ impl DerefMut for AppTypeRegistry { &mut self.0 } } + +/// 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. +/// +/// Panics if both approaches fail. +fn from_reflect_or_world( + 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 { + panic!( + "`FromReflect` failed and no `ReflectFromWorld` registration found for `{}`", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ); + }; + + let Ok(mut value) = reflect_from_world.from_world(world).take::() else { + panic!( + "the `ReflectFromWorld` registration for `{}` produced a value of a different type", + // FIXME: once we have unique reflect, use `TypePath`. + std::any::type_name::(), + ); + }; + + value.apply(reflected); + value +} diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index a8804fbd2c98d..bb109c19170d7 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -7,9 +7,11 @@ use crate::{ change_detection::Mut, system::Resource, - world::{unsafe_world_cell::UnsafeWorldCell, FromWorld, World}, + world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -use bevy_reflect::{FromType, Reflect}; +use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry}; + +use super::from_reflect_or_world; /// A struct used to operate on reflected [`Resource`] of a type. /// @@ -28,7 +30,7 @@ pub struct ReflectResource(ReflectResourceFns); /// > will not need. /// > Usually a [`ReflectResource`] is created for a type by deriving [`Reflect`] /// > and adding the `#[reflect(Resource)]` attribute. -/// > After adding the component to the [`TypeRegistry`][bevy_reflect::TypeRegistry], +/// > After adding the component to the [`TypeRegistry`], /// > its [`ReflectResource`] can then be retrieved when needed. /// /// Creating a custom [`ReflectResource`] may be useful if you need to create new resource types at @@ -41,11 +43,11 @@ pub struct ReflectResource(ReflectResourceFns); #[derive(Clone)] pub struct ReflectResourceFns { /// Function pointer implementing [`ReflectResource::insert()`]. - pub insert: fn(&mut World, &dyn Reflect), + pub insert: fn(&mut World, &dyn Reflect, &TypeRegistry), /// Function pointer implementing [`ReflectResource::apply()`]. pub apply: fn(&mut World, &dyn Reflect), /// Function pointer implementing [`ReflectResource::apply_or_insert()`]. - pub apply_or_insert: fn(&mut World, &dyn Reflect), + pub apply_or_insert: fn(&mut World, &dyn Reflect, &TypeRegistry), /// Function pointer implementing [`ReflectResource::remove()`]. pub remove: fn(&mut World), /// Function pointer implementing [`ReflectResource::reflect()`]. @@ -56,7 +58,7 @@ pub struct ReflectResourceFns { /// The function may only be called with an [`UnsafeWorldCell`] that can be used to mutably access the relevant resource. pub reflect_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>) -> Option>, /// Function pointer implementing [`ReflectResource::copy()`]. - pub copy: fn(&World, &mut World), + pub copy: fn(&World, &mut World, &TypeRegistry), } impl ReflectResourceFns { @@ -65,15 +67,15 @@ impl ReflectResourceFns { /// /// 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() -> Self { + pub fn new() -> Self { >::from_type().0 } } impl ReflectResource { /// Insert a reflected [`Resource`] into the world like [`insert()`](World::insert_resource). - pub fn insert(&self, world: &mut World, resource: &dyn Reflect) { - (self.0.insert)(world, resource); + pub fn insert(&self, world: &mut World, resource: &dyn Reflect, registry: &TypeRegistry) { + (self.0.insert)(world, resource, registry); } /// Uses reflection to set the value of this [`Resource`] type in the world to the given value. @@ -86,8 +88,13 @@ impl ReflectResource { } /// Uses reflection to set the value of this [`Resource`] type in the world to the given value or insert a new one if it does not exist. - pub fn apply_or_insert(&self, world: &mut World, resource: &dyn Reflect) { - (self.0.apply_or_insert)(world, resource); + pub fn apply_or_insert( + &self, + world: &mut World, + resource: &dyn Reflect, + registry: &TypeRegistry, + ) { + (self.0.apply_or_insert)(world, resource, registry); } /// Removes this [`Resource`] type from the world. Does nothing if it doesn't exist. @@ -124,8 +131,13 @@ impl ReflectResource { /// # Panics /// /// Panics if there is no [`Resource`] of the given type. - pub fn copy(&self, source_world: &World, destination_world: &mut World) { - (self.0.copy)(source_world, destination_world); + pub fn copy( + &self, + source_world: &World, + destination_world: &mut World, + registry: &TypeRegistry, + ) { + (self.0.copy)(source_world, destination_world, registry); } /// Create a custom implementation of [`ReflectResource`]. @@ -164,45 +176,43 @@ impl ReflectResource { } } -impl FromType for ReflectResource { +impl FromType for ReflectResource { fn from_type() -> Self { ReflectResource(ReflectResourceFns { - insert: |world, reflected_resource| { - let mut resource = C::from_world(world); - resource.apply(reflected_resource); + insert: |world, reflected_resource, registry| { + let resource = from_reflect_or_world::(reflected_resource, world, registry); world.insert_resource(resource); }, apply: |world, reflected_resource| { - let mut resource = world.resource_mut::(); + let mut resource = world.resource_mut::(); resource.apply(reflected_resource); }, - apply_or_insert: |world, reflected_resource| { - if let Some(mut resource) = world.get_resource_mut::() { + apply_or_insert: |world, reflected_resource, registry| { + if let Some(mut resource) = world.get_resource_mut::() { resource.apply(reflected_resource); } else { - let mut resource = C::from_world(world); - resource.apply(reflected_resource); + let resource = from_reflect_or_world::(reflected_resource, world, registry); world.insert_resource(resource); } }, remove: |world| { - world.remove_resource::(); + world.remove_resource::(); }, - reflect: |world| world.get_resource::().map(|res| res as &dyn Reflect), + reflect: |world| world.get_resource::().map(|res| res as &dyn Reflect), reflect_unchecked_mut: |world| { // SAFETY: all usages of `reflect_unchecked_mut` guarantee that there is either a single mutable // reference or multiple immutable ones alive at any given point unsafe { - world.get_resource_mut::().map(|res| Mut { + world.get_resource_mut::().map(|res| Mut { value: res.value as &mut dyn Reflect, ticks: res.ticks, }) } }, - copy: |source_world, destination_world| { - let source_resource = source_world.resource::(); - let mut destination_resource = C::from_world(destination_world); - destination_resource.apply(source_resource); + copy: |source_world, destination_world, registry| { + let source_resource = source_world.resource::(); + let destination_resource = + from_reflect_or_world::(source_resource, destination_world, registry); destination_world.insert_resource(destination_resource); }, }) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 6308c7731e009..4f9d9c38bccd1 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -96,7 +96,11 @@ pub struct OnTransition { /// } /// ``` #[derive(Resource, Debug)] -#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] +#[cfg_attr( + feature = "bevy_reflect", + derive(bevy_reflect::Reflect), + reflect(Resource) +)] pub struct State(S); impl State { diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 91254836570a2..2cf9a190e1fd4 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -90,7 +90,7 @@ impl DynamicScene { // If the world already contains an instance of the given resource // just apply the (possibly) new value, otherwise insert the resource - reflect_resource.apply_or_insert(world, &**resource); + reflect_resource.apply_or_insert(world, &**resource, &type_registry); } // For each component types that reference other entities, we keep track diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 30d4d4743fcd3..0bbe86732ca5c 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -89,7 +89,7 @@ impl Scene { type_path: registration.type_info().type_path().to_string(), } })?; - reflect_resource.copy(&self.world, world); + reflect_resource.copy(&self.world, world, &type_registry); } for archetype in self.world.archetypes().iter() {