bevy_scene: Serialize entities to map (#6416)
# Objective
Entities are unique, however, this is not reflected in the scene format. Currently, entities are stored in a list where a user could inadvertently create a duplicate of the same entity.
## Solution
Switch from the list representation to a map representation for entities.
---
## Changelog
* The `entities` field in the scene format is now a map of entity ID to entity data
## Migration Guide
The scene format now stores its collection of entities in a map rather than a list:
```rust
// OLD
(
entities: [
(
entity: 12,
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
),
),
},
),
],
)
// NEW
(
entities: {
12: (
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
),
),
},
),
},
)
```
This commit is contained in:
parent
8cdd977a12
commit
bb968f41bc
@ -1,7 +1,6 @@
|
|||||||
(
|
(
|
||||||
entities: [
|
entities: {
|
||||||
(
|
0: (
|
||||||
entity: 0,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_transform::components::transform::Transform": (
|
"bevy_transform::components::transform::Transform": (
|
||||||
translation: (
|
translation: (
|
||||||
@ -25,8 +24,7 @@
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
1: (
|
||||||
entity: 1,
|
|
||||||
components: {
|
components: {
|
||||||
"scene::ComponentA": (
|
"scene::ComponentA": (
|
||||||
x: 3.0,
|
x: 3.0,
|
||||||
@ -34,5 +32,5 @@
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use bevy_utils::HashSet;
|
|||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor},
|
de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor},
|
||||||
ser::{SerializeSeq, SerializeStruct},
|
ser::SerializeStruct,
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
};
|
};
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
@ -15,7 +15,6 @@ pub const SCENE_STRUCT: &str = "Scene";
|
|||||||
pub const SCENE_ENTITIES: &str = "entities";
|
pub const SCENE_ENTITIES: &str = "entities";
|
||||||
|
|
||||||
pub const ENTITY_STRUCT: &str = "Entity";
|
pub const ENTITY_STRUCT: &str = "Entity";
|
||||||
pub const ENTITY_FIELD_ENTITY: &str = "entity";
|
|
||||||
pub const ENTITY_FIELD_COMPONENTS: &str = "components";
|
pub const ENTITY_FIELD_COMPONENTS: &str = "components";
|
||||||
|
|
||||||
pub struct SceneSerializer<'a> {
|
pub struct SceneSerializer<'a> {
|
||||||
@ -56,12 +55,15 @@ impl<'a> Serialize for EntitiesSerializer<'a> {
|
|||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_seq(Some(self.entities.len()))?;
|
let mut state = serializer.serialize_map(Some(self.entities.len()))?;
|
||||||
for entity in self.entities {
|
for entity in self.entities {
|
||||||
state.serialize_element(&EntitySerializer {
|
state.serialize_entry(
|
||||||
entity,
|
&entity.entity,
|
||||||
registry: self.registry,
|
&EntitySerializer {
|
||||||
})?;
|
entity,
|
||||||
|
registry: self.registry,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
@ -77,8 +79,7 @@ impl<'a> Serialize for EntitySerializer<'a> {
|
|||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 2)?;
|
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
|
||||||
state.serialize_field(ENTITY_FIELD_ENTITY, &self.entity.entity)?;
|
|
||||||
state.serialize_field(
|
state.serialize_field(
|
||||||
ENTITY_FIELD_COMPONENTS,
|
ENTITY_FIELD_COMPONENTS,
|
||||||
&ComponentsSerializer {
|
&ComponentsSerializer {
|
||||||
@ -120,7 +121,6 @@ enum SceneField {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(field_identifier, rename_all = "lowercase")]
|
#[serde(field_identifier, rename_all = "lowercase")]
|
||||||
enum EntityField {
|
enum EntityField {
|
||||||
Entity,
|
|
||||||
Components,
|
Components,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
|
|||||||
if entities.is_some() {
|
if entities.is_some() {
|
||||||
return Err(Error::duplicate_field(SCENE_ENTITIES));
|
return Err(Error::duplicate_field(SCENE_ENTITIES));
|
||||||
}
|
}
|
||||||
entities = Some(map.next_value_seed(SceneEntitySeqDeserializer {
|
entities = Some(map.next_value_seed(SceneEntitiesDeserializer {
|
||||||
type_registry: self.type_registry,
|
type_registry: self.type_registry,
|
||||||
})?);
|
})?);
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
|
|||||||
A: SeqAccess<'de>,
|
A: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let entities = seq
|
let entities = seq
|
||||||
.next_element_seed(SceneEntitySeqDeserializer {
|
.next_element_seed(SceneEntitiesDeserializer {
|
||||||
type_registry: self.type_registry,
|
type_registry: self.type_registry,
|
||||||
})?
|
})?
|
||||||
.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
|
.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
|
||||||
@ -193,42 +193,44 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneEntitySeqDeserializer<'a> {
|
pub struct SceneEntitiesDeserializer<'a> {
|
||||||
pub type_registry: &'a TypeRegistry,
|
pub type_registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de> DeserializeSeed<'de> for SceneEntitySeqDeserializer<'a> {
|
impl<'a, 'de> DeserializeSeed<'de> for SceneEntitiesDeserializer<'a> {
|
||||||
type Value = Vec<DynamicEntity>;
|
type Value = Vec<DynamicEntity>;
|
||||||
|
|
||||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
deserializer.deserialize_seq(SceneEntitySeqVisitor {
|
deserializer.deserialize_map(SceneEntitiesVisitor {
|
||||||
type_registry: self.type_registry,
|
type_registry: self.type_registry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SceneEntitySeqVisitor<'a> {
|
struct SceneEntitiesVisitor<'a> {
|
||||||
pub type_registry: &'a TypeRegistry,
|
pub type_registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> {
|
impl<'a, 'de> Visitor<'de> for SceneEntitiesVisitor<'a> {
|
||||||
type Value = Vec<DynamicEntity>;
|
type Value = Vec<DynamicEntity>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
formatter.write_str("list of entities")
|
formatter.write_str("map of entities")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
where
|
where
|
||||||
A: SeqAccess<'de>,
|
A: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut entities = Vec::new();
|
let mut entities = Vec::new();
|
||||||
while let Some(entity) = seq.next_element_seed(SceneEntityDeserializer {
|
while let Some(id) = map.next_key::<u32>()? {
|
||||||
type_registry: self.type_registry,
|
let entity = map.next_value_seed(SceneEntityDeserializer {
|
||||||
})? {
|
id,
|
||||||
|
type_registry: self.type_registry,
|
||||||
|
})?;
|
||||||
entities.push(entity);
|
entities.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,6 +239,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneEntityDeserializer<'a> {
|
pub struct SceneEntityDeserializer<'a> {
|
||||||
|
pub id: u32,
|
||||||
pub type_registry: &'a TypeRegistry,
|
pub type_registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +252,9 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> {
|
|||||||
{
|
{
|
||||||
deserializer.deserialize_struct(
|
deserializer.deserialize_struct(
|
||||||
ENTITY_STRUCT,
|
ENTITY_STRUCT,
|
||||||
&[ENTITY_FIELD_ENTITY, ENTITY_FIELD_COMPONENTS],
|
&[ENTITY_FIELD_COMPONENTS],
|
||||||
SceneEntityVisitor {
|
SceneEntityVisitor {
|
||||||
|
id: self.id,
|
||||||
registry: self.type_registry,
|
registry: self.type_registry,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -258,6 +262,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SceneEntityVisitor<'a> {
|
struct SceneEntityVisitor<'a> {
|
||||||
|
pub id: u32,
|
||||||
pub registry: &'a TypeRegistry,
|
pub registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,16 +277,9 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
|
|||||||
where
|
where
|
||||||
A: MapAccess<'de>,
|
A: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut id = None;
|
|
||||||
let mut components = None;
|
let mut components = None;
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match key {
|
match key {
|
||||||
EntityField::Entity => {
|
|
||||||
if id.is_some() {
|
|
||||||
return Err(Error::duplicate_field(ENTITY_FIELD_ENTITY));
|
|
||||||
}
|
|
||||||
id = Some(map.next_value::<u32>()?);
|
|
||||||
}
|
|
||||||
EntityField::Components => {
|
EntityField::Components => {
|
||||||
if components.is_some() {
|
if components.is_some() {
|
||||||
return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS));
|
return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS));
|
||||||
@ -294,15 +292,11 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = id
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_ENTITY))?;
|
|
||||||
|
|
||||||
let components = components
|
let components = components
|
||||||
.take()
|
.take()
|
||||||
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
|
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
|
||||||
Ok(DynamicEntity {
|
Ok(DynamicEntity {
|
||||||
entity: *entity,
|
entity: self.id,
|
||||||
components,
|
components,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -420,29 +414,26 @@ mod tests {
|
|||||||
let scene = builder.build();
|
let scene = builder.build();
|
||||||
|
|
||||||
let expected = r#"(
|
let expected = r#"(
|
||||||
entities: [
|
entities: {
|
||||||
(
|
0: (
|
||||||
entity: 0,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
1: (
|
||||||
entity: 1,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
"bevy_scene::serde::tests::Bar": (345),
|
"bevy_scene::serde::tests::Bar": (345),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
2: (
|
||||||
entity: 2,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
"bevy_scene::serde::tests::Bar": (345),
|
"bevy_scene::serde::tests::Bar": (345),
|
||||||
"bevy_scene::serde::tests::Baz": (789),
|
"bevy_scene::serde::tests::Baz": (789),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
},
|
||||||
)"#;
|
)"#;
|
||||||
let output = scene
|
let output = scene
|
||||||
.serialize_ron(&world.resource::<AppTypeRegistry>().0)
|
.serialize_ron(&world.resource::<AppTypeRegistry>().0)
|
||||||
@ -455,29 +446,26 @@ mod tests {
|
|||||||
let world = create_world();
|
let world = create_world();
|
||||||
|
|
||||||
let input = r#"(
|
let input = r#"(
|
||||||
entities: [
|
entities: {
|
||||||
(
|
0: (
|
||||||
entity: 0,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
1: (
|
||||||
entity: 1,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
"bevy_scene::serde::tests::Bar": (345),
|
"bevy_scene::serde::tests::Bar": (345),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
2: (
|
||||||
entity: 2,
|
|
||||||
components: {
|
components: {
|
||||||
"bevy_scene::serde::tests::Foo": (123),
|
"bevy_scene::serde::tests::Foo": (123),
|
||||||
"bevy_scene::serde::tests::Bar": (345),
|
"bevy_scene::serde::tests::Bar": (345),
|
||||||
"bevy_scene::serde::tests::Baz": (789),
|
"bevy_scene::serde::tests::Baz": (789),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
},
|
||||||
)"#;
|
)"#;
|
||||||
let mut deserializer = ron::de::Deserializer::from_str(input).unwrap();
|
let mut deserializer = ron::de::Deserializer::from_str(input).unwrap();
|
||||||
let scene_deserializer = SceneDeserializer {
|
let scene_deserializer = SceneDeserializer {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user