(De) serialize resources in scenes (#6846)

# Objective

Co-Authored-By: davier
[bricedavier@gmail.com](mailto:bricedavier@gmail.com)
Fixes #3576.
Adds a `resources` field in scene serialization data to allow
de/serializing resources that have reflection enabled.

## Solution

Most of this code is taken from a previous closed PR:
https://github.com/bevyengine/bevy/pull/3580. Most of the credit goes to
@Davier , what I did was mostly getting it to work on the latest main
branch of Bevy, along with adding a few asserts in the currently
existing tests to be sure everything is working properly.

This PR changes the scene format to include resources in this way:
```
(
  resources: {
    // List of resources here, keyed by resource type name.
  },
  entities: [
    // Previous scene format here
  ],
)
```

An example taken from the tests:
```
(
  resources: {
    "bevy_scene::serde::tests::MyResource": (
      foo: 123,
    ),
  },
  entities: {
    // Previous scene format here
  },
)
```
For this, a `resources` fields has been added on the `DynamicScene` and
the `DynamicSceneBuilder` structs. The latter now also has a method
named `extract_resources` to properly extract the existing resources
registered in the local type registry, in a similar way to
`extract_entities`.


---

## Changelog


Added: Reflect resources registered in the type registry used by dynamic
scenes will now be properly de/serialized in scene data.

## Migration Guide

Since the scene format has been changed, the user may not be able to use
scenes saved prior to this PR due to the `resources` scene field being
missing. ~~To preserve backwards compatibility, I will try to make the
`resources` fully optional so that old scenes can be loaded without
issue.~~

## TODOs

- [x] I may have to update a few doc blocks still referring to dynamic
scenes as mere container of entities, since they now include resources
as well.
- [x] ~~I want to make the `resources` key optional, as specified in the
Migration Guide, so that old scenes will be compatible with this
change.~~ Since this would only be trivial for ron format, I think it
might be better to consider it in a separate PR/discussion to figure out
if it could be done for binary serialization too.
- [x] I suppose it might be a good idea to add a resources in the scene
example so that users will quickly notice they can serialize resources
just like entities.

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Francesco 2023-03-20 22:17:02 +01:00 committed by GitHub
parent 6a85eb3d7e
commit 7b38de0a64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 297 additions and 64 deletions

View File

@ -1,4 +1,9 @@
( (
resources: {
"scene::ResourceA": (
score: 2,
),
},
entities: { entities: {
0: ( 0: (
components: { components: {

View File

@ -10,10 +10,13 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use crate::serde::SceneSerializer; use crate::serde::SceneSerializer;
use bevy_ecs::reflect::ReflectResource;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use serde::Serialize; use serde::Serialize;
/// A collection of serializable dynamic entities, each with its own run-time defined set of components. /// A collection of serializable resources and dynamic entities.
///
/// Each dynamic entity in the collection contains its own run-time defined set of components.
/// To spawn a dynamic scene, you can use either: /// To spawn a dynamic scene, you can use either:
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic) /// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity /// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
@ -23,6 +26,7 @@ use serde::Serialize;
#[derive(Default, TypeUuid)] #[derive(Default, TypeUuid)]
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
pub struct DynamicScene { pub struct DynamicScene {
pub resources: Vec<Box<dyn Reflect>>,
pub entities: Vec<DynamicEntity>, pub entities: Vec<DynamicEntity>,
} }
@ -47,15 +51,16 @@ impl DynamicScene {
DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone()); DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone());
builder.extract_entities(world.iter_entities().map(|entity| entity.id())); builder.extract_entities(world.iter_entities().map(|entity| entity.id()));
builder.extract_resources();
builder.build() builder.build()
} }
/// Write the dynamic entities and their corresponding components to the given world. /// Write the resources, the dynamic entities, and their corresponding components to the given world.
/// ///
/// This method will return a [`SceneSpawnError`] if a type either is not registered /// This method will return a [`SceneSpawnError`] if a type either is not registered
/// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the /// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the
/// [`Component`](bevy_ecs::component::Component) trait. /// [`Component`](bevy_ecs::component::Component) or [`Resource`](bevy_ecs::prelude::Resource) trait.
pub fn write_to_world_with( pub fn write_to_world_with(
&self, &self,
world: &mut World, world: &mut World,
@ -64,6 +69,23 @@ impl DynamicScene {
) -> Result<(), SceneSpawnError> { ) -> Result<(), SceneSpawnError> {
let type_registry = type_registry.read(); let type_registry = type_registry.read();
for resource in &self.resources {
let registration = type_registry
.get_with_name(resource.type_name())
.ok_or_else(|| SceneSpawnError::UnregisteredType {
type_name: resource.type_name().to_string(),
})?;
let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource {
type_name: resource.type_name().to_string(),
}
})?;
// If the world already contains an instance of the given resource
// just apply the (possibly) new value, otherwise insert the resource
reflect_resource.apply_or_insert(world, &**resource);
}
for scene_entity in &self.entities { for scene_entity in &self.entities {
// Fetch the entity with the given entity id from the `entity_map` // Fetch the entity with the given entity id from the `entity_map`
// or spawn a new entity with a transiently unique id if there is // or spawn a new entity with a transiently unique id if there is
@ -105,7 +127,7 @@ impl DynamicScene {
Ok(()) Ok(())
} }
/// Write the dynamic entities and their corresponding components to the given world. /// Write the resources, the dynamic entities, and their corresponding components to the given world.
/// ///
/// This method will return a [`SceneSpawnError`] if a type either is not registered /// This method will return a [`SceneSpawnError`] if a type either is not registered
/// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the /// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the

View File

@ -1,10 +1,16 @@
use crate::{DynamicEntity, DynamicScene}; use crate::{DynamicEntity, DynamicScene};
use bevy_app::AppTypeRegistry; use bevy_app::AppTypeRegistry;
use bevy_ecs::{prelude::Entity, reflect::ReflectComponent, world::World}; use bevy_ecs::component::ComponentId;
use bevy_ecs::{
prelude::Entity,
reflect::{ReflectComponent, ReflectResource},
world::World,
};
use bevy_reflect::Reflect;
use bevy_utils::default; use bevy_utils::default;
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities. /// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources.
/// ///
/// # Entity Order /// # Entity Order
/// ///
@ -31,6 +37,7 @@ use std::collections::BTreeMap;
/// let dynamic_scene = builder.build(); /// let dynamic_scene = builder.build();
/// ``` /// ```
pub struct DynamicSceneBuilder<'w> { pub struct DynamicSceneBuilder<'w> {
extracted_resources: BTreeMap<ComponentId, Box<dyn Reflect>>,
extracted_scene: BTreeMap<u32, DynamicEntity>, extracted_scene: BTreeMap<u32, DynamicEntity>,
type_registry: AppTypeRegistry, type_registry: AppTypeRegistry,
original_world: &'w World, original_world: &'w World,
@ -41,6 +48,7 @@ impl<'w> DynamicSceneBuilder<'w> {
/// All components registered in that world's [`AppTypeRegistry`] resource will be extracted. /// All components registered in that world's [`AppTypeRegistry`] resource will be extracted.
pub fn from_world(world: &'w World) -> Self { pub fn from_world(world: &'w World) -> Self {
Self { Self {
extracted_resources: default(),
extracted_scene: default(), extracted_scene: default(),
type_registry: world.resource::<AppTypeRegistry>().clone(), type_registry: world.resource::<AppTypeRegistry>().clone(),
original_world: world, original_world: world,
@ -51,6 +59,7 @@ impl<'w> DynamicSceneBuilder<'w> {
/// Only components registered in the given [`AppTypeRegistry`] will be extracted. /// Only components registered in the given [`AppTypeRegistry`] will be extracted.
pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self { pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self {
Self { Self {
extracted_resources: default(),
extracted_scene: default(), extracted_scene: default(),
type_registry, type_registry,
original_world: world, original_world: world,
@ -63,6 +72,7 @@ impl<'w> DynamicSceneBuilder<'w> {
/// [`Self::remove_empty_entities`] before building the scene. /// [`Self::remove_empty_entities`] before building the scene.
pub fn build(self) -> DynamicScene { pub fn build(self) -> DynamicScene {
DynamicScene { DynamicScene {
resources: self.extracted_resources.into_values().collect(),
entities: self.extracted_scene.into_values().collect(), entities: self.extracted_scene.into_values().collect(),
} }
} }
@ -126,17 +136,20 @@ impl<'w> DynamicSceneBuilder<'w> {
let entity = self.original_world.entity(entity); let entity = self.original_world.entity(entity);
for component_id in entity.archetype().components() { for component_id in entity.archetype().components() {
let reflect_component = self let mut extract_and_push = || {
let type_id = self
.original_world .original_world
.components() .components()
.get_info(component_id) .get_info(component_id)?
.and_then(|info| type_registry.get(info.type_id().unwrap())) .type_id()?;
.and_then(|registration| registration.data::<ReflectComponent>()) let component = type_registry
.and_then(|reflect_component| reflect_component.reflect(entity)); .get(type_id)?
.data::<ReflectComponent>()?
if let Some(reflect_component) = reflect_component { .reflect(entity)?;
entry.components.push(reflect_component.clone_value()); entry.components.push(component.clone_value());
} Some(())
};
extract_and_push();
} }
self.extracted_scene.insert(index, entry); self.extracted_scene.insert(index, entry);
} }
@ -144,13 +157,59 @@ impl<'w> DynamicSceneBuilder<'w> {
drop(type_registry); drop(type_registry);
self self
} }
/// Extract resources from the builder's [`World`].
///
/// Only resources registered in the builder's [`AppTypeRegistry`] will be extracted.
/// Re-extracting a resource that was already extracted will have no effect.
/// ```
/// # use bevy_scene::DynamicSceneBuilder;
/// # use bevy_app::AppTypeRegistry;
/// # use bevy_ecs::prelude::{ReflectResource, Resource, World};
/// # use bevy_reflect::Reflect;
/// #[derive(Resource, Default, Reflect)]
/// #[reflect(Resource)]
/// struct MyResource;
///
/// # let mut world = World::default();
/// # world.init_resource::<AppTypeRegistry>();
/// world.insert_resource(MyResource);
///
/// let mut builder = DynamicSceneBuilder::from_world(&world);
/// builder.extract_resources();
/// let scene = builder.build();
/// ```
pub fn extract_resources(&mut self) -> &mut Self {
let type_registry = self.type_registry.read();
for (component_id, _) in self.original_world.storages().resources.iter() {
let mut extract_and_push = || {
let type_id = self
.original_world
.components()
.get_info(component_id)?
.type_id()?;
let resource = type_registry
.get(type_id)?
.data::<ReflectResource>()?
.reflect(self.original_world)?;
self.extracted_resources
.insert(component_id, resource.clone_value());
Some(())
};
extract_and_push();
}
drop(type_registry);
self
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy_app::AppTypeRegistry; use bevy_app::AppTypeRegistry;
use bevy_ecs::{ use bevy_ecs::{
component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World, component::Component, prelude::Entity, prelude::Resource, query::With,
reflect::ReflectComponent, reflect::ReflectResource, world::World,
}; };
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
@ -160,10 +219,15 @@ mod tests {
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)] #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct ComponentA; struct ComponentA;
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)] #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Component)] #[reflect(Component)]
struct ComponentB; struct ComponentB;
#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]
#[reflect(Resource)]
struct ResourceA;
#[test] #[test]
fn extract_one_entity() { fn extract_one_entity() {
let mut world = World::default(); let mut world = World::default();
@ -303,4 +367,41 @@ mod tests {
assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities.len(), 1);
assert_eq!(scene.entities[0].entity, entity_a.index()); assert_eq!(scene.entities[0].entity, entity_a.index());
} }
#[test]
fn extract_one_resource() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ResourceA>();
world.insert_resource(atr);
world.insert_resource(ResourceA);
let mut builder = DynamicSceneBuilder::from_world(&world);
builder.extract_resources();
let scene = builder.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
}
#[test]
fn extract_one_resource_twice() {
let mut world = World::default();
let atr = AppTypeRegistry::default();
atr.write().register::<ResourceA>();
world.insert_resource(atr);
world.insert_resource(ResourceA);
let mut builder = DynamicSceneBuilder::from_world(&world);
builder.extract_resources();
builder.extract_resources();
let scene = builder.build();
assert_eq!(scene.resources.len(), 1);
assert!(scene.resources[0].represents::<ResourceA>());
}
} }

View File

@ -1,7 +1,7 @@
use bevy_app::AppTypeRegistry; use bevy_app::AppTypeRegistry;
use bevy_ecs::{ use bevy_ecs::{
entity::EntityMap, entity::EntityMap,
reflect::{ReflectComponent, ReflectMapEntities}, reflect::{ReflectComponent, ReflectMapEntities, ReflectResource},
world::World, world::World,
}; };
use bevy_reflect::TypeUuid; use bevy_reflect::TypeUuid;
@ -61,6 +61,33 @@ impl Scene {
}; };
let type_registry = type_registry.read(); let type_registry = type_registry.read();
// Resources archetype
for (component_id, _) in self.world.storages().resources.iter() {
let component_info = self
.world
.components()
.get_info(component_id)
.expect("component_ids in archetypes should have ComponentInfo");
let type_id = component_info
.type_id()
.expect("reflected resources must have a type_id");
let registration =
type_registry
.get(type_id)
.ok_or_else(|| SceneSpawnError::UnregisteredType {
type_name: component_info.name().to_string(),
})?;
let reflect_resource = registration.data::<ReflectResource>().ok_or_else(|| {
SceneSpawnError::UnregisteredResource {
type_name: component_info.name().to_string(),
}
})?;
reflect_resource.copy(&self.world, world);
}
for archetype in self.world.archetypes().iter() { for archetype in self.world.archetypes().iter() {
for scene_entity in archetype.entities() { for scene_entity in archetype.entities() {
let entity = *instance_info let entity = *instance_info

View File

@ -45,6 +45,8 @@ pub struct SceneSpawner {
pub enum SceneSpawnError { pub enum SceneSpawnError {
#[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")] #[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")]
UnregisteredComponent { type_name: String }, UnregisteredComponent { type_name: String },
#[error("scene contains the unregistered resource `{type_name}`. consider adding `#[reflect(Resource)]` to your type")]
UnregisteredResource { type_name: String },
#[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::<T>()`")] #[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::<T>()`")]
UnregisteredType { type_name: String }, UnregisteredType { type_name: String },
#[error("scene does not exist")] #[error("scene does not exist")]

View File

@ -15,6 +15,7 @@ use serde::{
use std::fmt::Formatter; use std::fmt::Formatter;
pub const SCENE_STRUCT: &str = "Scene"; pub const SCENE_STRUCT: &str = "Scene";
pub const SCENE_RESOURCES: &str = "resources";
pub const SCENE_ENTITIES: &str = "entities"; pub const SCENE_ENTITIES: &str = "entities";
pub const ENTITY_STRUCT: &str = "Entity"; pub const ENTITY_STRUCT: &str = "Entity";
@ -36,7 +37,14 @@ impl<'a> Serialize for SceneSerializer<'a> {
where where
S: serde::Serializer, S: serde::Serializer,
{ {
let mut state = serializer.serialize_struct(SCENE_STRUCT, 1)?; 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( state.serialize_field(
SCENE_ENTITIES, SCENE_ENTITIES,
&EntitiesSerializer { &EntitiesSerializer {
@ -85,8 +93,8 @@ impl<'a> Serialize for EntitySerializer<'a> {
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?; let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
state.serialize_field( state.serialize_field(
ENTITY_FIELD_COMPONENTS, ENTITY_FIELD_COMPONENTS,
&ComponentsSerializer { &SceneMapSerializer {
components: &self.entity.components, entries: &self.entity.components,
registry: self.registry, registry: self.registry,
}, },
)?; )?;
@ -94,21 +102,21 @@ impl<'a> Serialize for EntitySerializer<'a> {
} }
} }
pub struct ComponentsSerializer<'a> { pub struct SceneMapSerializer<'a> {
pub components: &'a [Box<dyn Reflect>], pub entries: &'a [Box<dyn Reflect>],
pub registry: &'a TypeRegistryArc, pub registry: &'a TypeRegistryArc,
} }
impl<'a> Serialize for ComponentsSerializer<'a> { impl<'a> Serialize for SceneMapSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
let mut state = serializer.serialize_map(Some(self.components.len()))?; let mut state = serializer.serialize_map(Some(self.entries.len()))?;
for component in self.components { for reflect in self.entries {
state.serialize_entry( state.serialize_entry(
component.type_name(), reflect.type_name(),
&TypedReflectSerializer::new(&**component, &self.registry.read()), &TypedReflectSerializer::new(&**reflect, &self.registry.read()),
)?; )?;
} }
state.end() state.end()
@ -118,6 +126,7 @@ impl<'a> Serialize for ComponentsSerializer<'a> {
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")] #[serde(field_identifier, rename_all = "lowercase")]
enum SceneField { enum SceneField {
Resources,
Entities, Entities,
} }
@ -140,7 +149,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> {
{ {
deserializer.deserialize_struct( deserializer.deserialize_struct(
SCENE_STRUCT, SCENE_STRUCT,
&[SCENE_ENTITIES], &[SCENE_RESOURCES, SCENE_ENTITIES],
SceneVisitor { SceneVisitor {
type_registry: self.type_registry, type_registry: self.type_registry,
}, },
@ -163,9 +172,18 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
where where
A: MapAccess<'de>, A: MapAccess<'de>,
{ {
let mut resources = None;
let mut entities = None; let mut entities = None;
while let Some(key) = map.next_key()? { while let Some(key) = map.next_key()? {
match 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 => { SceneField::Entities => {
if entities.is_some() { if entities.is_some() {
return Err(Error::duplicate_field(SCENE_ENTITIES)); return Err(Error::duplicate_field(SCENE_ENTITIES));
@ -177,22 +195,35 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
} }
} }
let resources = resources.ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?;
let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?; let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
Ok(DynamicScene { entities }) Ok(DynamicScene {
resources,
entities,
})
} }
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where where
A: SeqAccess<'de>, 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 let entities = seq
.next_element_seed(SceneEntitiesDeserializer { .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))?;
Ok(DynamicScene { entities }) Ok(DynamicScene {
resources,
entities,
})
} }
} }
@ -281,7 +312,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
A: SeqAccess<'de>, A: SeqAccess<'de>,
{ {
let components = seq let components = seq
.next_element_seed(ComponentDeserializer { .next_element_seed(SceneMapDeserializer {
registry: self.registry, registry: self.registry,
})? })?
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
@ -304,7 +335,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS)); return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS));
} }
components = Some(map.next_value_seed(ComponentDeserializer { components = Some(map.next_value_seed(SceneMapDeserializer {
registry: self.registry, registry: self.registry,
})?); })?);
} }
@ -321,32 +352,32 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
} }
} }
pub struct ComponentDeserializer<'a> { pub struct SceneMapDeserializer<'a> {
pub registry: &'a TypeRegistry, pub registry: &'a TypeRegistry,
} }
impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> { impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> {
type Value = Vec<Box<dyn Reflect>>; type Value = Vec<Box<dyn Reflect>>;
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: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
deserializer.deserialize_map(ComponentVisitor { deserializer.deserialize_map(SceneMapVisitor {
registry: self.registry, registry: self.registry,
}) })
} }
} }
struct ComponentVisitor<'a> { struct SceneMapVisitor<'a> {
pub registry: &'a TypeRegistry, pub registry: &'a TypeRegistry,
} }
impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> {
type Value = Vec<Box<dyn Reflect>>; type Value = Vec<Box<dyn Reflect>>;
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("map of components") formatter.write_str("map of reflect types")
} }
fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error> fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
@ -354,23 +385,23 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
A: MapAccess<'de>, A: MapAccess<'de>,
{ {
let mut added = HashSet::new(); let mut added = HashSet::new();
let mut components = Vec::new(); let mut entries = Vec::new();
while let Some(registration) = while let Some(registration) =
map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))? map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))?
{ {
if !added.insert(registration.type_id()) { if !added.insert(registration.type_id()) {
return Err(Error::custom(format_args!( return Err(Error::custom(format_args!(
"duplicate component: `{}`", "duplicate reflect type: `{}`",
registration.type_name() registration.type_name()
))); )));
} }
components.push( entries.push(
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?, map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
); );
} }
Ok(components) Ok(entries)
} }
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
@ -394,7 +425,7 @@ mod tests {
use crate::{DynamicScene, DynamicSceneBuilder}; use crate::{DynamicScene, DynamicSceneBuilder};
use bevy_app::AppTypeRegistry; use bevy_app::AppTypeRegistry;
use bevy_ecs::entity::EntityMap; use bevy_ecs::entity::EntityMap;
use bevy_ecs::prelude::{Component, ReflectComponent, World}; use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World};
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize}; use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
use bincode::Options; use bincode::Options;
use serde::de::DeserializeSeed; use serde::de::DeserializeSeed;
@ -429,6 +460,12 @@ mod tests {
}, },
} }
#[derive(Resource, Reflect, Default)]
#[reflect(Resource)]
struct MyResource {
foo: i32,
}
fn create_world() -> World { fn create_world() -> World {
let mut world = World::new(); let mut world = World::new();
let registry = AppTypeRegistry::default(); let registry = AppTypeRegistry::default();
@ -443,6 +480,7 @@ mod tests {
registry.register_type_data::<String, ReflectSerialize>(); registry.register_type_data::<String, ReflectSerialize>();
registry.register::<[usize; 3]>(); registry.register::<[usize; 3]>();
registry.register::<(f32, f32)>(); registry.register::<(f32, f32)>();
registry.register::<MyResource>();
} }
world.insert_resource(registry); world.insert_resource(registry);
world world
@ -456,11 +494,19 @@ mod tests {
let b = world.spawn((Foo(123), Bar(345))).id(); let b = world.spawn((Foo(123), Bar(345))).id();
let c = world.spawn((Foo(123), Bar(345), Baz(789))).id(); let c = world.spawn((Foo(123), Bar(345), Baz(789))).id();
world.insert_resource(MyResource { foo: 123 });
let mut builder = DynamicSceneBuilder::from_world(&world); let mut builder = DynamicSceneBuilder::from_world(&world);
builder.extract_entities([a, b, c].into_iter()); builder.extract_entities([a, b, c].into_iter());
builder.extract_resources();
let scene = builder.build(); let scene = builder.build();
let expected = r#"( let expected = r#"(
resources: {
"bevy_scene::serde::tests::MyResource": (
foo: 123,
),
},
entities: { entities: {
0: ( 0: (
components: { components: {
@ -493,6 +539,11 @@ mod tests {
let world = create_world(); let world = create_world();
let input = r#"( let input = r#"(
resources: {
"bevy_scene::serde::tests::MyResource": (
foo: 123,
),
},
entities: { entities: {
0: ( 0: (
components: { components: {
@ -520,6 +571,11 @@ mod tests {
}; };
let scene = scene_deserializer.deserialize(&mut deserializer).unwrap(); let scene = scene_deserializer.deserialize(&mut deserializer).unwrap();
assert_eq!(
1,
scene.resources.len(),
"expected `resources` to contain 1 resource"
);
assert_eq!( assert_eq!(
3, 3,
scene.entities.len(), scene.entities.len(),
@ -530,6 +586,11 @@ mod tests {
let mut dst_world = create_world(); let mut dst_world = create_world();
scene.write_to_world(&mut dst_world, &mut map).unwrap(); 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!(3, dst_world.query::<&Foo>().iter(&dst_world).count());
assert_eq!(2, dst_world.query::<&Bar>().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()); assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count());
@ -554,10 +615,10 @@ mod tests {
assert_eq!( assert_eq!(
vec![ vec![
1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 0, 1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101,
100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112,
110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101, 111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72,
108, 108, 111, 32, 87, 111, 114, 108, 100, 33 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
], ],
serialized_scene serialized_scene
); );
@ -594,11 +655,11 @@ mod tests {
assert_eq!( assert_eq!(
vec![ vec![
145, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 146, 128, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121,
111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, 102, 67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166,
102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, 108, 102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101,
108, 111, 32, 87, 111, 114, 108, 100, 33 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
], ],
buf buf
); );
@ -635,12 +696,12 @@ mod tests {
assert_eq!( assert_eq!(
vec![ 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, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101, 37, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58,
58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111,
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, 109, 112, 111, 110, 101, 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 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, 3, 0, 0, 0, 0, 0, 0, 0, 102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0,
108, 108, 111, 32, 87, 111, 114, 108, 100, 33 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
], ],
serialized_scene serialized_scene
); );

View File

@ -1,8 +1,6 @@
//! This example illustrates loading scenes from files. //! This example illustrates loading scenes from files.
use std::fs::File;
use std::io::Write;
use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration}; use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration};
use std::{fs::File, io::Write};
fn main() { fn main() {
App::new() App::new()
@ -14,6 +12,7 @@ fn main() {
})) }))
.register_type::<ComponentA>() .register_type::<ComponentA>()
.register_type::<ComponentB>() .register_type::<ComponentB>()
.register_type::<ResourceA>()
.add_systems( .add_systems(
Startup, Startup,
(save_scene_system, load_scene_system, infotext_system), (save_scene_system, load_scene_system, infotext_system),
@ -57,6 +56,13 @@ impl FromWorld for ComponentB {
} }
} }
// Resources can be serialized in scenes as well, with the same requirements `Component`s have.
#[derive(Resource, Reflect, Default)]
#[reflect(Resource)]
struct ResourceA {
pub score: u32,
}
// The initial scene file will be loaded below and not change when the scene is saved // The initial scene file will be loaded below and not change when the scene is saved
const SCENE_FILE_PATH: &str = "scenes/load_scene_example.scn.ron"; const SCENE_FILE_PATH: &str = "scenes/load_scene_example.scn.ron";
@ -75,7 +81,10 @@ fn load_scene_system(mut commands: Commands, asset_server: Res<AssetServer>) {
// This system logs all ComponentA components in our world. Try making a change to a ComponentA in // This system logs all ComponentA components in our world. Try making a change to a ComponentA in
// load_scene_example.scn. You should immediately see the changes appear in the console. // load_scene_example.scn. You should immediately see the changes appear in the console.
fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) { fn log_system(
query: Query<(Entity, &ComponentA), Changed<ComponentA>>,
res: Option<Res<ResourceA>>,
) {
for (entity, component_a) in &query { for (entity, component_a) in &query {
info!(" Entity({})", entity.index()); info!(" Entity({})", entity.index());
info!( info!(
@ -83,6 +92,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
component_a.x, component_a.y component_a.x, component_a.y
); );
} }
if let Some(res) = res {
if res.is_added() {
info!(" New ResourceA: {{ score: {} }}\n", res.score);
}
}
} }
fn save_scene_system(world: &mut World) { fn save_scene_system(world: &mut World) {
@ -97,6 +111,7 @@ fn save_scene_system(world: &mut World) {
Transform::IDENTITY, Transform::IDENTITY,
)); ));
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 }); scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
scene_world.insert_resource(ResourceA { score: 1 });
// The TypeRegistry resource contains information about all registered types (including // The TypeRegistry resource contains information about all registered types (including
// components). This is used to construct scenes. // components). This is used to construct scenes.