reflect: treat proxy types correctly when serializing (#12024)

# Objective

- Fixes #12001.
- Note this PR doesn't change any feature flags, however flaky the issue
revealed they are.

## Solution

- Use `FromReflect` to convert proxy types to concrete ones in
`ReflectSerialize::get_serializable`.
- Use `get_represented_type_info() -> type_id()` to get the correct type
id to interact with the registry in
`bevy_reflect::serde::ser::get_serializable`.

---

## Changelog

- Registering `ReflectSerialize` now imposes additional `FromReflect`
and `TypePath` bounds.

## Migration Guide

- If `ReflectSerialize` is registered on a type, but `TypePath` or
`FromReflect` implementations are omitted (perhaps by
`#[reflect(type_path = false)` or `#[reflect(from_reflect = false)]`),
the traits must now be implemented.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
radiish 2024-02-26 16:13:04 +00:00 committed by GitHub
parent 51edf9cc8f
commit 2b7a3b2a55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 8 deletions

View file

@ -32,12 +32,19 @@ fn get_serializable<'a, E: Error>(
reflect_value: &'a dyn Reflect, reflect_value: &'a dyn Reflect,
type_registry: &TypeRegistry, type_registry: &TypeRegistry,
) -> Result<Serializable<'a>, E> { ) -> Result<Serializable<'a>, E> {
let info = reflect_value.get_represented_type_info().ok_or_else(|| {
Error::custom(format_args!(
"Type '{}' does not represent any type",
reflect_value.reflect_type_path(),
))
})?;
let reflect_serialize = type_registry let reflect_serialize = type_registry
.get_type_data::<ReflectSerialize>(reflect_value.type_id()) .get_type_data::<ReflectSerialize>(info.type_id())
.ok_or_else(|| { .ok_or_else(|| {
Error::custom(format_args!( Error::custom(format_args!(
"Type '{}' did not register ReflectSerialize", "Type '{}' did not register ReflectSerialize",
reflect_value.reflect_type_path() info.type_path(),
)) ))
})?; })?;
Ok(reflect_serialize.get_serializable(reflect_value)) Ok(reflect_serialize.get_serializable(reflect_value))

View file

@ -1,4 +1,4 @@
use crate::{serde::Serializable, Reflect, TypeInfo, TypePath, Typed}; use crate::{serde::Serializable, FromReflect, Reflect, TypeInfo, TypePath, Typed};
use bevy_ptr::{Ptr, PtrMut}; use bevy_ptr::{Ptr, PtrMut};
use bevy_utils::{HashMap, HashSet, TypeIdMap}; use bevy_utils::{HashMap, HashSet, TypeIdMap};
use downcast_rs::{impl_downcast, Downcast}; use downcast_rs::{impl_downcast, Downcast};
@ -461,14 +461,20 @@ pub struct ReflectSerialize {
get_serializable: for<'a> fn(value: &'a dyn Reflect) -> Serializable, get_serializable: for<'a> fn(value: &'a dyn Reflect) -> Serializable,
} }
impl<T: Reflect + erased_serde::Serialize> FromType<T> for ReflectSerialize { impl<T: TypePath + FromReflect + erased_serde::Serialize> FromType<T> for ReflectSerialize {
fn from_type() -> Self { fn from_type() -> Self {
ReflectSerialize { ReflectSerialize {
get_serializable: |value| { get_serializable: |value| {
let value = value.downcast_ref::<T>().unwrap_or_else(|| { value
panic!("ReflectSerialize::get_serialize called with type `{}`, even though it was created for `{}`", value.reflect_type_path(), std::any::type_name::<T>()) .downcast_ref::<T>()
}); .map(|value| Serializable::Borrowed(value))
Serializable::Borrowed(value) .or_else(|| T::from_reflect(value).map(|value| Serializable::Owned(Box::new(value))))
.unwrap_or_else(|| {
panic!(
"FromReflect::from_reflect failed when called on type `{}` with this value: {value:?}",
T::type_path(),
);
})
}, },
} }
} }

View file

@ -114,6 +114,7 @@ fn save_scene_system(world: &mut World) {
component_b, component_b,
ComponentA { x: 1.0, y: 2.0 }, ComponentA { x: 1.0, y: 2.0 },
Transform::IDENTITY, Transform::IDENTITY,
Name::new("joe"),
)); ));
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 }); scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
scene_world.insert_resource(ResourceA { score: 1 }); scene_world.insert_resource(ResourceA { score: 1 });