Skip to content

Commit

Permalink
bevy_scene: Replace root list with struct (bevyengine#6354)
Browse files Browse the repository at this point in the history
# 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>
  • Loading branch information
2 people authored and james7132 committed Oct 28, 2022
1 parent a48c318 commit edb1e29
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 58 deletions.
84 changes: 43 additions & 41 deletions 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,
),
},
],
),
]
},
],
),
]
)
133 changes: 116 additions & 17 deletions crates/bevy_scene/src/serde.rs
Expand Up @@ -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,
Expand All @@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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,
Expand Down Expand Up @@ -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,
}
Expand All @@ -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<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<DynamicEntity>;

fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(SceneEntitySeqVisitor {
type_registry: self.type_registry,
})
}
}
Expand Down Expand Up @@ -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,
}
Expand Down

0 comments on commit edb1e29

Please sign in to comment.