diff --git a/benches/benches/bevy_reflect/list.rs b/benches/benches/bevy_reflect/list.rs index c6c94ac2f7..fcbe59accd 100644 --- a/benches/benches/bevy_reflect/list.rs +++ b/benches/benches/bevy_reflect/list.rs @@ -10,7 +10,7 @@ use criterion::{ criterion_group!( benches, concrete_list_apply, - concrete_list_clone_dynamic, + concrete_list_to_dynamic_list, dynamic_list_apply, dynamic_list_push ); @@ -81,20 +81,20 @@ fn concrete_list_apply(criterion: &mut Criterion) { list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch); list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { - patch(size).clone_dynamic() + patch(size).to_dynamic_list() }); list_apply(&mut group, "same_len_concrete_patch", full_base, patch); list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| { - patch(size).clone_dynamic() + patch(size).to_dynamic_list() }); group.finish(); } -fn concrete_list_clone_dynamic(criterion: &mut Criterion) { - let mut group = create_group(criterion, bench!("concrete_list_clone_dynamic")); +fn concrete_list_to_dynamic_list(criterion: &mut Criterion) { + let mut group = create_group(criterion, bench!("concrete_list_to_dynamic_list")); for size in SIZES { group.throughput(Throughput::Elements(size as u64)); @@ -105,7 +105,7 @@ fn concrete_list_clone_dynamic(criterion: &mut Criterion) { |bencher, &size| { let v = iter::repeat_n(0, size).collect::>(); - bencher.iter(|| black_box(&v).clone_dynamic()); + bencher.iter(|| black_box(&v).to_dynamic_list()); }, ); } @@ -127,7 +127,7 @@ fn dynamic_list_push(criterion: &mut Criterion) { let dst = DynamicList::default(); bencher.iter_batched( - || (src.clone(), dst.clone_dynamic()), + || (src.clone(), dst.to_dynamic_list()), |(src, mut dst)| { for item in src { dst.push(item); @@ -145,20 +145,20 @@ fn dynamic_list_push(criterion: &mut Criterion) { fn dynamic_list_apply(criterion: &mut Criterion) { let mut group = create_group(criterion, bench!("dynamic_list_apply")); - let empty_base = |_: usize| || Vec::::new().clone_dynamic(); + let empty_base = |_: usize| || Vec::::new().to_dynamic_list(); let full_base = |size: usize| move || iter::repeat_n(0, size).collect::>(); let patch = |size: usize| iter::repeat_n(1, size).collect::>(); list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch); list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { - patch(size).clone_dynamic() + patch(size).to_dynamic_list() }); list_apply(&mut group, "same_len_concrete_patch", full_base, patch); list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| { - patch(size).clone_dynamic() + patch(size).to_dynamic_list() }); group.finish(); diff --git a/benches/benches/bevy_reflect/map.rs b/benches/benches/bevy_reflect/map.rs index cd0ed18847..e35af1c992 100644 --- a/benches/benches/bevy_reflect/map.rs +++ b/benches/benches/bevy_reflect/map.rs @@ -108,7 +108,7 @@ fn concrete_map_apply(criterion: &mut Criterion) { ); map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { - key_range_patch(size).clone_dynamic() + key_range_patch(size).to_dynamic_map() }); map_apply( @@ -122,7 +122,7 @@ fn concrete_map_apply(criterion: &mut Criterion) { &mut group, "same_keys_dynamic_patch", key_range_base, - |size| key_range_patch(size).clone_dynamic(), + |size| key_range_patch(size).to_dynamic_map(), ); map_apply( @@ -136,7 +136,7 @@ fn concrete_map_apply(criterion: &mut Criterion) { &mut group, "disjoint_keys_dynamic_patch", key_range_base, - |size| disjoint_patch(size).clone_dynamic(), + |size| disjoint_patch(size).to_dynamic_map(), ); } @@ -159,7 +159,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) { (0..size as u64) .zip(iter::repeat(0)) .collect::>() - .clone_dynamic() + .to_dynamic_map() } }; @@ -183,7 +183,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) { ); map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { - key_range_patch(size).clone_dynamic() + key_range_patch(size).to_dynamic_map() }); map_apply( @@ -197,7 +197,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) { &mut group, "same_keys_dynamic_patch", key_range_base, - |size| key_range_patch(size).clone_dynamic(), + |size| key_range_patch(size).to_dynamic_map(), ); map_apply( @@ -211,7 +211,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) { &mut group, "disjoint_keys_dynamic_patch", key_range_base, - |size| disjoint_patch(size).clone_dynamic(), + |size| disjoint_patch(size).to_dynamic_map(), ); } diff --git a/benches/benches/bevy_reflect/struct.rs b/benches/benches/bevy_reflect/struct.rs index d8f25554c3..7750213b6d 100644 --- a/benches/benches/bevy_reflect/struct.rs +++ b/benches/benches/bevy_reflect/struct.rs @@ -12,8 +12,8 @@ criterion_group!( concrete_struct_apply, concrete_struct_field, concrete_struct_type_info, - concrete_struct_clone, - dynamic_struct_clone, + concrete_struct_to_dynamic_struct, + dynamic_struct_to_dynamic_struct, dynamic_struct_apply, dynamic_struct_get_field, dynamic_struct_insert, @@ -113,7 +113,7 @@ fn concrete_struct_apply(criterion: &mut Criterion) { bencher.iter_batched( || { let (obj, _) = input(); - let patch = obj.clone_dynamic(); + let patch = obj.to_dynamic_struct(); (obj, patch) }, |(mut obj, patch)| obj.apply(black_box(&patch)), @@ -170,8 +170,8 @@ fn concrete_struct_type_info(criterion: &mut Criterion) { } } -fn concrete_struct_clone(criterion: &mut Criterion) { - let mut group = create_group(criterion, bench!("concrete_struct_clone")); +fn concrete_struct_to_dynamic_struct(criterion: &mut Criterion) { + let mut group = create_group(criterion, bench!("concrete_struct_to_dynamic_struct")); let structs: [(Box, Box); 5] = [ ( @@ -203,28 +203,28 @@ fn concrete_struct_clone(criterion: &mut Criterion) { BenchmarkId::new("NonGeneric", field_count), &standard, |bencher, s| { - bencher.iter(|| s.clone_dynamic()); + bencher.iter(|| s.to_dynamic_struct()); }, ); group.bench_with_input( BenchmarkId::new("Generic", field_count), &generic, |bencher, s| { - bencher.iter(|| s.clone_dynamic()); + bencher.iter(|| s.to_dynamic_struct()); }, ); } } -fn dynamic_struct_clone(criterion: &mut Criterion) { - let mut group = create_group(criterion, bench!("dynamic_struct_clone")); +fn dynamic_struct_to_dynamic_struct(criterion: &mut Criterion) { + let mut group = create_group(criterion, bench!("dynamic_struct_to_dynamic_struct")); let structs: [Box; 5] = [ - Box::new(Struct1::default().clone_dynamic()), - Box::new(Struct16::default().clone_dynamic()), - Box::new(Struct32::default().clone_dynamic()), - Box::new(Struct64::default().clone_dynamic()), - Box::new(Struct128::default().clone_dynamic()), + Box::new(Struct1::default().to_dynamic_struct()), + Box::new(Struct16::default().to_dynamic_struct()), + Box::new(Struct32::default().to_dynamic_struct()), + Box::new(Struct64::default().to_dynamic_struct()), + Box::new(Struct128::default().to_dynamic_struct()), ]; for s in structs { @@ -234,7 +234,7 @@ fn dynamic_struct_clone(criterion: &mut Criterion) { BenchmarkId::from_parameter(field_count), &s, |bencher, s| { - bencher.iter(|| s.clone_dynamic()); + bencher.iter(|| s.to_dynamic_struct()); }, ); } @@ -265,7 +265,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) { &patch, |bencher, patch| { bencher.iter_batched( - || (base.clone_dynamic(), patch()), + || (base.to_dynamic_struct(), patch()), |(mut base, patch)| base.apply(black_box(&*patch)), BatchSize::SmallInput, ); @@ -289,7 +289,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) { } bencher.iter_batched( - || base.clone_dynamic(), + || base.to_dynamic_struct(), |mut base| base.apply(black_box(&patch)), BatchSize::SmallInput, ); @@ -315,7 +315,7 @@ fn dynamic_struct_insert(criterion: &mut Criterion) { let field = format!("field_{}", field_count); bencher.iter_batched( - || s.clone_dynamic(), + || s.to_dynamic_struct(), |mut s| { s.insert(black_box(&field), ()); }, diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index f2401c951b..8c49fab226 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -658,7 +658,7 @@ mod tests { assert_eq!(UntypedHandle::from(typed.clone()), untyped); } - /// `Reflect::clone_value` should increase the strong count of a strong handle + /// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle #[test] fn strong_handle_reflect_clone() { use crate::{AssetApp, AssetPlugin, Assets, VisitAssetDependencies}; @@ -689,7 +689,7 @@ mod tests { ); let reflected: &dyn Reflect = &handle; - let cloned_handle: Box = reflected.clone_value(); + let _cloned_handle: Box = reflected.reflect_clone().unwrap(); assert_eq!( Arc::strong_count(strong), @@ -697,10 +697,18 @@ mod tests { "Cloning the handle with reflect should increase the strong count to 2" ); - let from_reflect_handle: Handle = - FromReflect::from_reflect(&*cloned_handle).unwrap(); + let dynamic_handle: Box = reflected.to_dynamic(); - assert_eq!(Arc::strong_count(strong), 3, "Converting the reflected value back to a handle should increase the strong count to 3"); + assert_eq!( + Arc::strong_count(strong), + 3, + "Converting the handle to a dynamic should increase the strong count to 3" + ); + + let from_reflect_handle: Handle = + FromReflect::from_reflect(&*dynamic_handle).unwrap(); + + assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4"); assert!( from_reflect_handle.is_strong(), "The cloned handle should still be strong" diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index bb543ec0e1..fdbe4c7a57 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -2911,12 +2911,14 @@ pub fn component_clone_via_clone( /// - Component has [`TypeId`] /// - Component is registered /// - Component has [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) registered -/// - Component has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect), +/// - Component can be cloned via [`PartialReflect::reflect_clone`] _or_ has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect), /// [`ReflectDefault`](bevy_reflect::std_traits::ReflectDefault), [`ReflectFromWorld`](crate::reflect::ReflectFromWorld) /// /// If any of the conditions is not satisfied, the component will be skipped. /// /// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details. +/// +/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone #[cfg(feature = "bevy_reflect")] pub fn component_clone_via_reflect( commands: &mut Commands, @@ -2934,6 +2936,21 @@ pub fn component_clone_via_reflect( // checked in read_source_component_reflect let type_id = component_info.type_id().unwrap(); + // Try to clone using `reflect_clone` + if let Ok(mut component) = source_component_reflect.reflect_clone() { + if let Some(reflect_component) = + registry.get_type_data::(type_id) + { + reflect_component.visit_entities_mut(&mut *component, &mut |entity| { + *entity = ctx.entity_mapper().get_mapped(*entity); + }); + } + drop(registry); + + ctx.write_target_component_reflect(component); + return; + } + // Try to clone using ReflectFromReflect if let Some(reflect_from_reflect) = registry.get_type_data::(type_id) @@ -2977,7 +2994,7 @@ pub fn component_clone_via_reflect( mapped_entities.push(entity); }); } - let source_component_cloned = source_component_reflect.clone_value(); + let source_component_cloned = source_component_reflect.to_dynamic(); let component_layout = component_info.layout(); let target = ctx.target(); let component_id = ctx.component_id(); @@ -3008,7 +3025,11 @@ pub fn component_clone_via_reflect( world .entity_mut(target) .insert_by_id(component_id, OwningPtr::new(raw_component_ptr)); - alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout); + + if component_layout.size() > 0 { + // Ensure we don't attempt to deallocate zero-sized components + alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout); + } } }); } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b9564c1352..7759af2f35 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -256,7 +256,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { ); self.bundle_scratch .push_ptr(self.component_id, PtrMut::new(target_component_data_ptr)); - alloc::alloc::dealloc(component_data_ptr, component_layout); + + if component_layout.size() > 0 { + // Ensure we don't attempt to deallocate zero-sized components + alloc::alloc::dealloc(component_data_ptr, component_layout); + } } self.target_component_written = true; @@ -848,6 +852,7 @@ mod tests { use alloc::vec::Vec; use bevy_ptr::OwningPtr; use bevy_reflect::Reflect; + use core::marker::PhantomData; use core::{alloc::Layout, ops::Deref}; #[cfg(feature = "bevy_reflect")] @@ -888,67 +893,95 @@ mod tests { assert!(world.get::(e_clone).is_some_and(|c| *c == component)); } - // TODO: remove this when https://github.com/bevyengine/bevy/pull/13432 lands #[test] fn clone_entity_using_reflect_all_paths() { - // `ReflectDefault`-based fast path + #[derive(PartialEq, Eq, Default, Debug)] + struct NotClone; + + // `reflect_clone`-based fast path #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] - #[reflect(Default)] #[reflect(from_reflect = false)] struct A { field: usize, field2: Vec, } - // `ReflectFromReflect`-based fast path + // `ReflectDefault`-based fast path #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + #[reflect(Default)] + #[reflect(from_reflect = false)] struct B { field: usize, field2: Vec, + #[reflect(ignore)] + ignored: NotClone, + } + + // `ReflectFromReflect`-based fast path + #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] + struct C { + field: usize, + field2: Vec, + #[reflect(ignore)] + ignored: NotClone, } // `ReflectFromWorld`-based fast path #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] #[reflect(FromWorld)] #[reflect(from_reflect = false)] - struct C { + struct D { field: usize, field2: Vec, + #[reflect(ignore)] + ignored: NotClone, } let mut world = World::default(); world.init_resource::(); let registry = world.get_resource::().unwrap(); - registry.write().register::<(A, B, C)>(); + registry.write().register::<(A, B, C, D)>(); let a_id = world.register_component::(); let b_id = world.register_component::(); let c_id = world.register_component::(); + let d_id = world.register_component::(); let component_a = A { field: 5, field2: vec![1, 2, 3, 4, 5], }; let component_b = B { - field: 6, + field: 5, field2: vec![1, 2, 3, 4, 5], + ignored: NotClone, }; let component_c = C { + field: 6, + field2: vec![1, 2, 3, 4, 5], + ignored: NotClone, + }; + let component_d = D { field: 7, field2: vec![1, 2, 3, 4, 5], + ignored: NotClone, }; - let e = world.spawn((component_a, component_b, component_c)).id(); + let e = world + .spawn((component_a, component_b, component_c, component_d)) + .id(); let e_clone = world.spawn_empty().id(); EntityCloner::build(&mut world) .override_clone_behavior_with_id(a_id, ComponentCloneBehavior::reflect()) .override_clone_behavior_with_id(b_id, ComponentCloneBehavior::reflect()) .override_clone_behavior_with_id(c_id, ComponentCloneBehavior::reflect()) + .override_clone_behavior_with_id(d_id, ComponentCloneBehavior::reflect()) .clone_entity(e, e_clone); assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); + assert_eq!(world.get::(e_clone), Some(world.get::(e).unwrap())); } #[test] @@ -1025,16 +1058,16 @@ mod tests { #[derive(Component, PartialEq, Eq, Default, Debug)] struct A; - // No valid type data + // No valid type data and not `reflect_clone`-able #[derive(Component, Reflect, PartialEq, Eq, Default, Debug)] #[reflect(Component)] #[reflect(from_reflect = false)] - struct B; + struct B(#[reflect(ignore)] PhantomData<()>); let mut world = World::default(); // No AppTypeRegistry - let e = world.spawn((A, B)).id(); + let e = world.spawn((A, B(Default::default()))).id(); let e_clone = world.spawn_empty().id(); EntityCloner::build(&mut world) .override_clone_behavior::(ComponentCloneBehavior::reflect()) @@ -1048,7 +1081,7 @@ mod tests { let registry = world.get_resource::().unwrap(); registry.write().register::(); - let e = world.spawn((A, B)).id(); + let e = world.spawn((A, B(Default::default()))).id(); let e_clone = world.spawn_empty().id(); EntityCloner::build(&mut world).clone_entity(e, e_clone); assert_eq!(world.get::(e_clone), None); @@ -1347,7 +1380,11 @@ mod tests { fn clone_with_reflect_from_world() { #[derive(Component, Reflect, PartialEq, Eq, Debug)] #[reflect(Component, FromWorld, from_reflect = false)] - struct SomeRef(#[entities] Entity); + struct SomeRef( + #[entities] Entity, + // We add an ignored field here to ensure `reflect_clone` fails and `FromWorld` is used + #[reflect(ignore)] PhantomData<()>, + ); #[derive(Resource)] struct FromWorldCalled(bool); @@ -1355,7 +1392,7 @@ mod tests { impl FromWorld for SomeRef { fn from_world(world: &mut World) -> Self { world.insert_resource(FromWorldCalled(true)); - SomeRef(Entity::PLACEHOLDER) + SomeRef(Entity::PLACEHOLDER, Default::default()) } } let mut world = World::new(); @@ -1365,14 +1402,17 @@ mod tests { let a = world.spawn_empty().id(); let b = world.spawn_empty().id(); - let c = world.spawn(SomeRef(a)).id(); + let c = world.spawn(SomeRef(a, Default::default())).id(); let d = world.spawn_empty().id(); let mut map = EntityHashMap::::new(); map.insert(a, b); map.insert(c, d); let cloned = EntityCloner::default().clone_entity_mapped(&mut world, c, &mut map); - assert_eq!(*world.entity(cloned).get::().unwrap(), SomeRef(b)); + assert_eq!( + *world.entity(cloned).get::().unwrap(), + SomeRef(b, Default::default()) + ); assert!(world.resource::().0); } } diff --git a/crates/bevy_ecs/src/reflect/entity_commands.rs b/crates/bevy_ecs/src/reflect/entity_commands.rs index 725c61265f..20c5e16c6d 100644 --- a/crates/bevy_ecs/src/reflect/entity_commands.rs +++ b/crates/bevy_ecs/src/reflect/entity_commands.rs @@ -82,7 +82,7 @@ pub trait ReflectCommandExt { /// // use the insert_reflect entity command to insert that component/bundle into an entity. /// commands /// .spawn_empty() - /// .insert_reflect(prefab.data.clone_value()); + /// .insert_reflect(prefab.data.reflect_clone().unwrap().into_partial_reflect()); /// } /// ``` fn insert_reflect(&mut self, component: Box) -> &mut Self; @@ -442,21 +442,30 @@ mod tests { let entity = commands.spawn_empty().id(); let entity2 = commands.spawn_empty().id(); + let entity3 = commands.spawn_empty().id(); let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box; - let boxed_reflect_component_a_clone = boxed_reflect_component_a.clone_value(); + let boxed_reflect_component_a_clone = boxed_reflect_component_a.reflect_clone().unwrap(); + let boxed_reflect_component_a_dynamic = boxed_reflect_component_a.to_dynamic(); commands .entity(entity) .insert_reflect(boxed_reflect_component_a); commands .entity(entity2) - .insert_reflect(boxed_reflect_component_a_clone); + .insert_reflect(boxed_reflect_component_a_clone.into_partial_reflect()); + commands + .entity(entity3) + .insert_reflect(boxed_reflect_component_a_dynamic); system_state.apply(&mut world); assert_eq!( world.entity(entity).get::(), - world.entity(entity2).get::() + world.entity(entity2).get::(), + ); + assert_eq!( + world.entity(entity).get::(), + world.entity(entity3).get::(), ); } diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index cec99a23ce..3cbd8cce95 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -175,7 +175,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream } } - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicEnum { + fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum { #bevy_reflect_path::DynamicEnum::from_ref::(self) } } @@ -186,11 +186,6 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream #FQOption::Some(::type_info()) } - #[inline] - fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box { - #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::Enum::clone_dynamic(self)) - } - #[inline] fn try_apply( &mut self, diff --git a/crates/bevy_reflect/derive/src/impls/opaque.rs b/crates/bevy_reflect/derive/src/impls/opaque.rs index 11893a62e9..2a08cadc28 100644 --- a/crates/bevy_reflect/derive/src/impls/opaque.rs +++ b/crates/bevy_reflect/derive/src/impls/opaque.rs @@ -78,7 +78,7 @@ pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream { } #[inline] - fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box { + fn to_dynamic(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#FQClone::clone(self)) } diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 9487fbc6e7..7e10de3f2b 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -120,10 +120,10 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #bevy_reflect_path::FieldIter::new(self) } - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + fn to_dynamic_struct(&self) -> #bevy_reflect_path::DynamicStruct { let mut dynamic: #bevy_reflect_path::DynamicStruct = #FQDefault::default(); dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self)); - #(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)* + #(dynamic.insert_boxed(#field_names, #bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)* dynamic } } @@ -134,11 +134,6 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #FQOption::Some(::type_info()) } - #[inline] - fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box { - #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) - } - #[inline] fn try_apply( &mut self, diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index 753412b4b9..90c3555230 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -87,10 +87,10 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: #bevy_reflect_path::TupleStructFieldIter::new(self) } - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { + fn to_dynamic_tuple_struct(&self) -> #bevy_reflect_path::DynamicTupleStruct { let mut dynamic: #bevy_reflect_path::DynamicTupleStruct = #FQDefault::default(); dynamic.set_represented_type(#bevy_reflect_path::PartialReflect::get_represented_type_info(self)); - #(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::clone_value(#fields_ref));)* + #(dynamic.insert_boxed(#bevy_reflect_path::PartialReflect::to_dynamic(#fields_ref));)* dynamic } } @@ -100,10 +100,6 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { #FQOption::Some(::type_info()) } - #[inline] - fn clone_value(&self) -> #bevy_reflect_path::__macro_exports::alloc_utils::Box { - #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) - } #[inline] fn try_apply( diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 300e69c2a7..9ad906cfce 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -69,10 +69,16 @@ pub trait Array: PartialReflect { fn drain(self: Box) -> Vec>; /// Clones the list, producing a [`DynamicArray`]. + #[deprecated(since = "0.16.0", note = "use `to_dynamic_array` instead")] fn clone_dynamic(&self) -> DynamicArray { + self.to_dynamic_array() + } + + /// Creates a new [`DynamicArray`] from this array. + fn to_dynamic_array(&self) -> DynamicArray { DynamicArray { represented_type: self.get_represented_type_info(), - values: self.iter().map(PartialReflect::clone_value).collect(), + values: self.iter().map(PartialReflect::to_dynamic).collect(), } } @@ -256,11 +262,6 @@ impl PartialReflect for DynamicArray { ReflectOwned::Array(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - #[inline] fn reflect_hash(&self) -> Option { array_hash(self) @@ -307,18 +308,6 @@ impl Array for DynamicArray { fn drain(self: Box) -> Vec> { self.values.into_vec() } - - #[inline] - fn clone_dynamic(&self) -> DynamicArray { - DynamicArray { - represented_type: self.represented_type, - values: self - .values - .iter() - .map(|value| value.clone_value()) - .collect(), - } - } } impl FromIterator> for DynamicArray { diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index d9e228c07e..a968b311f2 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -23,8 +23,8 @@ impl Clone for DynamicVariant { fn clone(&self) -> Self { match self { DynamicVariant::Unit => DynamicVariant::Unit, - DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.clone_dynamic()), - DynamicVariant::Struct(data) => DynamicVariant::Struct(data.clone_dynamic()), + DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.to_dynamic_tuple()), + DynamicVariant::Struct(data) => DynamicVariant::Struct(data.to_dynamic_struct()), } } } @@ -150,7 +150,7 @@ impl DynamicEnum { /// Create a [`DynamicEnum`] from an existing one. /// /// This is functionally the same as [`DynamicEnum::from`] except it takes a reference. - pub fn from_ref(value: &TEnum) -> Self { + pub fn from_ref(value: &TEnum) -> Self { let type_info = value.get_represented_type_info(); let mut dyn_enum = match value.variant_type() { VariantType::Unit => DynamicEnum::new_with_index( @@ -161,7 +161,7 @@ impl DynamicEnum { VariantType::Tuple => { let mut data = DynamicTuple::default(); for field in value.iter_fields() { - data.insert_boxed(field.value().clone_value()); + data.insert_boxed(field.value().to_dynamic()); } DynamicEnum::new_with_index( value.variant_index(), @@ -173,7 +173,7 @@ impl DynamicEnum { let mut data = DynamicStruct::default(); for field in value.iter_fields() { let name = field.name().unwrap(); - data.insert_boxed(name, field.value().clone_value()); + data.insert_boxed(name, field.value().to_dynamic()); } DynamicEnum::new_with_index( value.variant_index(), @@ -339,14 +339,14 @@ impl PartialReflect for DynamicEnum { VariantType::Tuple => { let mut dyn_tuple = DynamicTuple::default(); for field in value.iter_fields() { - dyn_tuple.insert_boxed(field.value().clone_value()); + dyn_tuple.insert_boxed(field.value().to_dynamic()); } DynamicVariant::Tuple(dyn_tuple) } VariantType::Struct => { let mut dyn_struct = DynamicStruct::default(); for field in value.iter_fields() { - dyn_struct.insert_boxed(field.name().unwrap(), field.value().clone_value()); + dyn_struct.insert_boxed(field.name().unwrap(), field.value().to_dynamic()); } DynamicVariant::Struct(dyn_struct) } @@ -377,11 +377,6 @@ impl PartialReflect for DynamicEnum { ReflectOwned::Enum(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - #[inline] fn reflect_hash(&self) -> Option { enum_hash(self) diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index d3f6550640..4df7aea527 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -125,7 +125,14 @@ pub trait Enum: PartialReflect { /// The type of the current variant. fn variant_type(&self) -> VariantType; // Clones the enum into a [`DynamicEnum`]. - fn clone_dynamic(&self) -> DynamicEnum; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_enum` instead")] + fn clone_dynamic(&self) -> DynamicEnum { + self.to_dynamic_enum() + } + /// Creates a new [`DynamicEnum`] from this enum. + fn to_dynamic_enum(&self) -> DynamicEnum { + DynamicEnum::from_ref(self) + } /// Returns true if the current variant's type matches the given one. fn is_variant(&self, variant_type: VariantType) -> bool { self.variant_type() == variant_type diff --git a/crates/bevy_reflect/src/func/dynamic_function.rs b/crates/bevy_reflect/src/func/dynamic_function.rs index 6d1c9776a9..9253921112 100644 --- a/crates/bevy_reflect/src/func/dynamic_function.rs +++ b/crates/bevy_reflect/src/func/dynamic_function.rs @@ -358,7 +358,7 @@ impl Function for DynamicFunction<'static> { self.call(args) } - fn clone_dynamic(&self) -> DynamicFunction<'static> { + fn to_dynamic_function(&self) -> DynamicFunction<'static> { self.clone() } } @@ -395,7 +395,7 @@ impl PartialReflect for DynamicFunction<'static> { fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { match value.reflect_ref() { ReflectRef::Function(func) => { - *self = func.clone_dynamic(); + *self = func.to_dynamic_function(); Ok(()) } _ => Err(ApplyError::MismatchedTypes { @@ -421,10 +421,6 @@ impl PartialReflect for DynamicFunction<'static> { ReflectOwned::Function(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - fn reflect_hash(&self) -> Option { None } @@ -562,14 +558,14 @@ mod tests { assert_eq!(greet.name().unwrap(), "greet"); assert_eq!(clone.name().unwrap(), "greet"); - let clone_value = clone + let cloned_value = clone .call(ArgList::default().with_ref(&String::from("world"))) .unwrap() .unwrap_owned() .try_take::() .unwrap(); - assert_eq!(clone_value, "Hello, world!"); + assert_eq!(cloned_value, "Hello, world!"); } #[test] diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 2ae8c1877b..eb770e9e50 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -64,7 +64,13 @@ pub trait Function: PartialReflect + Debug { fn reflect_call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a>; /// Clone this function into a [`DynamicFunction`]. - fn clone_dynamic(&self) -> DynamicFunction<'static>; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_function` instead")] + fn clone_dynamic(&self) -> DynamicFunction<'static> { + self.to_dynamic_function() + } + + /// Creates a new [`DynamicFunction`] from this function. + fn to_dynamic_function(&self) -> DynamicFunction<'static>; } #[cfg(test)] diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 495abcbd80..942bcbe83f 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -134,10 +134,6 @@ where ReflectOwned::List(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new( self.iter() diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index f364389449..54d8393e7a 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -9,12 +9,11 @@ use crate::{ reflect::impl_full_reflect, set_apply, set_partial_eq, set_try_apply, utility::{reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell}, - ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet, DynamicTypePath, FromReflect, - FromType, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, - MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectCloneError, ReflectDeserialize, - ReflectFromPtr, ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, - ReflectSerialize, Set, SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, - TypeRegistry, Typed, + ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicTypePath, FromReflect, FromType, + Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, MaybeTyped, + OpaqueInfo, PartialReflect, Reflect, ReflectCloneError, ReflectDeserialize, ReflectFromPtr, + ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, + SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, TypeRegistry, Typed, }; use alloc::{ borrow::{Cow, ToOwned}, @@ -409,10 +408,6 @@ macro_rules! impl_reflect_for_atomic { fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { Some(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(<$ty>::new(self.load($ordering))) - } #[inline] fn reflect_clone(&self) -> Result, ReflectCloneError> { @@ -626,10 +621,6 @@ macro_rules! impl_reflect_for_veclike { ReflectOwned::List(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new( self.iter() @@ -779,7 +770,7 @@ macro_rules! impl_reflect_for_hashmap { .collect() } - fn clone_dynamic(&self) -> DynamicMap { + fn to_dynamic_map(&self) -> DynamicMap { let mut dynamic_map = DynamicMap::default(); dynamic_map.set_represented_type(self.get_represented_type_info()); for (k, v) in self { @@ -789,7 +780,7 @@ macro_rules! impl_reflect_for_hashmap { k.reflect_type_path() ) }); - dynamic_map.insert_boxed(Box::new(key), v.clone_value()); + dynamic_map.insert_boxed(Box::new(key), v.to_dynamic()); } dynamic_map } @@ -880,10 +871,6 @@ macro_rules! impl_reflect_for_hashmap { ReflectOwned::Map(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { let mut map = Self::with_capacity_and_hasher(self.len(), S::default()); for (key, value) in self.iter() { @@ -1043,15 +1030,6 @@ macro_rules! impl_reflect_for_hashset { .collect() } - fn clone_dynamic(&self) -> DynamicSet { - let mut dynamic_set = DynamicSet::default(); - dynamic_set.set_represented_type(self.get_represented_type_info()); - for v in self { - dynamic_set.insert_boxed(v.clone_value()); - } - dynamic_set - } - fn insert_boxed(&mut self, value: Box) -> bool { let value = V::take_from_reflect(value).unwrap_or_else(|value| { panic!( @@ -1146,10 +1124,6 @@ macro_rules! impl_reflect_for_hashset { ReflectOwned::Set(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { let mut set = Self::with_capacity_and_hasher(self.len(), S::default()); for value in self.iter() { @@ -1321,7 +1295,7 @@ where k.reflect_type_path() ) }); - dynamic_map.insert_boxed(Box::new(key), v.clone_value()); + dynamic_map.insert_boxed(Box::new(key), v.to_dynamic()); } dynamic_map } @@ -1407,10 +1381,6 @@ where ReflectOwned::Map(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { let mut map = Self::new(); for (key, value) in self.iter() { @@ -1596,11 +1566,6 @@ impl P ReflectOwned::Array(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - #[inline] fn reflect_hash(&self) -> Option { crate::array_hash(self) @@ -1795,10 +1760,6 @@ impl PartialReflect for Cow<'static, str> { ReflectOwned::Opaque(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(self.clone())) } @@ -1987,10 +1948,6 @@ impl Parti ReflectOwned::List(self) } - fn clone_value(&self) -> Box { - Box::new(List::clone_dynamic(self)) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(self.clone())) } @@ -2100,10 +2057,6 @@ impl PartialReflect for &'static str { ReflectOwned::Opaque(self) } - fn clone_value(&self) -> Box { - Box::new(*self) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(*self)) } @@ -2243,10 +2196,6 @@ impl PartialReflect for &'static Path { ReflectOwned::Opaque(self) } - fn clone_value(&self) -> Box { - Box::new(*self) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(*self)) } @@ -2386,10 +2335,6 @@ impl PartialReflect for Cow<'static, Path> { ReflectOwned::Opaque(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(self.clone())) } @@ -2548,10 +2493,6 @@ impl PartialReflect for &'static Location<'static> { ReflectOwned::Opaque(self) } - fn clone_value(&self) -> Box { - Box::new(*self) - } - fn reflect_clone(&self) -> Result, ReflectCloneError> { Ok(Box::new(*self)) } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 25a7e3a0c5..c3ad8c6bd6 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -204,8 +204,8 @@ //! //! They are most commonly used as "proxies" for other types, //! where they contain the same data as— and therefore, represent— a concrete type. -//! The [`PartialReflect::clone_value`] method will return a dynamic type for all non-opaque types, -//! allowing all types to essentially be "cloned". +//! The [`PartialReflect::to_dynamic`] method will return a dynamic type for all non-opaque types, +//! allowing all types to essentially be "cloned" into a dynamic type. //! And since dynamic types themselves implement [`PartialReflect`], //! we may pass them around just like most other reflected types. //! @@ -219,9 +219,9 @@ //! foo: 123 //! }); //! -//! // `cloned` will be a `DynamicStruct` representing a `MyStruct` -//! let cloned: Box = original.clone_value(); -//! assert!(cloned.represents::()); +//! // `dynamic` will be a `DynamicStruct` representing a `MyStruct` +//! let dynamic: Box = original.to_dynamic(); +//! assert!(dynamic.represents::()); //! ``` //! //! ## Patching @@ -253,8 +253,8 @@ //! foo: 123 //! }); //! -//! let cloned: Box = original.clone_value(); -//! let value = cloned.try_take::().unwrap(); // PANIC! +//! let dynamic: Box = original.to_dynamic(); +//! let value = dynamic.try_take::().unwrap(); // PANIC! //! ``` //! //! To resolve this issue, we'll need to convert the dynamic type to the concrete one. @@ -278,8 +278,8 @@ //! foo: 123 //! }); //! -//! let cloned: Box = original.clone_value(); -//! let value = ::from_reflect(&*cloned).unwrap(); // OK! +//! let dynamic: Box = original.to_dynamic(); +//! let value = ::from_reflect(&*dynamic).unwrap(); // OK! //! ``` //! //! When deriving, all active fields and sub-elements must also implement `FromReflect`. @@ -945,7 +945,7 @@ mod tests { let foo = Foo { a: 1 }; assert!(foo.reflect_hash().is_some()); - let dynamic = foo.clone_dynamic(); + let dynamic = foo.to_dynamic_struct(); let mut map = DynamicMap::default(); map.insert(dynamic, 11u32); @@ -1498,7 +1498,7 @@ mod tests { list.push(3isize); list.push(4isize); list.push(5isize); - foo_patch.insert("c", list.clone_dynamic()); + foo_patch.insert("c", list.to_dynamic_list()); let mut map = DynamicMap::default(); map.insert(2usize, 3i8); @@ -1507,7 +1507,7 @@ mod tests { let mut bar_patch = DynamicStruct::default(); bar_patch.insert("x", 2u32); - foo_patch.insert("e", bar_patch.clone_dynamic()); + foo_patch.insert("e", bar_patch.to_dynamic_struct()); let mut tuple = DynamicTuple::default(); tuple.insert(2i32); @@ -1834,22 +1834,22 @@ mod tests { #[test] fn not_dynamic_names() { let list = Vec::::new(); - let dyn_list = list.clone_dynamic(); + let dyn_list = list.to_dynamic_list(); assert_ne!(dyn_list.reflect_type_path(), Vec::::type_path()); let array = [b'0'; 4]; - let dyn_array = array.clone_dynamic(); + let dyn_array = array.to_dynamic_array(); assert_ne!(dyn_array.reflect_type_path(), <[u8; 4]>::type_path()); let map = HashMap::::default(); - let dyn_map = map.clone_dynamic(); + let dyn_map = map.to_dynamic_map(); assert_ne!( dyn_map.reflect_type_path(), HashMap::::type_path() ); let tuple = (0usize, "1".to_string(), 2.0f32); - let mut dyn_tuple = tuple.clone_dynamic(); + let mut dyn_tuple = tuple.to_dynamic_tuple(); dyn_tuple.insert::(3); assert_ne!( dyn_tuple.reflect_type_path(), @@ -1861,13 +1861,13 @@ mod tests { a: usize, } let struct_ = TestStruct { a: 0 }; - let dyn_struct = struct_.clone_dynamic(); + let dyn_struct = struct_.to_dynamic_struct(); assert_ne!(dyn_struct.reflect_type_path(), TestStruct::type_path()); #[derive(Reflect)] struct TestTupleStruct(usize); let tuple_struct = TestTupleStruct(0); - let dyn_tuple_struct = tuple_struct.clone_dynamic(); + let dyn_tuple_struct = tuple_struct.to_dynamic_tuple_struct(); assert_ne!( dyn_tuple_struct.reflect_type_path(), TestTupleStruct::type_path() @@ -2252,7 +2252,7 @@ mod tests { #[test] fn should_permit_valid_represented_type_for_dynamic() { let type_info = <[i32; 2] as Typed>::type_info(); - let mut dynamic_array = [123; 2].clone_dynamic(); + let mut dynamic_array = [123; 2].to_dynamic_array(); dynamic_array.set_represented_type(Some(type_info)); } @@ -2260,7 +2260,7 @@ mod tests { #[should_panic(expected = "expected TypeInfo::Array but received")] fn should_prohibit_invalid_represented_type_for_dynamic() { let type_info = <(i32, i32) as Typed>::type_info(); - let mut dynamic_array = [123; 2].clone_dynamic(); + let mut dynamic_array = [123; 2].to_dynamic_array(); dynamic_array.set_represented_type(Some(type_info)); } @@ -2800,7 +2800,7 @@ bevy_reflect::tests::Test { map, value: 12, } - .clone_dynamic(); + .to_dynamic_struct(); // test unknown DynamicStruct let mut test_unknown_struct = DynamicStruct::default(); diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index e0f019c4b2..2e1c085676 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -104,10 +104,16 @@ pub trait List: PartialReflect { fn drain(&mut self) -> Vec>; /// Clones the list, producing a [`DynamicList`]. + #[deprecated(since = "0.16.0", note = "use `to_dynamic_list` instead")] fn clone_dynamic(&self) -> DynamicList { + self.to_dynamic_list() + } + + /// Creates a new [`DynamicList`] from this list. + fn to_dynamic_list(&self) -> DynamicList { DynamicList { represented_type: self.get_represented_type_info(), - values: self.iter().map(PartialReflect::clone_value).collect(), + values: self.iter().map(PartialReflect::to_dynamic).collect(), } } @@ -246,17 +252,6 @@ impl List for DynamicList { fn drain(&mut self) -> Vec> { self.values.drain(..).collect() } - - fn clone_dynamic(&self) -> DynamicList { - DynamicList { - represented_type: self.represented_type, - values: self - .values - .iter() - .map(|value| value.clone_value()) - .collect(), - } - } } impl PartialReflect for DynamicList { @@ -320,11 +315,6 @@ impl PartialReflect for DynamicList { ReflectOwned::List(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - #[inline] fn reflect_hash(&self) -> Option { list_hash(self) @@ -470,7 +460,7 @@ pub fn list_try_apply(a: &mut L, b: &dyn PartialReflect) -> Result<(), v.try_apply(value)?; } } else { - List::push(a, value.clone_value()); + List::push(a, value.to_dynamic()); } } diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 1ce685b127..0b18132fba 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -82,7 +82,20 @@ pub trait Map: PartialReflect { fn drain(&mut self) -> Vec<(Box, Box)>; /// Clones the map, producing a [`DynamicMap`]. - fn clone_dynamic(&self) -> DynamicMap; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_map` instead")] + fn clone_dynamic(&self) -> DynamicMap { + self.to_dynamic_map() + } + + /// Creates a new [`DynamicMap`] from this map. + fn to_dynamic_map(&self) -> DynamicMap { + let mut map = DynamicMap::default(); + map.set_represented_type(self.get_represented_type_info()); + for (key, value) in self.iter() { + map.insert_boxed(key.to_dynamic(), value.to_dynamic()); + } + map + } /// Inserts a key-value pair into the map. /// @@ -302,18 +315,6 @@ impl Map for DynamicMap { self.values.drain(..).collect() } - fn clone_dynamic(&self) -> DynamicMap { - DynamicMap { - represented_type: self.represented_type, - values: self - .values - .iter() - .map(|(key, value)| (key.clone_value(), value.clone_value())) - .collect(), - indices: self.indices.clone(), - } - } - fn insert_boxed( &mut self, key: Box, @@ -431,10 +432,6 @@ impl PartialReflect for DynamicMap { ReflectOwned::Map(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { map_partial_eq(self, value) } @@ -620,7 +617,7 @@ pub fn map_try_apply(a: &mut M, b: &dyn PartialReflect) -> Result<(), Ap if let Some(a_value) = a.get_mut(key) { a_value.try_apply(b_value)?; } else { - a.insert_boxed(key.clone_value(), b_value.clone_value()); + a.insert_boxed(key.to_dynamic(), b_value.to_dynamic()); } } diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index e1130572cb..4918179e12 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -246,11 +246,69 @@ where /// [`Struct`]: crate::Struct /// [`Struct::clone_dynamic`]: crate::Struct::clone_dynamic /// [`DynamicStruct`]: crate::DynamicStruct - fn clone_value(&self) -> Box; + #[deprecated( + since = "0.16.0", + note = "to clone reflected values, prefer using `reflect_clone`. To convert reflected values to dynamic ones, use `to_dynamic`." + )] + fn clone_value(&self) -> Box { + self.to_dynamic() + } + + /// Converts this reflected value into its dynamic representation based on its [kind]. + /// + /// For example, a [`List`] type will internally invoke [`List::to_dynamic_list`], returning [`DynamicList`]. + /// A [`Struct`] type will invoke [`Struct::to_dynamic_struct`], returning [`DynamicStruct`]. + /// And so on. + /// + /// If the [kind] is [opaque], then the value will attempt to be cloned directly via [`reflect_clone`], + /// since opaque types do not have any standard dynamic representation. + /// + /// To attempt to clone the value directly such that it returns a concrete instance of this type, + /// use [`reflect_clone`]. + /// + /// # Panics + /// + /// This method will panic if the [kind] is [opaque] and the call to [`reflect_clone`] fails. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::{PartialReflect}; + /// let value = (1, true, 3.14); + /// let dynamic_value = value.to_dynamic(); + /// assert!(dynamic_value.is_dynamic()) + /// ``` + /// + /// [kind]: PartialReflect::reflect_kind + /// [`List`]: crate::List + /// [`List::to_dynamic_list`]: crate::List::to_dynamic_list + /// [`DynamicList`]: crate::DynamicList + /// [`Struct`]: crate::Struct + /// [`Struct::to_dynamic_struct`]: crate::Struct::to_dynamic_struct + /// [`DynamicStruct`]: crate::DynamicStruct + /// [opaque]: crate::ReflectKind::Opaque + /// [`reflect_clone`]: PartialReflect::reflect_clone + fn to_dynamic(&self) -> Box { + match self.reflect_ref() { + ReflectRef::Struct(dyn_struct) => Box::new(dyn_struct.to_dynamic_struct()), + ReflectRef::TupleStruct(dyn_tuple_struct) => { + Box::new(dyn_tuple_struct.to_dynamic_tuple_struct()) + } + ReflectRef::Tuple(dyn_tuple) => Box::new(dyn_tuple.to_dynamic_tuple()), + ReflectRef::List(dyn_list) => Box::new(dyn_list.to_dynamic_list()), + ReflectRef::Array(dyn_array) => Box::new(dyn_array.to_dynamic_array()), + ReflectRef::Map(dyn_map) => Box::new(dyn_map.to_dynamic_map()), + ReflectRef::Set(dyn_set) => Box::new(dyn_set.to_dynamic_set()), + ReflectRef::Enum(dyn_enum) => Box::new(dyn_enum.to_dynamic_enum()), + #[cfg(feature = "functions")] + ReflectRef::Function(dyn_function) => Box::new(dyn_function.to_dynamic_function()), + ReflectRef::Opaque(value) => value.reflect_clone().unwrap().into_partial_reflect(), + } + } /// Attempts to clone `Self` using reflection. /// - /// Unlike [`PartialReflect::clone_value`], which often returns a dynamic representation of `Self`, + /// Unlike [`to_dynamic`], which generally returns a dynamic representation of `Self`, /// this method attempts create a clone of `Self` directly, if possible. /// /// If the clone cannot be performed, an appropriate [`ReflectCloneError`] is returned. @@ -263,6 +321,8 @@ where /// let cloned = value.reflect_clone().unwrap(); /// assert!(cloned.is::<(i32, bool, f64)>()) /// ``` + /// + /// [`to_dynamic`]: PartialReflect::to_dynamic fn reflect_clone(&self) -> Result, ReflectCloneError> { Err(ReflectCloneError::NotImplemented { type_path: Cow::Owned(self.reflect_type_path().to_string()), diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index cf682edbea..2d912fd97b 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -164,7 +164,7 @@ mod tests { let mut registry = TypeRegistry::default(); registry.register::(); - let value: DynamicStruct = TestStruct { a: 123, b: 456 }.clone_dynamic(); + let value: DynamicStruct = TestStruct { a: 123, b: 456 }.to_dynamic_struct(); let serializer = ReflectSerializer::new(&value, ®istry); @@ -175,7 +175,7 @@ mod tests { let mut deserializer = ron::de::Deserializer::from_str(&result).unwrap(); let reflect_deserializer = ReflectDeserializer::new(®istry); - let expected = value.clone_value(); + let expected = value.to_dynamic(); let result = reflect_deserializer.deserialize(&mut deserializer).unwrap(); assert!(expected diff --git a/crates/bevy_reflect/src/serde/ser/mod.rs b/crates/bevy_reflect/src/serde/ser/mod.rs index 6cfebc747f..3844c2a2cb 100644 --- a/crates/bevy_reflect/src/serde/ser/mod.rs +++ b/crates/bevy_reflect/src/serde/ser/mod.rs @@ -406,7 +406,7 @@ mod tests { some: Some(SomeStruct { foo: 999999999 }), none: None, }; - let dynamic = value.clone_dynamic(); + let dynamic = value.to_dynamic_struct(); let reflect = dynamic.as_partial_reflect(); let registry = get_registry(); diff --git a/crates/bevy_reflect/src/set.rs b/crates/bevy_reflect/src/set.rs index 799499a8ad..21aa7a4d20 100644 --- a/crates/bevy_reflect/src/set.rs +++ b/crates/bevy_reflect/src/set.rs @@ -70,7 +70,20 @@ pub trait Set: PartialReflect { fn drain(&mut self) -> Vec>; /// Clones the set, producing a [`DynamicSet`]. - fn clone_dynamic(&self) -> DynamicSet; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_set` instead")] + fn clone_dynamic(&self) -> DynamicSet { + self.to_dynamic_set() + } + + /// Creates a new [`DynamicSet`] from this set. + fn to_dynamic_set(&self) -> DynamicSet { + let mut set = DynamicSet::default(); + set.set_represented_type(self.get_represented_type_info()); + for value in self.iter() { + set.insert_boxed(value.to_dynamic()); + } + set + } /// Inserts a value into the set. /// @@ -201,23 +214,6 @@ impl Set for DynamicSet { self.hash_table.drain().collect::>() } - fn clone_dynamic(&self) -> DynamicSet { - let mut hash_table = HashTable::new(); - self.hash_table - .iter() - .map(|value| value.clone_value()) - .for_each(|value| { - hash_table.insert_unique(Self::internal_hash(value.as_ref()), value, |boxed| { - Self::internal_hash(boxed.as_ref()) - }); - }); - - DynamicSet { - represented_type: self.represented_type, - hash_table, - } - } - fn insert_boxed(&mut self, value: Box) -> bool { assert_eq!( value.reflect_partial_eq(&*value), @@ -317,10 +313,6 @@ impl PartialReflect for DynamicSet { ReflectOwned::Set(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { set_partial_eq(self, value) } @@ -467,7 +459,7 @@ pub fn set_apply(a: &mut M, b: &dyn PartialReflect) { if let ReflectRef::Set(set_value) = b.reflect_ref() { for b_value in set_value.iter() { if a.get(b_value).is_none() { - a.insert_boxed(b_value.clone_value()); + a.insert_boxed(b_value.to_dynamic()); } } } else { @@ -490,7 +482,7 @@ pub fn set_try_apply(a: &mut S, b: &dyn PartialReflect) -> Result<(), Ap for b_value in set_value.iter() { if a.get(b_value).is_none() { - a.insert_boxed(b_value.clone_value()); + a.insert_boxed(b_value.to_dynamic()); } } diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index dc7ed55cef..3f96c74b3c 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -72,7 +72,19 @@ pub trait Struct: PartialReflect { fn iter_fields(&self) -> FieldIter; /// Clones the struct into a [`DynamicStruct`]. - fn clone_dynamic(&self) -> DynamicStruct; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_struct` instead")] + fn clone_dynamic(&self) -> DynamicStruct { + self.to_dynamic_struct() + } + + fn to_dynamic_struct(&self) -> DynamicStruct { + let mut dynamic_struct = DynamicStruct::default(); + dynamic_struct.set_represented_type(self.get_represented_type_info()); + for (i, value) in self.iter_fields().enumerate() { + dynamic_struct.insert_boxed(self.name_at(i).unwrap(), value.to_dynamic()); + } + dynamic_struct + } /// Will return `None` if [`TypeInfo`] is not available. fn get_represented_struct_info(&self) -> Option<&'static StructInfo> { @@ -370,19 +382,6 @@ impl Struct for DynamicStruct { index: 0, } } - - fn clone_dynamic(&self) -> DynamicStruct { - DynamicStruct { - represented_type: self.get_represented_type_info(), - field_names: self.field_names.clone(), - field_indices: self.field_indices.clone(), - fields: self - .fields - .iter() - .map(|value| value.clone_value()) - .collect(), - } - } } impl PartialReflect for DynamicStruct { @@ -449,11 +448,6 @@ impl PartialReflect for DynamicStruct { ReflectOwned::Struct(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { struct_partial_eq(self, value) } diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 1916a92f7e..31ad67fdcf 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -55,8 +55,19 @@ pub trait Tuple: PartialReflect { /// Drain the fields of this tuple to get a vector of owned values. fn drain(self: Box) -> Vec>; - /// Clones the struct into a [`DynamicTuple`]. - fn clone_dynamic(&self) -> DynamicTuple; + /// Clones the tuple into a [`DynamicTuple`]. + #[deprecated(since = "0.16.0", note = "use `to_dynamic_tuple` instead")] + fn clone_dynamic(&self) -> DynamicTuple { + self.to_dynamic_tuple() + } + + /// Creates a new [`DynamicTuple`] from this tuple. + fn to_dynamic_tuple(&self) -> DynamicTuple { + DynamicTuple { + represented_type: self.get_represented_type_info(), + fields: self.iter_fields().map(PartialReflect::to_dynamic).collect(), + } + } /// Will return `None` if [`TypeInfo`] is not available. fn get_represented_tuple_info(&self) -> Option<&'static TupleInfo> { @@ -270,18 +281,6 @@ impl Tuple for DynamicTuple { fn drain(self: Box) -> Vec> { self.fields } - - #[inline] - fn clone_dynamic(&self) -> DynamicTuple { - DynamicTuple { - represented_type: self.represented_type, - fields: self - .fields - .iter() - .map(|value| value.clone_value()) - .collect(), - } - } } impl PartialReflect for DynamicTuple { @@ -339,11 +338,6 @@ impl PartialReflect for DynamicTuple { ReflectOwned::Tuple(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { tuple_try_apply(self, value) } @@ -518,18 +512,6 @@ macro_rules! impl_reflect_tuple { $(Box::new(self.$index),)* ] } - - #[inline] - fn clone_dynamic(&self) -> DynamicTuple { - let info = self.get_represented_type_info(); - DynamicTuple { - represented_type: info, - fields: self - .iter_fields() - .map(|value| value.clone_value()) - .collect(), - } - } } impl<$($name: Reflect + MaybeTyped + TypePath + GetTypeRegistration),*> PartialReflect for ($($name,)*) { @@ -578,10 +560,6 @@ macro_rules! impl_reflect_tuple { ReflectOwned::Tuple(self) } - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { crate::tuple_partial_eq(self, value) } diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 8348ca7740..ed8a0c5ea1 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -56,7 +56,18 @@ pub trait TupleStruct: PartialReflect { fn iter_fields(&self) -> TupleStructFieldIter; /// Clones the struct into a [`DynamicTupleStruct`]. - fn clone_dynamic(&self) -> DynamicTupleStruct; + #[deprecated(since = "0.16.0", note = "use `to_dynamic_tuple_struct` instead")] + fn clone_dynamic(&self) -> DynamicTupleStruct { + self.to_dynamic_tuple_struct() + } + + /// Creates a new [`DynamicTupleStruct`] from this tuple struct. + fn to_dynamic_tuple_struct(&self) -> DynamicTupleStruct { + DynamicTupleStruct { + represented_type: self.get_represented_type_info(), + fields: self.iter_fields().map(PartialReflect::to_dynamic).collect(), + } + } /// Will return `None` if [`TypeInfo`] is not available. fn get_represented_tuple_struct_info(&self) -> Option<&'static TupleStructInfo> { @@ -279,17 +290,6 @@ impl TupleStruct for DynamicTupleStruct { index: 0, } } - - fn clone_dynamic(&self) -> DynamicTupleStruct { - DynamicTupleStruct { - represented_type: self.represented_type, - fields: self - .fields - .iter() - .map(|value| value.clone_value()) - .collect(), - } - } } impl PartialReflect for DynamicTupleStruct { @@ -357,11 +357,6 @@ impl PartialReflect for DynamicTupleStruct { ReflectOwned::TupleStruct(self) } - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone_dynamic()) - } - #[inline] fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option { tuple_struct_partial_eq(self, value) diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 2add261aa2..1a3be15c36 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -72,7 +72,6 @@ use thiserror::Error; /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } /// # } /// # impl Reflect for MyStruct { /// # fn into_any(self: Box) -> Box { todo!() } diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index aa340c6363..33725a4633 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -88,7 +88,6 @@ mod sealed { /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } /// # } /// # impl Reflect for Foo { /// # fn into_any(self: Box) -> Box { todo!() } @@ -176,7 +175,6 @@ impl Default for NonGenericTypeCell { /// # fn reflect_ref(&self) -> ReflectRef { todo!() } /// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } /// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } /// # } /// # impl Reflect for Foo { /// # fn into_any(self: Box) -> Box { todo!() } diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index ded2788305..a94887568b 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -8,6 +8,7 @@ use bevy_ecs::{ }; use bevy_reflect::{PartialReflect, TypePath, TypeRegistry}; +use crate::reflect_utils::clone_reflect_value; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; use bevy_ecs::component::ComponentCloneBehavior; @@ -87,7 +88,6 @@ impl DynamicScene { // Apply/ add each component to the given entity. for component in &scene_entity.components { - let component = component.clone_value(); let type_info = component.get_represented_type_info().ok_or_else(|| { SceneSpawnError::NoRepresentedType { type_path: component.reflect_type_path().to_string(), @@ -131,7 +131,6 @@ impl DynamicScene { // Insert resources after all entities have been added to the world. // This ensures the entities are available for the resources to reference during mapping. for resource in &self.resources { - let mut resource = resource.clone_value(); let type_info = resource.get_represented_type_info().ok_or_else(|| { SceneSpawnError::NoRepresentedType { type_path: resource.reflect_type_path().to_string(), @@ -151,6 +150,7 @@ impl DynamicScene { // If this component references entities in the scene, update // them to the entities in the world. if let Some(map_entities) = registration.data::() { + let mut resource = clone_reflect_value(resource.as_partial_reflect(), registration); SceneEntityMapper::world_scope(entity_map, world, |_, mapper| { map_entities.map_entities(resource.as_partial_reflect_mut(), mapper); }); diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index c33346373b..6742a32ded 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,5 +1,6 @@ use core::any::TypeId; +use crate::reflect_utils::clone_reflect_value; use crate::{DynamicEntity, DynamicScene, SceneFilter}; use alloc::collections::BTreeMap; use bevy_ecs::{ @@ -10,7 +11,7 @@ use bevy_ecs::{ resource::Resource, world::World, }; -use bevy_reflect::{PartialReflect, ReflectFromReflect}; +use bevy_reflect::PartialReflect; use bevy_utils::default; /// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources. @@ -303,14 +304,8 @@ impl<'w> DynamicSceneBuilder<'w> { .data::()? .reflect(original_entity)?; - // Clone via `FromReflect`. Unlike `PartialReflect::clone_value` this - // retains the original type and `ReflectSerialize` type data which is needed to - // deserialize. - let component = type_registration - .data::() - .and_then(|fr| fr.from_reflect(component.as_partial_reflect())) - .map(PartialReflect::into_partial_reflect) - .unwrap_or_else(|| component.clone_value()); + let component = + clone_reflect_value(component.as_partial_reflect(), type_registration); entry.components.push(component); Some(()) @@ -383,11 +378,8 @@ impl<'w> DynamicSceneBuilder<'w> { .data::()? .reflect(self.original_world)?; - let resource = type_registration - .data::() - .and_then(|fr| fr.from_reflect(resource.as_partial_reflect())) - .map(PartialReflect::into_partial_reflect) - .unwrap_or_else(|| resource.clone_value()); + let resource = + clone_reflect_value(resource.as_partial_reflect(), type_registration); self.extracted_resources.insert(component_id, resource); Some(()) diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index a8919a5c77..7a9526c0f4 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -15,6 +15,7 @@ extern crate alloc; mod components; mod dynamic_scene; mod dynamic_scene_builder; +mod reflect_utils; mod scene; mod scene_filter; mod scene_loader; diff --git a/crates/bevy_scene/src/reflect_utils.rs b/crates/bevy_scene/src/reflect_utils.rs new file mode 100644 index 0000000000..bf69dd0352 --- /dev/null +++ b/crates/bevy_scene/src/reflect_utils.rs @@ -0,0 +1,25 @@ +use bevy_reflect::{PartialReflect, ReflectFromReflect, TypeRegistration}; + +/// Attempts to clone a [`PartialReflect`] value using various methods. +/// +/// This first attempts to clone via [`PartialReflect::reflect_clone`]. +/// then falls back to [`ReflectFromReflect::from_reflect`], +/// and finally [`PartialReflect::to_dynamic`] if the first two methods fail. +/// +/// This helps ensure that the original type and type data is retained, +/// and only returning a dynamic type if all other methods fail. +pub(super) fn clone_reflect_value( + value: &dyn PartialReflect, + type_registration: &TypeRegistration, +) -> Box { + value + .reflect_clone() + .map(PartialReflect::into_partial_reflect) + .unwrap_or_else(|_| { + type_registration + .data::() + .and_then(|fr| fr.from_reflect(value.as_partial_reflect())) + .map(PartialReflect::into_partial_reflect) + .unwrap_or_else(|| value.to_dynamic()) + }) +} diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 7cbfb59771..f59d967890 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,5 +1,6 @@ use core::any::TypeId; +use crate::reflect_utils::clone_reflect_value; use crate::{DynamicScene, SceneSpawnError}; use bevy_asset::Asset; use bevy_ecs::{ @@ -10,7 +11,7 @@ use bevy_ecs::{ relationship::RelationshipInsertHookMode, world::World, }; -use bevy_reflect::{PartialReflect, TypePath}; +use bevy_reflect::TypePath; /// A composition of [`World`] objects. /// @@ -143,7 +144,9 @@ impl Scene { let Some(component) = reflect_component .reflect(self.world.entity(scene_entity.id())) - .map(PartialReflect::clone_value) + .map(|component| { + clone_reflect_value(component.as_partial_reflect(), registration) + }) else { continue; }; diff --git a/examples/reflection/dynamic_types.rs b/examples/reflection/dynamic_types.rs index 387fe7d0ed..772ffe1dd2 100644 --- a/examples/reflection/dynamic_types.rs +++ b/examples/reflection/dynamic_types.rs @@ -10,7 +10,7 @@ use serde::de::DeserializeSeed; use std::collections::{HashMap, HashSet}; fn main() { - #[derive(Reflect, Default)] + #[derive(Reflect, Default, PartialEq, Debug)] #[reflect(Identifiable, Default)] struct Player { id: u32, @@ -42,22 +42,27 @@ fn main() { // Because it's the same type under the hood, we can still downcast it back to the original type. assert!(reflected.downcast_ref::().is_some()); - // But now let's "clone" our type using `PartialReflect::clone_value`. - // Notice here we bind it as a `dyn PartialReflect`. - let cloned: Box = reflected.clone_value(); + // We can attempt to clone our value using `PartialReflect::reflect_clone`. + // This will recursively call `PartialReflect::reflect_clone` on all fields of the type. + // Or, if we had registered `ReflectClone` using `#[reflect(Clone)]`, it would simply call `Clone::clone` directly. + let cloned: Box = reflected.reflect_clone().unwrap(); + assert_eq!(cloned.downcast_ref::(), Some(&Player { id: 123 })); - // If we try and convert it to a `dyn Reflect` trait object, we'll get an error. - assert!(cloned.try_as_reflect().is_none()); + // Another way we can "clone" our data is by converting it to a dynamic type. + // Notice here we bind it as a `dyn PartialReflect` instead of `dyn Reflect`. + // This is because it returns a dynamic type that simply represents the original type. + // In this case, because `Player` is a struct, it will return a `DynamicStruct`. + let dynamic: Box = reflected.to_dynamic(); + assert!(dynamic.is_dynamic()); - // Why is this? - // Well the reason is that `PartialReflect::clone_value` actually creates a dynamic type. - // Since `Player` is a struct, our trait object is actually a value of `DynamicStruct`. - assert!(cloned.is_dynamic()); + // And if we try to convert it back to a `dyn Reflect` trait object, we'll get `None`. + // Dynamic types cannot be directly cast to `dyn Reflect` trait objects. + assert!(dynamic.try_as_reflect().is_none()); - // This dynamic type is used to represent (or "proxy") the original type, + // Generally dynamic types are used to represent (or "proxy") the original type, // so that we can continue to access its fields and overall structure. - let cloned_ref = cloned.reflect_ref().as_struct().unwrap(); - let id = cloned_ref.field("id").unwrap().try_downcast_ref::(); + let dynamic_ref = dynamic.reflect_ref().as_struct().unwrap(); + let id = dynamic_ref.field("id").unwrap().try_downcast_ref::(); assert_eq!(id, Some(&123)); // It also enables us to create a representation of a type without having compile-time @@ -137,7 +142,7 @@ fn main() { } // Lastly, while dynamic types are commonly generated via reflection methods like - // `PartialReflect::clone_value` or via the reflection deserializers, + // `PartialReflect::to_dynamic` or via the reflection deserializers, // you can also construct them manually. let mut my_dynamic_list = DynamicList::from_iter([1u32, 2u32, 3u32]);