bevy/crates/bevy_scene/src/serde.rs
Carter Anderson 3c8fae2390
Improved Entity Mapping and Cloning (#17687)
Fixes #17535

Bevy's approach to handling "entity mapping" during spawning and cloning
needs some work. The addition of
[Relations](https://github.com/bevyengine/bevy/pull/17398) both
[introduced a new "duplicate entities" bug when spawning scenes in the
scene system](#17535) and made the weaknesses of the current mapping
system exceedingly clear:

1. Entity mapping requires _a ton_ of boilerplate (implement or derive
VisitEntities and VisitEntitesMut, then register / reflect MapEntities).
Knowing the incantation is challenging and if you forget to do it in
part or in whole, spawning subtly breaks.
2. Entity mapping a spawned component in scenes incurs unnecessary
overhead: look up ReflectMapEntities, create a _brand new temporary
instance_ of the component using FromReflect, map the entities in that
instance, and then apply that on top of the actual component using
reflection. We can do much better.

Additionally, while our new [Entity cloning
system](https://github.com/bevyengine/bevy/pull/16132) is already pretty
great, it has some areas we can make better:

* It doesn't expose semantic info about the clone (ex: ignore or "clone
empty"), meaning we can't key off of that in places where it would be
useful, such as scene spawning. Rather than duplicating this info across
contexts, I think it makes more sense to add that info to the clone
system, especially given that we'd like to use cloning code in some of
our spawning scenarios.
* EntityCloner is currently built in a way that prioritizes a single
entity clone
* EntityCloner's recursive cloning is built to be done "inside out" in a
parallel context (queue commands that each have a clone of
EntityCloner). By making EntityCloner the orchestrator of the clone we
can remove internal arcs, improve the clarity of the code, make
EntityCloner mutable again, and simplify the builder code.
* EntityCloner does not currently take into account entity mapping. This
is necessary to do true "bullet proof" cloning, would allow us to unify
the per-component scene spawning and cloning UX, and ultimately would
allow us to use EntityCloner in place of raw reflection for scenes like
`Scene(World)` (which would give us a nice performance boost: fewer
archetype moves, less reflection overhead).

## Solution

### Improved Entity Mapping

First, components now have first-class "entity visiting and mapping"
behavior:

```rust
#[derive(Component, Reflect)]
#[reflect(Component)]
struct Inventory {
    size: usize,
    #[entities]
    items: Vec<Entity>,
}
```

Any field with the `#[entities]` annotation will be viewable and
mappable when cloning and spawning scenes.

Compare that to what was required before!

```rust
#[derive(Component, Reflect, VisitEntities, VisitEntitiesMut)]
#[reflect(Component, MapEntities)]
struct Inventory {
    #[visit_entities(ignore)]
    size: usize,
    items: Vec<Entity>,
}
```

Additionally, for relationships `#[entities]` is implied, meaning this
"just works" in scenes and cloning:

```rust
#[derive(Component, Reflect)]
#[relationship(relationship_target = Children)]
#[reflect(Component)]
struct ChildOf(pub Entity);
```

Note that Component _does not_ implement `VisitEntities` directly.
Instead, it has `Component::visit_entities` and
`Component::visit_entities_mut` methods. This is for a few reasons:

1. We cannot implement `VisitEntities for C: Component` because that
would conflict with our impl of VisitEntities for anything that
implements `IntoIterator<Item=Entity>`. Preserving that impl is more
important from a UX perspective.
2. We should not implement `Component: VisitEntities` VisitEntities in
the Component derive, as that would increase the burden of manual
Component trait implementors.
3. Making VisitEntitiesMut directly callable for components would make
it easy to invalidate invariants defined by a component author. By
putting it in the `Component` impl, we can make it harder to call
naturally / unavailable to autocomplete using `fn
visit_entities_mut(this: &mut Self, ...)`.

`ReflectComponent::apply_or_insert` is now
`ReflectComponent::apply_or_insert_mapped`. By moving mapping inside
this impl, we remove the need to go through the reflection system to do
entity mapping, meaning we no longer need to create a clone of the
target component, map the entities in that component, and patch those
values on top. This will make spawning mapped entities _much_ faster
(The default `Component::visit_entities_mut` impl is an inlined empty
function, so it will incur no overhead for unmapped entities).

### The Bug Fix

To solve #17535, spawning code now skips entities with the new
`ComponentCloneBehavior::Ignore` and
`ComponentCloneBehavior::RelationshipTarget` variants (note
RelationshipTarget is a temporary "workaround" variant that allows
scenes to skip these components. This is a temporary workaround that can
be removed as these cases should _really_ be using EntityCloner logic,
which should be done in a followup PR. When that is done,
`ComponentCloneBehavior::RelationshipTarget` can be merged into the
normal `ComponentCloneBehavior::Custom`).

### Improved Cloning

* `Option<ComponentCloneHandler>` has been replaced by
`ComponentCloneBehavior`, which encodes additional intent and context
(ex: `Default`, `Ignore`, `Custom`, `RelationshipTarget` (this last one
is temporary)).
* Global per-world entity cloning configuration has been removed. This
felt overly complicated, increased our API surface, and felt too
generic. Each clone context can have different requirements (ex: what a
user wants in a specific system, what a scene spawner wants, etc). I'd
prefer to see how far context-specific EntityCloners get us first.
* EntityCloner's internals have been reworked to remove Arcs and make it
mutable.
* EntityCloner is now directly stored on EntityClonerBuilder,
simplifying the code somewhat
* EntityCloner's "bundle scratch" pattern has been moved into the new
BundleScratch type, improving its usability and making it usable in
other contexts (such as future cross-world cloning code). Currently this
is still private, but with some higher level safe APIs it could be used
externally for making dynamic bundles
* EntityCloner's recursive cloning behavior has been "externalized". It
is now responsible for orchestrating recursive clones, meaning it no
longer needs to be sharable/clone-able across threads / read-only.
* EntityCloner now does entity mapping during clones, like scenes do.
This gives behavior parity and also makes it more generically useful.
* `RelatonshipTarget::RECURSIVE_SPAWN` is now
`RelationshipTarget::LINKED_SPAWN`, and this field is used when cloning
relationship targets to determine if cloning should happen recursively.
The new `LINKED_SPAWN` term was picked to make it more generically
applicable across spawning and cloning scenarios.

## Next Steps

* I think we should adapt EntityCloner to support cross world cloning. I
think this PR helps set the stage for that by making the internals
slightly more generalized. We could have a CrossWorldEntityCloner that
reuses a lot of this infrastructure.
* Once we support cross world cloning, we should use EntityCloner to
spawn `Scene(World)` scenes. This would yield significant performance
benefits (no archetype moves, less reflection overhead).

---------

Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-06 22:13:41 +00:00

1037 lines
32 KiB
Rust

//! `serde` serialization and deserialization implementation for Bevy scenes.
use crate::{DynamicEntity, DynamicScene};
use bevy_ecs::entity::Entity;
use bevy_platform_support::collections::HashSet;
use bevy_reflect::{
serde::{
ReflectDeserializer, TypeRegistrationDeserializer, TypedReflectDeserializer,
TypedReflectSerializer,
},
PartialReflect, ReflectFromReflect, TypeRegistry,
};
use core::fmt::Formatter;
use serde::{
de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor},
ser::{SerializeMap, SerializeStruct},
Deserialize, Deserializer, Serialize, Serializer,
};
/// Name of the serialized scene struct type.
pub const SCENE_STRUCT: &str = "Scene";
/// Name of the serialized resources field in a scene struct.
pub const SCENE_RESOURCES: &str = "resources";
/// Name of the serialized entities field in a scene struct.
pub const SCENE_ENTITIES: &str = "entities";
/// Name of the serialized entity struct type.
pub const ENTITY_STRUCT: &str = "Entity";
/// Name of the serialized component field in an entity struct.
pub const ENTITY_FIELD_COMPONENTS: &str = "components";
/// Serializer for a [`DynamicScene`].
///
/// Helper object defining Bevy's serialize format for a [`DynamicScene`] and implementing
/// the [`Serialize`] trait for use with Serde.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_scene::{DynamicScene, serde::SceneSerializer};
/// # let mut world = World::default();
/// # world.insert_resource(AppTypeRegistry::default());
/// // Get the type registry
/// let registry = world.resource::<AppTypeRegistry>();
/// let registry = registry.read();
///
/// // Get a DynamicScene to serialize, for example from the World itself
/// let scene = DynamicScene::from_world(&world);
///
/// // Create a serializer for that DynamicScene, using the associated TypeRegistry
/// let scene_serializer = SceneSerializer::new(&scene, &registry);
///
/// // Serialize through any serde-compatible Serializer
/// let ron_string = bevy_scene::ron::ser::to_string(&scene_serializer);
/// ```
pub struct SceneSerializer<'a> {
/// The scene to serialize.
pub scene: &'a DynamicScene,
/// The type registry containing the types present in the scene.
pub registry: &'a TypeRegistry,
}
impl<'a> SceneSerializer<'a> {
/// Create a new serializer from a [`DynamicScene`] and an associated [`TypeRegistry`].
///
/// The type registry must contain all types present in the scene. This is generally the case
/// if you obtain both the scene and the registry from the same [`World`].
///
/// [`World`]: bevy_ecs::world::World
pub fn new(scene: &'a DynamicScene, registry: &'a TypeRegistry) -> Self {
SceneSerializer { scene, registry }
}
}
impl<'a> Serialize for SceneSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?;
state.serialize_field(
SCENE_RESOURCES,
&SceneMapSerializer {
entries: &self.scene.resources,
registry: self.registry,
},
)?;
state.serialize_field(
SCENE_ENTITIES,
&EntitiesSerializer {
entities: &self.scene.entities,
registry: self.registry,
},
)?;
state.end()
}
}
/// Handles serialization of multiple entities as a map of entity id to serialized entity.
pub struct EntitiesSerializer<'a> {
/// The entities to serialize.
pub entities: &'a [DynamicEntity],
/// Type registry in which the component types used by the entities are registered.
pub registry: &'a TypeRegistry,
}
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_map(Some(self.entities.len()))?;
for entity in self.entities {
state.serialize_entry(
&entity.entity,
&EntitySerializer {
entity,
registry: self.registry,
},
)?;
}
state.end()
}
}
/// Handles entity serialization as a map of component type to component value.
pub struct EntitySerializer<'a> {
/// The entity to serialize.
pub entity: &'a DynamicEntity,
/// Type registry in which the component types used by the entity are registered.
pub registry: &'a TypeRegistry,
}
impl<'a> Serialize for EntitySerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
state.serialize_field(
ENTITY_FIELD_COMPONENTS,
&SceneMapSerializer {
entries: &self.entity.components,
registry: self.registry,
},
)?;
state.end()
}
}
/// Handles serializing a list of values with a unique type as a map of type to value.
///
/// Used to serialize scene resources in [`SceneSerializer`] and entity components in [`EntitySerializer`].
/// Note that having several entries of the same type in `entries` will lead to an error when using the RON format and
/// deserializing through [`SceneMapDeserializer`].
///
/// Note: The entries are sorted by type path before they're serialized.
pub struct SceneMapSerializer<'a> {
/// List of boxed values of unique type to serialize.
pub entries: &'a [Box<dyn PartialReflect>],
/// Type registry in which the types used in `entries` are registered.
pub registry: &'a TypeRegistry,
}
impl<'a> Serialize for SceneMapSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(Some(self.entries.len()))?;
let sorted_entries = {
let mut entries = self
.entries
.iter()
.map(|entry| {
(
entry.get_represented_type_info().unwrap().type_path(),
entry.as_partial_reflect(),
)
})
.collect::<Vec<_>>();
entries.sort_by_key(|(type_path, _)| *type_path);
entries
};
for (type_path, partial_reflect) in sorted_entries {
state.serialize_entry(
type_path,
&TypedReflectSerializer::new(partial_reflect, self.registry),
)?;
}
state.end()
}
}
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum SceneField {
Resources,
Entities,
}
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum EntityField {
Components,
}
/// Handles scene deserialization.
pub struct SceneDeserializer<'a> {
/// Type registry in which the components and resources types used in the scene to deserialize are registered.
pub type_registry: &'a TypeRegistry,
}
impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> {
type Value = DynamicScene;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_struct(
SCENE_STRUCT,
&[SCENE_RESOURCES, 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) -> core::fmt::Result {
formatter.write_str("scene struct")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let resources = seq
.next_element_seed(SceneMapDeserializer {
registry: self.type_registry,
})?
.ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?;
let entities = seq
.next_element_seed(SceneEntitiesDeserializer {
type_registry: self.type_registry,
})?
.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
Ok(DynamicScene {
resources,
entities,
})
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut resources = None;
let mut entities = None;
while let Some(key) = map.next_key()? {
match key {
SceneField::Resources => {
if resources.is_some() {
return Err(Error::duplicate_field(SCENE_RESOURCES));
}
resources = Some(map.next_value_seed(SceneMapDeserializer {
registry: self.type_registry,
})?);
}
SceneField::Entities => {
if entities.is_some() {
return Err(Error::duplicate_field(SCENE_ENTITIES));
}
entities = Some(map.next_value_seed(SceneEntitiesDeserializer {
type_registry: self.type_registry,
})?);
}
}
}
let resources = resources.ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?;
let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
Ok(DynamicScene {
resources,
entities,
})
}
}
/// Handles deserialization for a collection of entities.
pub struct SceneEntitiesDeserializer<'a> {
/// Type registry in which the component types used by the entities to deserialize are registered.
pub type_registry: &'a TypeRegistry,
}
impl<'a, 'de> DeserializeSeed<'de> for SceneEntitiesDeserializer<'a> {
type Value = Vec<DynamicEntity>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(SceneEntitiesVisitor {
type_registry: self.type_registry,
})
}
}
struct SceneEntitiesVisitor<'a> {
pub type_registry: &'a TypeRegistry,
}
impl<'a, 'de> Visitor<'de> for SceneEntitiesVisitor<'a> {
type Value = Vec<DynamicEntity>;
fn expecting(&self, formatter: &mut Formatter) -> core::fmt::Result {
formatter.write_str("map of entities")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut entities = Vec::new();
while let Some(entity) = map.next_key::<Entity>()? {
let entity = map.next_value_seed(SceneEntityDeserializer {
entity,
type_registry: self.type_registry,
})?;
entities.push(entity);
}
Ok(entities)
}
}
/// Handle deserialization of an entity and its components.
pub struct SceneEntityDeserializer<'a> {
/// Id of the deserialized entity.
pub entity: Entity,
/// Type registry in which the component types used by the entity to deserialize are registered.
pub type_registry: &'a TypeRegistry,
}
impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> {
type Value = DynamicEntity;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_struct(
ENTITY_STRUCT,
&[ENTITY_FIELD_COMPONENTS],
SceneEntityVisitor {
entity: self.entity,
registry: self.type_registry,
},
)
}
}
struct SceneEntityVisitor<'a> {
pub entity: Entity,
pub registry: &'a TypeRegistry,
}
impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
type Value = DynamicEntity;
fn expecting(&self, formatter: &mut Formatter) -> core::fmt::Result {
formatter.write_str("entities")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let components = seq
.next_element_seed(SceneMapDeserializer {
registry: self.registry,
})?
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
Ok(DynamicEntity {
entity: self.entity,
components,
})
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut components = None;
while let Some(key) = map.next_key()? {
match key {
EntityField::Components => {
if components.is_some() {
return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS));
}
components = Some(map.next_value_seed(SceneMapDeserializer {
registry: self.registry,
})?);
}
}
}
let components = components
.take()
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
Ok(DynamicEntity {
entity: self.entity,
components,
})
}
}
/// Handles deserialization of a sequence of values with unique types.
pub struct SceneMapDeserializer<'a> {
/// Type registry in which the types of the values to deserialize are registered.
pub registry: &'a TypeRegistry,
}
impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> {
type Value = Vec<Box<dyn PartialReflect>>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(SceneMapVisitor {
registry: self.registry,
})
}
}
struct SceneMapVisitor<'a> {
pub registry: &'a TypeRegistry,
}
impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> {
type Value = Vec<Box<dyn PartialReflect>>;
fn expecting(&self, formatter: &mut Formatter) -> core::fmt::Result {
formatter.write_str("map of reflect types")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut dynamic_properties = Vec::new();
while let Some(entity) = seq.next_element_seed(ReflectDeserializer::new(self.registry))? {
dynamic_properties.push(entity);
}
Ok(dynamic_properties)
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut added = <HashSet<_>>::default();
let mut entries = Vec::new();
while let Some(registration) =
map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))?
{
if !added.insert(registration.type_id()) {
return Err(Error::custom(format_args!(
"duplicate reflect type: `{}`",
registration.type_info().type_path(),
)));
}
let value =
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?;
// Attempt to convert using FromReflect.
let value = self
.registry
.get(registration.type_id())
.and_then(|tr| tr.data::<ReflectFromReflect>())
.and_then(|fr| fr.from_reflect(value.as_partial_reflect()))
.map(PartialReflect::into_partial_reflect)
.unwrap_or(value);
entries.push(value);
}
Ok(entries)
}
}
#[cfg(test)]
mod tests {
use crate::{
ron,
serde::{SceneDeserializer, SceneSerializer},
DynamicScene, DynamicSceneBuilder,
};
use bevy_ecs::{
entity::{hash_map::EntityHashMap, Entity},
prelude::{Component, ReflectComponent, ReflectResource, Resource, World},
query::{With, Without},
reflect::AppTypeRegistry,
world::FromWorld,
};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bincode::Options;
use serde::{de::DeserializeSeed, Deserialize, Serialize};
use std::io::BufReader;
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct Foo(i32);
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct Bar(i32);
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct Baz(i32);
// De/serialize as hex.
mod qux {
use serde::{de::Error, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(value: &u32, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{:X}", value))
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: Deserializer<'de>,
{
u32::from_str_radix(<&str as Deserialize>::deserialize(deserializer)?, 16)
.map_err(Error::custom)
}
}
#[derive(Component, Copy, Clone, Reflect, Debug, PartialEq, Serialize, Deserialize)]
#[reflect(Component, Serialize, Deserialize)]
struct Qux(#[serde(with = "qux")] u32);
#[derive(Component, Reflect, Default)]
#[reflect(Component)]
struct MyComponent {
foo: [usize; 3],
bar: (f32, f32),
baz: MyEnum,
}
#[derive(Reflect, Default)]
enum MyEnum {
#[default]
Unit,
Tuple(String),
Struct {
value: u32,
},
}
#[derive(Resource, Reflect, Default)]
#[reflect(Resource)]
struct MyResource {
foo: i32,
}
#[derive(Clone, Component, Reflect, PartialEq)]
#[reflect(Component, PartialEq)]
struct MyEntityRef(#[entities] Entity);
impl FromWorld for MyEntityRef {
fn from_world(_world: &mut World) -> Self {
Self(Entity::PLACEHOLDER)
}
}
fn create_world() -> World {
let mut world = World::new();
let registry = AppTypeRegistry::default();
{
let mut registry = registry.write();
registry.register::<Foo>();
registry.register::<Bar>();
registry.register::<Baz>();
registry.register::<Qux>();
registry.register::<MyComponent>();
registry.register::<MyEnum>();
registry.register::<String>();
registry.register_type_data::<String, ReflectSerialize>();
registry.register::<[usize; 3]>();
registry.register::<(f32, f32)>();
registry.register::<MyEntityRef>();
registry.register::<Entity>();
registry.register::<MyResource>();
}
world.insert_resource(registry);
world
}
#[test]
fn should_serialize() {
let mut world = create_world();
let a = world.spawn(Foo(123)).id();
let b = world.spawn((Foo(123), Bar(345))).id();
let c = world.spawn((Foo(123), Bar(345), Baz(789))).id();
world.insert_resource(MyResource { foo: 123 });
let scene = DynamicSceneBuilder::from_world(&world)
.extract_entities([a, b, c].into_iter())
.extract_resources()
.build();
let expected = r#"(
resources: {
"bevy_scene::serde::tests::MyResource": (
foo: 123,
),
},
entities: {
4294967296: (
components: {
"bevy_scene::serde::tests::Foo": (123),
},
),
4294967297: (
components: {
"bevy_scene::serde::tests::Bar": (345),
"bevy_scene::serde::tests::Foo": (123),
},
),
4294967298: (
components: {
"bevy_scene::serde::tests::Bar": (345),
"bevy_scene::serde::tests::Baz": (789),
"bevy_scene::serde::tests::Foo": (123),
},
),
},
)"#;
let output = scene
.serialize(&world.resource::<AppTypeRegistry>().read())
.unwrap();
assert_eq!(expected, output);
}
#[test]
fn should_deserialize() {
let world = create_world();
let input = r#"(
resources: {
"bevy_scene::serde::tests::MyResource": (
foo: 123,
),
},
entities: {
4294967296: (
components: {
"bevy_scene::serde::tests::Foo": (123),
},
),
4294967297: (
components: {
"bevy_scene::serde::tests::Foo": (123),
"bevy_scene::serde::tests::Bar": (345),
},
),
4294967298: (
components: {
"bevy_scene::serde::tests::Foo": (123),
"bevy_scene::serde::tests::Bar": (345),
"bevy_scene::serde::tests::Baz": (789),
},
),
},
)"#;
let mut deserializer = ron::de::Deserializer::from_str(input).unwrap();
let scene_deserializer = SceneDeserializer {
type_registry: &world.resource::<AppTypeRegistry>().read(),
};
let scene = scene_deserializer.deserialize(&mut deserializer).unwrap();
assert_eq!(
1,
scene.resources.len(),
"expected `resources` to contain 1 resource"
);
assert_eq!(
3,
scene.entities.len(),
"expected `entities` to contain 3 entities"
);
let mut map = EntityHashMap::default();
let mut dst_world = create_world();
scene.write_to_world(&mut dst_world, &mut map).unwrap();
let my_resource = dst_world.get_resource::<MyResource>();
assert!(my_resource.is_some());
let my_resource = my_resource.unwrap();
assert_eq!(my_resource.foo, 123);
assert_eq!(3, dst_world.query::<&Foo>().iter(&dst_world).count());
assert_eq!(2, dst_world.query::<&Bar>().iter(&dst_world).count());
assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count());
}
fn roundtrip_ron(world: &World) -> (DynamicScene, DynamicScene) {
let scene = DynamicScene::from_world(world);
let registry = world.resource::<AppTypeRegistry>().read();
let serialized = scene.serialize(&registry).unwrap();
let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap();
let scene_deserializer = SceneDeserializer {
type_registry: &registry,
};
let deserialized_scene = scene_deserializer.deserialize(&mut deserializer).unwrap();
(scene, deserialized_scene)
}
#[test]
fn should_roundtrip_with_later_generations_and_obsolete_references() {
let mut world = create_world();
world.spawn_empty().despawn();
let a = world.spawn_empty().id();
let foo = world.spawn(MyEntityRef(a)).insert(Foo(123)).id();
world.despawn(a);
world.spawn(MyEntityRef(foo)).insert(Bar(123));
let (scene, deserialized_scene) = roundtrip_ron(&world);
let mut map = EntityHashMap::default();
let mut dst_world = create_world();
deserialized_scene
.write_to_world(&mut dst_world, &mut map)
.unwrap();
assert_eq!(2, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
let bar_to_foo = dst_world
.query_filtered::<&MyEntityRef, Without<Foo>>()
.get_single(&dst_world)
.cloned()
.unwrap();
let foo = dst_world
.query_filtered::<Entity, With<Foo>>()
.get_single(&dst_world)
.unwrap();
assert_eq!(foo, bar_to_foo.0);
assert!(dst_world
.query_filtered::<&MyEntityRef, With<Foo>>()
.iter(&dst_world)
.all(|r| world.get_entity(r.0).is_err()));
}
#[test]
fn should_roundtrip_with_custom_serialization() {
let mut world = create_world();
let qux = Qux(42);
world.spawn(qux);
let (scene, deserialized_scene) = roundtrip_ron(&world);
assert_eq!(1, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
let mut world = create_world();
deserialized_scene
.write_to_world(&mut world, &mut EntityHashMap::default())
.unwrap();
assert_eq!(&qux, world.query::<&Qux>().single(&world));
}
#[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::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, registry);
let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap();
assert_eq!(
vec![
0, 1, 128, 128, 128, 128, 16, 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: registry,
};
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_messagepack() {
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::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, registry);
let mut buf = Vec::new();
let mut ser = rmp_serde::Serializer::new(&mut buf);
scene_serializer.serialize(&mut ser).unwrap();
assert_eq!(
vec![
146, 128, 129, 207, 0, 0, 0, 1, 0, 0, 0, 0, 145, 129, 217, 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, 147, 147, 1,
2, 3, 146, 202, 63, 166, 102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112,
108, 101, 172, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
],
buf
);
let scene_deserializer = SceneDeserializer {
type_registry: registry,
};
let mut reader = BufReader::new(buf.as_slice());
let deserialized_scene = scene_deserializer
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
.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::<AppTypeRegistry>();
let registry = &registry.read();
let scene = DynamicScene::from_world(&world);
let scene_serializer = SceneSerializer::new(&scene, registry);
let serialized_scene = bincode::serialize(&scene_serializer).unwrap();
assert_eq!(
vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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: registry,
};
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.get_represented_type_info().unwrap().type_path()
== expected.get_represented_type_info().unwrap().type_path()
})
.unwrap_or_else(|| {
panic!(
"missing component (expected: `{}`)",
expected.get_represented_type_info().unwrap().type_path()
)
});
assert!(
expected
.reflect_partial_eq(received.as_ref())
.unwrap_or_default(),
"components did not match: (expected: `{expected:?}`, received: `{received:?}`)",
);
}
}
}
/// These tests just verify 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 scene_a = DynamicScene::from_world(&world);
world.spawn(MyComponent {
foo: [1, 2, 3],
bar: (1.3, 3.7),
baz: MyEnum::Unit,
});
let scene_b = DynamicScene::from_world(&world);
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 scene_a = DynamicScene::from_world(&world);
world.entity_mut(entity).insert(MyComponent {
foo: [3, 2, 1],
bar: (1.3, 3.7),
baz: MyEnum::Unit,
});
let scene_b = DynamicScene::from_world(&world);
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 scene_a = DynamicScene::from_world(&world);
world.entity_mut(entity).remove::<MyComponent>();
let scene_b = DynamicScene::from_world(&world);
assert_scene_eq(&scene_a, &scene_b);
}
}
}