Skip to content

Commit 06d9384

Browse files
Daviercart
andcommitted
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene. For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits. In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section). To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow. Co-authored-by: Carter Anderson <mcanders1@gmail.com>
1 parent 585d0b8 commit 06d9384

File tree

14 files changed

+495
-36
lines changed

14 files changed

+495
-36
lines changed

crates/bevy_asset/src/handle.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,25 @@ use crate::{
1010
Asset, Assets,
1111
};
1212
use bevy_ecs::{component::Component, reflect::ReflectComponent};
13-
use bevy_reflect::{Reflect, ReflectDeserialize};
13+
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize};
1414
use bevy_utils::Uuid;
1515
use crossbeam_channel::{Receiver, Sender};
1616
use serde::{Deserialize, Serialize};
1717

1818
/// A unique, stable asset id
1919
#[derive(
20-
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
20+
Debug,
21+
Clone,
22+
Copy,
23+
Eq,
24+
PartialEq,
25+
Hash,
26+
Ord,
27+
PartialOrd,
28+
Serialize,
29+
Deserialize,
30+
Reflect,
31+
FromReflect,
2132
)]
2233
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
2334
pub enum HandleId {
@@ -58,7 +69,7 @@ impl HandleId {
5869
///
5970
/// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets)
6071
/// collection.
61-
#[derive(Component, Reflect)]
72+
#[derive(Component, Reflect, FromReflect)]
6273
#[reflect(Component)]
6374
pub struct Handle<T>
6475
where
@@ -77,6 +88,13 @@ enum HandleType {
7788
Strong(Sender<RefChange>),
7889
}
7990

91+
// FIXME: This only is needed because `Handle`'s field `handle_type` is currently ignored for reflection
92+
impl Default for HandleType {
93+
fn default() -> Self {
94+
Self::Weak
95+
}
96+
}
97+
8098
impl Debug for HandleType {
8199
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82100
match self {

crates/bevy_ecs/src/reflect.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::{
66
entity::{Entity, EntityMap, MapEntities, MapEntitiesError},
77
world::{FromWorld, World},
88
};
9-
use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
9+
use bevy_reflect::{
10+
impl_from_reflect_value, impl_reflect_value, FromType, Reflect, ReflectDeserialize,
11+
};
1012

1113
#[derive(Clone)]
1214
pub struct ReflectComponent {
@@ -121,6 +123,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
121123
}
122124

123125
impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize));
126+
impl_from_reflect_value!(Entity);
124127

125128
#[derive(Clone)]
126129
pub struct ReflectMapEntities {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
use syn::{Field, Generics, Ident, Index, Member, Path};
4+
5+
pub fn impl_struct(
6+
struct_name: &Ident,
7+
generics: &Generics,
8+
bevy_reflect_path: &Path,
9+
active_fields: &[(&Field, usize)],
10+
ignored_fields: &[(&Field, usize)],
11+
) -> TokenStream {
12+
let field_names = active_fields
13+
.iter()
14+
.map(|(field, index)| {
15+
field
16+
.ident
17+
.as_ref()
18+
.map(|i| i.to_string())
19+
.unwrap_or_else(|| index.to_string())
20+
})
21+
.collect::<Vec<String>>();
22+
let field_idents = active_fields
23+
.iter()
24+
.map(|(field, index)| {
25+
field
26+
.ident
27+
.as_ref()
28+
.map(|ident| Member::Named(ident.clone()))
29+
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
30+
})
31+
.collect::<Vec<_>>();
32+
33+
let field_types = active_fields
34+
.iter()
35+
.map(|(field, _index)| field.ty.clone())
36+
.collect::<Vec<_>>();
37+
let field_count = active_fields.len();
38+
let ignored_field_idents = ignored_fields
39+
.iter()
40+
.map(|(field, index)| {
41+
field
42+
.ident
43+
.as_ref()
44+
.map(|ident| Member::Named(ident.clone()))
45+
.unwrap_or_else(|| Member::Unnamed(Index::from(*index)))
46+
})
47+
.collect::<Vec<_>>();
48+
49+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
50+
51+
// Add FromReflect bound for each active field
52+
let mut where_from_reflect_clause = if where_clause.is_some() {
53+
quote! {#where_clause}
54+
} else if field_count > 0 {
55+
quote! {where}
56+
} else {
57+
quote! {}
58+
};
59+
where_from_reflect_clause.extend(quote! {
60+
#(#field_types: #bevy_reflect_path::FromReflect,)*
61+
});
62+
63+
TokenStream::from(quote! {
64+
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
65+
{
66+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
67+
use #bevy_reflect_path::Struct;
68+
if let #bevy_reflect_path::ReflectRef::Struct(ref_struct) = reflect.reflect_ref() {
69+
Some(
70+
Self{
71+
#(#field_idents: {
72+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_struct.field(#field_names)?)?
73+
},)*
74+
#(#ignored_field_idents: Default::default(),)*
75+
}
76+
)
77+
} else {
78+
None
79+
}
80+
}
81+
}
82+
})
83+
}
84+
85+
pub fn impl_tuple_struct(
86+
struct_name: &Ident,
87+
generics: &Generics,
88+
bevy_reflect_path: &Path,
89+
active_fields: &[(&Field, usize)],
90+
ignored_fields: &[(&Field, usize)],
91+
) -> TokenStream {
92+
let field_idents = active_fields
93+
.iter()
94+
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
95+
.collect::<Vec<_>>();
96+
let field_types = active_fields
97+
.iter()
98+
.map(|(field, _index)| field.ty.clone())
99+
.collect::<Vec<_>>();
100+
let field_count = active_fields.len();
101+
let field_indices = (0..field_count).collect::<Vec<usize>>();
102+
let ignored_field_idents = ignored_fields
103+
.iter()
104+
.map(|(_field, index)| Member::Unnamed(Index::from(*index)))
105+
.collect::<Vec<_>>();
106+
107+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
108+
// Add FromReflect bound for each active field
109+
let mut where_from_reflect_clause = if where_clause.is_some() {
110+
quote! {#where_clause}
111+
} else if field_count > 0 {
112+
quote! {where}
113+
} else {
114+
quote! {}
115+
};
116+
where_from_reflect_clause.extend(quote! {
117+
#(#field_types: #bevy_reflect_path::FromReflect,)*
118+
});
119+
120+
TokenStream::from(quote! {
121+
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause
122+
{
123+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
124+
use #bevy_reflect_path::TupleStruct;
125+
if let #bevy_reflect_path::ReflectRef::TupleStruct(ref_tuple_struct) = reflect.reflect_ref() {
126+
Some(
127+
Self{
128+
#(#field_idents:
129+
<#field_types as #bevy_reflect_path::FromReflect>::from_reflect(ref_tuple_struct.field(#field_indices)?)?
130+
,)*
131+
#(#ignored_field_idents: Default::default(),)*
132+
}
133+
)
134+
} else {
135+
None
136+
}
137+
}
138+
}
139+
})
140+
}
141+
142+
pub fn impl_value(type_name: &Ident, generics: &Generics, bevy_reflect_path: &Path) -> TokenStream {
143+
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
144+
TokenStream::from(quote! {
145+
impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause {
146+
fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option<Self> {
147+
Some(reflect.any().downcast_ref::<#type_name #ty_generics>()?.clone())
148+
}
149+
}
150+
})
151+
}

crates/bevy_reflect/bevy_reflect_derive/src/lib.rs

+115
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate proc_macro;
22

3+
mod from_reflect;
34
mod reflect_trait;
45
mod type_uuid;
56

@@ -740,3 +741,117 @@ pub fn external_type_uuid(tokens: proc_macro::TokenStream) -> proc_macro::TokenS
740741
pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream {
741742
reflect_trait::reflect_trait(args, input)
742743
}
744+
745+
#[proc_macro_derive(FromReflect)]
746+
pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
747+
let ast = parse_macro_input!(input as DeriveInput);
748+
let unit_struct_punctuated = Punctuated::new();
749+
let (fields, mut derive_type) = match &ast.data {
750+
Data::Struct(DataStruct {
751+
fields: Fields::Named(fields),
752+
..
753+
}) => (&fields.named, DeriveType::Struct),
754+
Data::Struct(DataStruct {
755+
fields: Fields::Unnamed(fields),
756+
..
757+
}) => (&fields.unnamed, DeriveType::TupleStruct),
758+
Data::Struct(DataStruct {
759+
fields: Fields::Unit,
760+
..
761+
}) => (&unit_struct_punctuated, DeriveType::UnitStruct),
762+
_ => (&unit_struct_punctuated, DeriveType::Value),
763+
};
764+
765+
let fields_and_args = fields
766+
.iter()
767+
.enumerate()
768+
.map(|(i, f)| {
769+
(
770+
f,
771+
f.attrs
772+
.iter()
773+
.find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME)
774+
.map(|a| {
775+
syn::custom_keyword!(ignore);
776+
let mut attribute_args = PropAttributeArgs { ignore: None };
777+
a.parse_args_with(|input: ParseStream| {
778+
if input.parse::<Option<ignore>>()?.is_some() {
779+
attribute_args.ignore = Some(true);
780+
return Ok(());
781+
}
782+
Ok(())
783+
})
784+
.expect("Invalid 'property' attribute format.");
785+
786+
attribute_args
787+
}),
788+
i,
789+
)
790+
})
791+
.collect::<Vec<(&Field, Option<PropAttributeArgs>, usize)>>();
792+
let active_fields = fields_and_args
793+
.iter()
794+
.filter(|(_field, attrs, _i)| {
795+
attrs.is_none()
796+
|| match attrs.as_ref().unwrap().ignore {
797+
Some(ignore) => !ignore,
798+
None => true,
799+
}
800+
})
801+
.map(|(f, _attr, i)| (*f, *i))
802+
.collect::<Vec<(&Field, usize)>>();
803+
let ignored_fields = fields_and_args
804+
.iter()
805+
.filter(|(_field, attrs, _i)| {
806+
attrs
807+
.as_ref()
808+
.map(|attrs| attrs.ignore.unwrap_or(false))
809+
.unwrap_or(false)
810+
})
811+
.map(|(f, _attr, i)| (*f, *i))
812+
.collect::<Vec<(&Field, usize)>>();
813+
814+
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
815+
let type_name = &ast.ident;
816+
817+
for attribute in ast.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
818+
let meta_list = if let Meta::List(meta_list) = attribute {
819+
meta_list
820+
} else {
821+
continue;
822+
};
823+
824+
if let Some(ident) = meta_list.path.get_ident() {
825+
if ident == REFLECT_VALUE_ATTRIBUTE_NAME {
826+
derive_type = DeriveType::Value;
827+
}
828+
}
829+
}
830+
831+
match derive_type {
832+
DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(
833+
type_name,
834+
&ast.generics,
835+
&bevy_reflect_path,
836+
&active_fields,
837+
&ignored_fields,
838+
),
839+
DeriveType::TupleStruct => from_reflect::impl_tuple_struct(
840+
type_name,
841+
&ast.generics,
842+
&bevy_reflect_path,
843+
&active_fields,
844+
&ignored_fields,
845+
),
846+
DeriveType::Value => from_reflect::impl_value(type_name, &ast.generics, &bevy_reflect_path),
847+
}
848+
}
849+
850+
#[proc_macro]
851+
pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
852+
let reflect_value_def = parse_macro_input!(input as ReflectDef);
853+
854+
let bevy_reflect_path = BevyManifest::default().get_path("bevy_reflect");
855+
let ty = &reflect_value_def.type_name;
856+
from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path)
857+
}

crates/bevy_reflect/src/impls/glam.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate as bevy_reflect;
22
use crate::ReflectDeserialize;
3-
use bevy_reflect_derive::impl_reflect_value;
3+
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value};
44
use glam::{IVec2, IVec3, IVec4, Mat3, Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4};
55

66
impl_reflect_value!(IVec2(PartialEq, Serialize, Deserialize));
@@ -15,3 +15,16 @@ impl_reflect_value!(Vec4(PartialEq, Serialize, Deserialize));
1515
impl_reflect_value!(Mat3(PartialEq, Serialize, Deserialize));
1616
impl_reflect_value!(Mat4(PartialEq, Serialize, Deserialize));
1717
impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize));
18+
19+
impl_from_reflect_value!(IVec2);
20+
impl_from_reflect_value!(IVec3);
21+
impl_from_reflect_value!(IVec4);
22+
impl_from_reflect_value!(UVec2);
23+
impl_from_reflect_value!(UVec3);
24+
impl_from_reflect_value!(UVec4);
25+
impl_from_reflect_value!(Vec2);
26+
impl_from_reflect_value!(Vec3);
27+
impl_from_reflect_value!(Vec4);
28+
impl_from_reflect_value!(Mat3);
29+
impl_from_reflect_value!(Mat4);
30+
impl_from_reflect_value!(Quat);

0 commit comments

Comments
 (0)