From 696e9c6d7e0056a49376d2cca81c9191b5e27b33 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 24 Oct 2022 21:01:11 +0000 Subject: [PATCH] bevy_scene: Replace root list with struct (#6354) # Objective Scenes are currently represented as a list of entities. This is all we need currently, but we may want to add more data to this format in the future (metadata, asset lists, etc.). It would be nice to update the format in preparation of possible future changes. Doing so now (i.e., before 0.9) could mean reduced[^1] breakage for things added in 0.10. [^1]: Obviously, adding features runs the risk of breaking things regardless. But if all features added are for whatever reason optional or well-contained, then users should at least have an easier time updating. ## Solution Made the scene root a struct rather than a list. ```rust ( entities: [ // Entity data here... ] ) ``` --- ## Changelog * The scene format now puts the entity list in a newly added `entities` field, rather than having it be the root object ## Migration Guide The scene file format now uses a struct as the root object rather than a list of entities. The list of entities is now found in the `entities` field of this struct. ```rust // OLD [ ( entity: 0, components: [ // Components... ] ), ] // NEW ( entities: [ ( entity: 0, components: [ // Components... ] ), ] ) ``` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> --- assets/scenes/load_scene_example.scn.ron | 84 +++++++------- crates/bevy_scene/src/serde.rs | 133 ++++++++++++++++++++--- 2 files changed, 159 insertions(+), 58 deletions(-) diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index 015b41774f3c0..696f0c79274f1 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -1,44 +1,46 @@ -[ - ( - entity: 0, - components: [ - { - "bevy_transform::components::transform::Transform": ( - translation: ( - x: 0.0, - y: 0.0, - z: 0.0 +( + entities: [ + ( + entity: 0, + components: [ + { + "bevy_transform::components::transform::Transform": ( + translation: ( + x: 0.0, + y: 0.0, + z: 0.0 + ), + rotation: (0.0, 0.0, 0.0, 1.0), + scale: ( + x: 1.0, + y: 1.0, + z: 1.0 + ), ), - rotation: (0.0, 0.0, 0.0, 1.0), - scale: ( + }, + { + "scene::ComponentB": ( + value: "hello", + ), + }, + { + "scene::ComponentA": ( x: 1.0, - y: 1.0, - z: 1.0 + y: 2.0, + ), + }, + ], + ), + ( + entity: 1, + components: [ + { + "scene::ComponentA": ( + x: 3.0, + y: 4.0, ), - ), - }, - { - "scene::ComponentB": ( - value: "hello", - ), - }, - { - "scene::ComponentA": ( - x: 1.0, - y: 2.0, - ), - }, - ], - ), - ( - entity: 1, - components: [ - { - "scene::ComponentA": ( - x: 3.0, - y: 4.0, - ), - }, - ], - ), -] + }, + ], + ), + ] +) diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 8a3e5383af170..7515785692a8c 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -7,8 +7,16 @@ use bevy_reflect::{ use serde::{ de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor}, ser::{SerializeSeq, SerializeStruct}, - Deserialize, Serialize, + Deserialize, Deserializer, Serialize, Serializer, }; +use std::fmt::Formatter; + +pub const SCENE_STRUCT: &str = "Scene"; +pub const SCENE_ENTITIES: &str = "entities"; + +pub const ENTITY_STRUCT: &str = "Entity"; +pub const ENTITY_FIELD_ENTITY: &str = "entity"; +pub const ENTITY_FIELD_COMPONENTS: &str = "components"; pub struct SceneSerializer<'a> { pub scene: &'a DynamicScene, @@ -26,8 +34,30 @@ impl<'a> Serialize for SceneSerializer<'a> { where S: serde::Serializer, { - let mut state = serializer.serialize_seq(Some(self.scene.entities.len()))?; - for entity in &self.scene.entities { + let mut state = serializer.serialize_struct(SCENE_STRUCT, 1)?; + state.serialize_field( + SCENE_ENTITIES, + &EntitiesSerializer { + entities: &self.scene.entities, + registry: self.registry, + }, + )?; + state.end() + } +} + +pub struct EntitiesSerializer<'a> { + pub entities: &'a [DynamicEntity], + pub registry: &'a TypeRegistryArc, +} + +impl<'a> Serialize for EntitiesSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_seq(Some(self.entities.len()))?; + for entity in self.entities { state.serialize_element(&EntitySerializer { entity, registry: self.registry, @@ -81,6 +111,19 @@ impl<'a> Serialize for ComponentsSerializer<'a> { } } +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum SceneField { + Entities, +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum EntityField { + Entity, + Components, +} + pub struct SceneDeserializer<'a> { pub type_registry: &'a TypeRegistry, } @@ -92,10 +135,77 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> { where D: serde::Deserializer<'de>, { - Ok(DynamicScene { - entities: deserializer.deserialize_seq(SceneEntitySeqVisitor { + deserializer.deserialize_struct( + SCENE_STRUCT, + &[SCENE_ENTITIES], + SceneVisitor { + type_registry: self.type_registry, + }, + ) + } +} + +struct SceneVisitor<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { + type Value = DynamicScene; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("scene struct") + } + + fn visit_map(self, mut map: A) -> std::result::Result + where + A: MapAccess<'de>, + { + let mut entities = None; + while let Some(key) = map.next_key()? { + match key { + SceneField::Entities => { + if entities.is_some() { + return Err(Error::duplicate_field(SCENE_ENTITIES)); + } + entities = Some(map.next_value_seed(SceneEntitySeqDeserializer { + type_registry: self.type_registry, + })?); + } + } + } + + let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?; + + Ok(DynamicScene { entities }) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let entities = seq + .next_element_seed(SceneEntitySeqDeserializer { type_registry: self.type_registry, - })?, + })? + .ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?; + + Ok(DynamicScene { entities }) + } +} + +pub struct SceneEntitySeqDeserializer<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> DeserializeSeed<'de> for SceneEntitySeqDeserializer<'a> { + type Value = Vec; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(SceneEntitySeqVisitor { + type_registry: self.type_registry, }) } } @@ -147,17 +257,6 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { } } -#[derive(Deserialize)] -#[serde(field_identifier, rename_all = "lowercase")] -enum EntityField { - Entity, - Components, -} - -pub const ENTITY_STRUCT: &str = "Entity"; -pub const ENTITY_FIELD_ENTITY: &str = "entity"; -pub const ENTITY_FIELD_COMPONENTS: &str = "components"; - struct SceneEntityVisitor<'a> { pub registry: &'a TypeRegistry, }