diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 73eaac3e5207e..34b6b74806f06 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -35,6 +35,8 @@ glam = { version = "0.21", features = ["serde"], optional = true } [dev-dependencies] ron = "0.8.0" +rmp-serde = "1.1" +bincode = "1.3" [[example]] name = "reflect_docs" diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs index 16aeb88a84e2b..5d5a04c91d53a 100644 --- a/crates/bevy_reflect/src/enums/enum_trait.rs +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -136,6 +136,7 @@ pub struct EnumInfo { type_name: &'static str, type_id: TypeId, variants: Box<[VariantInfo]>, + variant_names: Box<[&'static str]>, variant_indices: HashMap<&'static str, usize>, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -156,11 +157,14 @@ impl EnumInfo { .map(|(index, variant)| (variant.name(), index)) .collect::>(); + let variant_names = variants.iter().map(|variant| variant.name()).collect(); + Self { name, type_name: std::any::type_name::(), type_id: TypeId::of::(), variants: variants.to_vec().into_boxed_slice(), + variant_names, variant_indices, #[cfg(feature = "documentation")] docs: None, @@ -173,6 +177,11 @@ impl EnumInfo { Self { docs, ..self } } + /// A slice containing the names of all variants in order. + pub fn variant_names(&self) -> &[&'static str] { + &self.variant_names + } + /// Get a variant with the given name. pub fn variant(&self, name: &str) -> Option<&VariantInfo> { self.variant_indices diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs index a67e9b4d62ab8..6901474041408 100644 --- a/crates/bevy_reflect/src/enums/variants.rs +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -89,6 +89,7 @@ impl VariantInfo { pub struct StructVariantInfo { name: &'static str, fields: Box<[NamedField]>, + field_names: Box<[&'static str]>, field_indices: HashMap<&'static str, usize>, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -98,9 +99,11 @@ impl StructVariantInfo { /// Create a new [`StructVariantInfo`]. pub fn new(name: &'static str, fields: &[NamedField]) -> Self { let field_indices = Self::collect_field_indices(fields); + let field_names = fields.iter().map(|field| field.name()).collect(); Self { name, fields: fields.to_vec().into_boxed_slice(), + field_names, field_indices, #[cfg(feature = "documentation")] docs: None, @@ -118,6 +121,11 @@ impl StructVariantInfo { self.name } + /// A slice containing the names of all fields in order. + pub fn field_names(&self) -> &[&'static str] { + &self.field_names + } + /// Get the field with the given name. pub fn field(&self, name: &str) -> Option<&NamedField> { self.field_indices diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 103fb1fc426f3..4f6c0f31f7517 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -35,6 +35,14 @@ trait TupleLikeInfo { fn get_field_len(&self) -> usize; } +trait Container { + fn get_field_registration<'a, E: Error>( + &self, + index: usize, + registry: &'a TypeRegistry, + ) -> Result<&'a TypeRegistration, E>; +} + impl StructLikeInfo for StructInfo { fn get_name(&self) -> &str { self.type_name() @@ -49,6 +57,23 @@ impl StructLikeInfo for StructInfo { } } +impl Container for StructInfo { + fn get_field_registration<'a, E: Error>( + &self, + index: usize, + registry: &'a TypeRegistry, + ) -> Result<&'a TypeRegistration, E> { + let field = self.field_at(index).ok_or_else(|| { + de::Error::custom(format_args!( + "no field at index {} on struct {}", + index, + self.type_name(), + )) + })?; + get_registration(field.type_id(), field.type_name(), registry) + } +} + impl StructLikeInfo for StructVariantInfo { fn get_name(&self) -> &str { self.name() @@ -63,6 +88,23 @@ impl StructLikeInfo for StructVariantInfo { } } +impl Container for StructVariantInfo { + fn get_field_registration<'a, E: Error>( + &self, + index: usize, + registry: &'a TypeRegistry, + ) -> Result<&'a TypeRegistration, E> { + let field = self.field_at(index).ok_or_else(|| { + de::Error::custom(format_args!( + "no field at index {} on variant {}", + index, + self.name(), + )) + })?; + get_registration(field.type_id(), field.type_name(), registry) + } +} + impl TupleLikeInfo for TupleInfo { fn get_name(&self) -> &str { self.type_name() @@ -151,6 +193,23 @@ impl<'de> Deserialize<'de> for Ident { } } +struct U32Visitor; + +impl<'de> Visitor<'de> for U32Visitor { + type Value = u32; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("u32") + } + + fn visit_u32(self, v: u32) -> Result + where + E: Error, + { + Ok(v) + } +} + /// A general purpose deserializer for reflected types. /// /// This will return a [`Box`] containing the deserialized data. @@ -192,7 +251,7 @@ impl<'a, 'de> DeserializeSeed<'de> for UntypedReflectDeserializer<'a> { where D: serde::Deserializer<'de>, { - deserializer.deserialize_any(UntypedReflectDeserializerVisitor { + deserializer.deserialize_map(UntypedReflectDeserializerVisitor { registry: self.registry, }) } @@ -279,8 +338,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { TypeInfo::Struct(struct_info) => { let mut dynamic_struct = deserializer.deserialize_struct( struct_info.name(), - // Field names are mainly just a hint, we don't necessarily need to store and pass that data - &[], + struct_info.field_names(), StructVisitor { struct_info, registry: self.registry, @@ -350,8 +408,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> { } else { deserializer.deserialize_enum( enum_info.name(), - // Variant names are mainly just a hint, we don't necessarily need to store and pass that data - &[], + enum_info.variant_names(), EnumVisitor { enum_info, registry: self.registry, @@ -398,6 +455,30 @@ impl<'a, 'de> Visitor<'de> for StructVisitor<'a> { { visit_struct(&mut map, self.struct_info, self.registry) } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut index = 0usize; + let mut output = DynamicStruct::default(); + + while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { + registration: self + .struct_info + .get_field_registration(index, self.registry)?, + registry: self.registry, + })? { + let name = self.struct_info.field_at(index).unwrap().name(); + output.insert_boxed(name, value); + index += 1; + if index >= self.struct_info.field_len() { + break; + } + } + + Ok(output) + } } struct TupleStructVisitor<'a> { @@ -609,21 +690,15 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { A: EnumAccess<'de>, { let mut dynamic_enum = DynamicEnum::default(); - let (Ident(variant_name), variant) = data.variant().unwrap(); - let variant_info = self.enum_info.variant(&variant_name).ok_or_else(|| { - let names = self.enum_info.iter().map(|variant| variant.name()); - Error::custom(format_args!( - "unknown variant `{}`, expected one of {:?}", - variant_name, - ExpectedValues(names.collect()) - )) + let (variant_info, variant) = data.variant_seed(VariantDeserializer { + enum_info: self.enum_info, })?; + let value: DynamicVariant = match variant_info { VariantInfo::Unit(..) => variant.unit_variant()?.into(), VariantInfo::Struct(struct_info) => variant .struct_variant( - // Field names are mainly just a hint, we don't necessarily need to store and pass that data - &[], + struct_info.field_names(), StructVariantVisitor { struct_info, registry: self.registry, @@ -653,11 +728,63 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { .into(), }; - dynamic_enum.set_variant(variant_name, value); + dynamic_enum.set_variant(variant_info.name(), value); Ok(dynamic_enum) } } +struct VariantDeserializer { + enum_info: &'static EnumInfo, +} + +impl<'de> DeserializeSeed<'de> for VariantDeserializer { + type Value = &'static VariantInfo; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct VariantVisitor(&'static EnumInfo); + + impl<'de> Visitor<'de> for VariantVisitor { + type Value = &'static VariantInfo; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("expected either a variant index or variant name") + } + + fn visit_str(self, variant_name: &str) -> Result + where + E: Error, + { + self.0.variant(variant_name).ok_or_else(|| { + let names = self.0.iter().map(|variant| variant.name()); + Error::custom(format_args!( + "unknown variant `{}`, expected one of {:?}", + variant_name, + ExpectedValues(names.collect()) + )) + }) + } + + fn visit_u32(self, variant_index: u32) -> Result + where + E: Error, + { + self.0.variant_at(variant_index as usize).ok_or_else(|| { + Error::custom(format_args!( + "no variant found at index `{}` on enum `{}`", + variant_index, + self.0.name() + )) + }) + } + } + + deserializer.deserialize_identifier(VariantVisitor(self.enum_info)) + } +} + struct StructVariantVisitor<'a> { struct_info: &'static StructVariantInfo, registry: &'a TypeRegistry, @@ -676,6 +803,30 @@ impl<'a, 'de> Visitor<'de> for StructVariantVisitor<'a> { { visit_struct(&mut map, self.struct_info, self.registry) } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut index = 0usize; + let mut output = DynamicStruct::default(); + + while let Some(value) = seq.next_element_seed(TypedReflectDeserializer { + registration: self + .struct_info + .get_field_registration(index, self.registry)?, + registry: self.registry, + })? { + let name = self.struct_info.field_at(index).unwrap().name(); + output.insert_boxed(name, value); + index += 1; + if index >= self.struct_info.field_len() { + break; + } + } + + Ok(output) + } } struct TupleVariantVisitor<'a> { @@ -836,6 +987,7 @@ fn get_registration<'a, E: Error>( #[cfg(test)] mod tests { + use bincode::Options; use std::any::TypeId; use std::f32::consts::PI; @@ -1169,4 +1321,111 @@ mod tests { }); assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); } + + #[test] + fn should_deserialize_non_self_describing_binary() { + let mut map = HashMap::new(); + map.insert(64, 32); + + let expected = MyStruct { + primitive_value: 123, + option_value: Some(String::from("Hello world!")), + option_value_complex: Some(SomeStruct { foo: 123 }), + tuple_value: (PI, 1337), + list_value: vec![-2, -1, 0, 1, 2], + array_value: [-2, -1, 0, 1, 2], + map_value: map, + struct_value: SomeStruct { foo: 999999999 }, + tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")), + unit_enum: SomeEnum::Unit, + newtype_enum: SomeEnum::NewType(123), + tuple_enum: SomeEnum::Tuple(1.23, 3.21), + struct_enum: SomeEnum::Struct { + foo: String::from("Struct variant value"), + }, + custom_deserialize: CustomDeserialize { + value: 100, + inner_struct: SomeDeserializableStruct { foo: 101 }, + }, + }; + + let registry = get_registry(); + + let input = vec![ + 1, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 114, 101, 102, + 108, 101, 99, 116, 58, 58, 115, 101, 114, 100, 101, 58, 58, 100, 101, 58, 58, 116, 101, + 115, 116, 115, 58, 58, 77, 121, 83, 116, 114, 117, 99, 116, 123, 1, 12, 0, 0, 0, 0, 0, + 0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 1, 123, 0, 0, 0, 0, 0, + 0, 0, 219, 15, 73, 64, 57, 5, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 254, 255, 255, 255, 255, + 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 64, 32, 0, + 0, 0, 0, 0, 0, 0, 255, 201, 154, 59, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 84, 117, 112, + 108, 101, 32, 83, 116, 114, 117, 99, 116, 0, 0, 0, 0, 1, 0, 0, 0, 123, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 164, 112, 157, 63, 164, 112, 77, 64, 3, 0, 0, 0, 20, 0, 0, 0, 0, 0, + 0, 0, 83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, + 108, 117, 101, 100, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + ]; + + let deserializer = UntypedReflectDeserializer::new(®istry); + + let dynamic_output = bincode::DefaultOptions::new() + .with_fixint_encoding() + .deserialize_seed(deserializer, &input) + .unwrap(); + + let output = ::from_reflect(dynamic_output.as_ref()).unwrap(); + assert_eq!(expected, output); + } + + #[test] + fn should_deserialize_self_describing_binary() { + let mut map = HashMap::new(); + map.insert(64, 32); + + let expected = MyStruct { + primitive_value: 123, + option_value: Some(String::from("Hello world!")), + option_value_complex: Some(SomeStruct { foo: 123 }), + tuple_value: (PI, 1337), + list_value: vec![-2, -1, 0, 1, 2], + array_value: [-2, -1, 0, 1, 2], + map_value: map, + struct_value: SomeStruct { foo: 999999999 }, + tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")), + unit_enum: SomeEnum::Unit, + newtype_enum: SomeEnum::NewType(123), + tuple_enum: SomeEnum::Tuple(1.23, 3.21), + struct_enum: SomeEnum::Struct { + foo: String::from("Struct variant value"), + }, + custom_deserialize: CustomDeserialize { + value: 100, + inner_struct: SomeDeserializableStruct { foo: 101 }, + }, + }; + + let registry = get_registry(); + + let input = vec![ + 129, 217, 40, 98, 101, 118, 121, 95, 114, 101, 102, 108, 101, 99, 116, 58, 58, 115, + 101, 114, 100, 101, 58, 58, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, + 83, 116, 114, 117, 99, 116, 158, 123, 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, + 108, 100, 33, 145, 123, 146, 202, 64, 73, 15, 219, 205, 5, 57, 149, 254, 255, 0, 1, 2, + 149, 254, 255, 0, 1, 2, 129, 64, 32, 145, 206, 59, 154, 201, 255, 145, 172, 84, 117, + 112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 164, 85, 110, 105, 116, 129, 167, 78, + 101, 119, 84, 121, 112, 101, 123, 129, 165, 84, 117, 112, 108, 101, 146, 202, 63, 157, + 112, 164, 202, 64, 77, 112, 164, 129, 166, 83, 116, 114, 117, 99, 116, 145, 180, 83, + 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, 108, 117, + 101, 146, 100, 145, 101, + ]; + + let deserializer = UntypedReflectDeserializer::new(®istry); + + let dynamic_output = deserializer + .deserialize(&mut rmp_serde::Deserializer::new(input.as_slice())) + .unwrap(); + + let output = ::from_reflect(dynamic_output.as_ref()).unwrap(); + assert_eq!(expected, output); + } } diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index b55b0ee1f34ad..1dee7dda46bf5 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -533,6 +533,7 @@ mod tests { registry.register::(); registry.register::(); registry.register::(); + registry.register::(); registry.register::(); registry.register_type_data::(); registry.register::(); @@ -728,4 +729,102 @@ mod tests { }"#; assert_eq!(expected, output); } + + #[test] + fn should_serialize_non_self_describing_binary() { + let mut map = HashMap::new(); + map.insert(64, 32); + + let input = MyStruct { + primitive_value: 123, + option_value: Some(String::from("Hello world!")), + option_value_complex: Some(SomeStruct { foo: 123 }), + tuple_value: (PI, 1337), + list_value: vec![-2, -1, 0, 1, 2], + array_value: [-2, -1, 0, 1, 2], + map_value: map, + struct_value: SomeStruct { foo: 999999999 }, + tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")), + unit_enum: SomeEnum::Unit, + newtype_enum: SomeEnum::NewType(123), + tuple_enum: SomeEnum::Tuple(1.23, 3.21), + struct_enum: SomeEnum::Struct { + foo: String::from("Struct variant value"), + }, + custom_serialize: CustomSerialize { + value: 100, + inner_struct: SomeSerializableStruct { foo: 101 }, + }, + }; + + let registry = get_registry(); + + let serializer = ReflectSerializer::new(&input, ®istry); + let bytes = bincode::serialize(&serializer).unwrap(); + + let expected: Vec = vec![ + 1, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 114, 101, 102, + 108, 101, 99, 116, 58, 58, 115, 101, 114, 100, 101, 58, 58, 115, 101, 114, 58, 58, 116, + 101, 115, 116, 115, 58, 58, 77, 121, 83, 116, 114, 117, 99, 116, 123, 1, 12, 0, 0, 0, + 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 1, 123, 0, 0, 0, + 0, 0, 0, 0, 219, 15, 73, 64, 57, 5, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 254, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 254, 255, 255, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 64, 32, + 0, 0, 0, 0, 0, 0, 0, 255, 201, 154, 59, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 84, 117, + 112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 0, 0, 0, 0, 1, 0, 0, 0, 123, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 164, 112, 157, 63, 164, 112, 77, 64, 3, 0, 0, 0, 20, 0, 0, 0, 0, + 0, 0, 0, 83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, + 108, 117, 101, 100, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, + ]; + + assert_eq!(expected, bytes); + } + + #[test] + fn should_serialize_self_describing_binary() { + let mut map = HashMap::new(); + map.insert(64, 32); + + let input = MyStruct { + primitive_value: 123, + option_value: Some(String::from("Hello world!")), + option_value_complex: Some(SomeStruct { foo: 123 }), + tuple_value: (PI, 1337), + list_value: vec![-2, -1, 0, 1, 2], + array_value: [-2, -1, 0, 1, 2], + map_value: map, + struct_value: SomeStruct { foo: 999999999 }, + tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")), + unit_enum: SomeEnum::Unit, + newtype_enum: SomeEnum::NewType(123), + tuple_enum: SomeEnum::Tuple(1.23, 3.21), + struct_enum: SomeEnum::Struct { + foo: String::from("Struct variant value"), + }, + custom_serialize: CustomSerialize { + value: 100, + inner_struct: SomeSerializableStruct { foo: 101 }, + }, + }; + + let registry = get_registry(); + + let serializer = ReflectSerializer::new(&input, ®istry); + let bytes: Vec = rmp_serde::to_vec(&serializer).unwrap(); + + let expected: Vec = vec![ + 129, 217, 41, 98, 101, 118, 121, 95, 114, 101, 102, 108, 101, 99, 116, 58, 58, 115, + 101, 114, 100, 101, 58, 58, 115, 101, 114, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, + 121, 83, 116, 114, 117, 99, 116, 158, 123, 172, 72, 101, 108, 108, 111, 32, 119, 111, + 114, 108, 100, 33, 145, 123, 146, 202, 64, 73, 15, 219, 205, 5, 57, 149, 254, 255, 0, + 1, 2, 149, 254, 255, 0, 1, 2, 129, 64, 32, 145, 206, 59, 154, 201, 255, 145, 172, 84, + 117, 112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 164, 85, 110, 105, 116, 129, 167, + 78, 101, 119, 84, 121, 112, 101, 123, 129, 165, 84, 117, 112, 108, 101, 146, 202, 63, + 157, 112, 164, 202, 64, 77, 112, 164, 129, 166, 83, 116, 114, 117, 99, 116, 145, 180, + 83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, 108, 117, + 101, 146, 100, 145, 101, + ]; + + assert_eq!(expected, bytes); + } } diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 78002085a627c..71736d5c5ebc9 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -73,6 +73,7 @@ pub struct StructInfo { type_name: &'static str, type_id: TypeId, fields: Box<[NamedField]>, + field_names: Box<[&'static str]>, field_indices: HashMap<&'static str, usize>, #[cfg(feature = "documentation")] docs: Option<&'static str>, @@ -93,11 +94,14 @@ impl StructInfo { .map(|(index, field)| (field.name(), index)) .collect::>(); + let field_names = fields.iter().map(|field| field.name()).collect(); + Self { name, type_name: std::any::type_name::(), type_id: TypeId::of::(), fields: fields.to_vec().into_boxed_slice(), + field_names, field_indices, #[cfg(feature = "documentation")] docs: None, @@ -110,6 +114,11 @@ impl StructInfo { Self { docs, ..self } } + /// A slice containing the names of all fields in order. + pub fn field_names(&self) -> &[&'static str] { + &self.field_names + } + /// Get the field with the given name. pub fn field(&self, name: &str) -> Option<&NamedField> { self.field_indices diff --git a/crates/bevy_scene/Cargo.toml b/crates/bevy_scene/Cargo.toml index ded4ad5fd1e96..81fbb5ab7a78a 100644 --- a/crates/bevy_scene/Cargo.toml +++ b/crates/bevy_scene/Cargo.toml @@ -26,3 +26,7 @@ ron = "0.8.0" uuid = { version = "1.1", features = ["v4", "serde"] } anyhow = "1.0.4" thiserror = "1.0" + +[dev-dependencies] +postcard = { version = "1.0", features = ["alloc"] } +bincode = "1.3" diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 4e02e5f709bb2..0a225690168d2 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -273,6 +273,22 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { formatter.write_str("entities") } + fn visit_seq(self, mut seq: A) -> std::result::Result + where + A: SeqAccess<'de>, + { + let components = seq + .next_element_seed(ComponentDeserializer { + registry: self.registry, + })? + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; + + Ok(DynamicEntity { + entity: self.id, + components, + }) + } + fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, @@ -370,12 +386,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { #[cfg(test)] mod tests { - use crate::serde::SceneDeserializer; - use crate::DynamicSceneBuilder; + use crate::serde::{SceneDeserializer, SceneSerializer}; + use crate::{DynamicScene, DynamicSceneBuilder}; use bevy_app::AppTypeRegistry; use bevy_ecs::entity::EntityMap; use bevy_ecs::prelude::{Component, ReflectComponent, World}; - use bevy_reflect::Reflect; + use bevy_reflect::{FromReflect, Reflect, ReflectSerialize}; + use bincode::Options; use serde::de::DeserializeSeed; #[derive(Component, Reflect, Default)] @@ -388,6 +405,24 @@ mod tests { #[reflect(Component)] struct Baz(i32); + #[derive(Component, Reflect, Default)] + #[reflect(Component)] + struct MyComponent { + foo: [usize; 3], + bar: (f32, f32), + baz: MyEnum, + } + + #[derive(Reflect, FromReflect, Default)] + enum MyEnum { + #[default] + Unit, + Tuple(String), + Struct { + value: u32, + }, + } + fn create_world() -> World { let mut world = World::new(); let registry = AppTypeRegistry::default(); @@ -396,6 +431,12 @@ mod tests { registry.register::(); registry.register::(); registry.register::(); + registry.register::(); + registry.register::(); + registry.register::(); + registry.register_type_data::(); + registry.register::<[usize; 3]>(); + registry.register::<(f32, f32)>(); } world.insert_resource(registry); world @@ -487,4 +528,198 @@ mod tests { assert_eq!(2, dst_world.query::<&Bar>().iter(&dst_world).count()); assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count()); } + + #[test] + fn should_roundtrip_postcard() { + let mut world = create_world(); + + world.spawn(MyComponent { + foo: [1, 2, 3], + bar: (1.3, 3.7), + baz: MyEnum::Tuple("Hello World!".to_string()), + }); + + let registry = world.resource::(); + + let scene = DynamicScene::from_world(&world, registry); + + let scene_serializer = SceneSerializer::new(&scene, ®istry.0); + let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap(); + + assert_eq!( + vec![ + 1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, + 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, + 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101, + 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 + ], + serialized_scene + ); + + let scene_deserializer = SceneDeserializer { + type_registry: ®istry.0.read(), + }; + let deserialized_scene = scene_deserializer + .deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene)) + .unwrap(); + + assert_eq!(1, deserialized_scene.entities.len()); + assert_scene_eq(&scene, &deserialized_scene); + } + + #[test] + fn should_roundtrip_bincode() { + let mut world = create_world(); + + world.spawn(MyComponent { + foo: [1, 2, 3], + bar: (1.3, 3.7), + baz: MyEnum::Tuple("Hello World!".to_string()), + }); + + let registry = world.resource::(); + + let scene = DynamicScene::from_world(&world, registry); + + let scene_serializer = SceneSerializer::new(&scene, ®istry.0); + let serialized_scene = bincode::serialize(&scene_serializer).unwrap(); + + assert_eq!( + vec![ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, + 0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101, + 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, + 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101, + 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 + ], + serialized_scene + ); + + let scene_deserializer = SceneDeserializer { + type_registry: ®istry.0.read(), + }; + + let deserialized_scene = bincode::DefaultOptions::new() + .with_fixint_encoding() + .deserialize_seed(scene_deserializer, &serialized_scene) + .unwrap(); + + assert_eq!(1, deserialized_scene.entities.len()); + assert_scene_eq(&scene, &deserialized_scene); + } + + /// A crude equality checker for [`DynamicScene`], used solely for testing purposes. + fn assert_scene_eq(expected: &DynamicScene, received: &DynamicScene) { + assert_eq!( + expected.entities.len(), + received.entities.len(), + "entity count did not match", + ); + + for expected in &expected.entities { + let received = received + .entities + .iter() + .find(|dynamic_entity| dynamic_entity.entity == expected.entity) + .unwrap_or_else(|| panic!("missing entity (expected: `{}`)", expected.entity)); + + assert_eq!(expected.entity, received.entity, "entities did not match",); + + for expected in &expected.components { + let received = received + .components + .iter() + .find(|component| component.type_name() == expected.type_name()) + .unwrap_or_else(|| { + panic!("missing component (expected: `{}`)", expected.type_name()) + }); + + assert!( + expected + .reflect_partial_eq(received.as_ref()) + .unwrap_or_default(), + "components did not match: (expected: `{:?}`, received: `{:?}`)", + expected, + received + ); + } + } + } + + /// These tests just verify that that the [`assert_scene_eq`] function is working properly for our tests. + mod assert_scene_eq_tests { + use super::*; + + #[test] + #[should_panic(expected = "entity count did not match")] + fn should_panic_when_entity_count_not_eq() { + let mut world = create_world(); + let registry = world.resource::(); + let scene_a = DynamicScene::from_world(&world, registry); + + world.spawn(MyComponent { + foo: [1, 2, 3], + bar: (1.3, 3.7), + baz: MyEnum::Unit, + }); + + let registry = world.resource::(); + let scene_b = DynamicScene::from_world(&world, registry); + + assert_scene_eq(&scene_a, &scene_b); + } + + #[test] + #[should_panic(expected = "components did not match")] + fn should_panic_when_components_not_eq() { + let mut world = create_world(); + + let entity = world + .spawn(MyComponent { + foo: [1, 2, 3], + bar: (1.3, 3.7), + baz: MyEnum::Unit, + }) + .id(); + + let registry = world.resource::(); + let scene_a = DynamicScene::from_world(&world, registry); + + world.entity_mut(entity).insert(MyComponent { + foo: [3, 2, 1], + bar: (1.3, 3.7), + baz: MyEnum::Unit, + }); + + let registry = world.resource::(); + let scene_b = DynamicScene::from_world(&world, registry); + + assert_scene_eq(&scene_a, &scene_b); + } + + #[test] + #[should_panic(expected = "missing component")] + fn should_panic_when_missing_component() { + let mut world = create_world(); + + let entity = world + .spawn(MyComponent { + foo: [1, 2, 3], + bar: (1.3, 3.7), + baz: MyEnum::Unit, + }) + .id(); + + let registry = world.resource::(); + let scene_a = DynamicScene::from_world(&world, registry); + + world.entity_mut(entity).remove::(); + + let registry = world.resource::(); + let scene_b = DynamicScene::from_world(&world, registry); + + assert_scene_eq(&scene_a, &scene_b); + } + } }