mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
(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:
parent
6a85eb3d7e
commit
7b38de0a64
7 changed files with 297 additions and 64 deletions
|
@ -1,4 +1,9 @@
|
|||
(
|
||||
resources: {
|
||||
"scene::ResourceA": (
|
||||
score: 2,
|
||||
),
|
||||
},
|
||||
entities: {
|
||||
0: (
|
||||
components: {
|
||||
|
|
|
@ -10,10 +10,13 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
|
|||
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::serde::SceneSerializer;
|
||||
use bevy_ecs::reflect::ReflectResource;
|
||||
#[cfg(feature = "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:
|
||||
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
|
||||
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
|
||||
|
@ -23,6 +26,7 @@ use serde::Serialize;
|
|||
#[derive(Default, TypeUuid)]
|
||||
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
|
||||
pub struct DynamicScene {
|
||||
pub resources: Vec<Box<dyn Reflect>>,
|
||||
pub entities: Vec<DynamicEntity>,
|
||||
}
|
||||
|
||||
|
@ -47,15 +51,16 @@ impl DynamicScene {
|
|||
DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone());
|
||||
|
||||
builder.extract_entities(world.iter_entities().map(|entity| entity.id()));
|
||||
builder.extract_resources();
|
||||
|
||||
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
|
||||
/// 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(
|
||||
&self,
|
||||
world: &mut World,
|
||||
|
@ -64,6 +69,23 @@ impl DynamicScene {
|
|||
) -> Result<(), SceneSpawnError> {
|
||||
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 {
|
||||
// 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
|
||||
|
@ -105,7 +127,7 @@ impl DynamicScene {
|
|||
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
|
||||
/// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
use crate::{DynamicEntity, DynamicScene};
|
||||
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 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
|
||||
///
|
||||
|
@ -31,6 +37,7 @@ use std::collections::BTreeMap;
|
|||
/// let dynamic_scene = builder.build();
|
||||
/// ```
|
||||
pub struct DynamicSceneBuilder<'w> {
|
||||
extracted_resources: BTreeMap<ComponentId, Box<dyn Reflect>>,
|
||||
extracted_scene: BTreeMap<u32, DynamicEntity>,
|
||||
type_registry: AppTypeRegistry,
|
||||
original_world: &'w World,
|
||||
|
@ -41,6 +48,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||
/// All components registered in that world's [`AppTypeRegistry`] resource will be extracted.
|
||||
pub fn from_world(world: &'w World) -> Self {
|
||||
Self {
|
||||
extracted_resources: default(),
|
||||
extracted_scene: default(),
|
||||
type_registry: world.resource::<AppTypeRegistry>().clone(),
|
||||
original_world: world,
|
||||
|
@ -51,6 +59,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||
/// Only components registered in the given [`AppTypeRegistry`] will be extracted.
|
||||
pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self {
|
||||
Self {
|
||||
extracted_resources: default(),
|
||||
extracted_scene: default(),
|
||||
type_registry,
|
||||
original_world: world,
|
||||
|
@ -63,6 +72,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||
/// [`Self::remove_empty_entities`] before building the scene.
|
||||
pub fn build(self) -> DynamicScene {
|
||||
DynamicScene {
|
||||
resources: self.extracted_resources.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);
|
||||
for component_id in entity.archetype().components() {
|
||||
let reflect_component = self
|
||||
.original_world
|
||||
.components()
|
||||
.get_info(component_id)
|
||||
.and_then(|info| type_registry.get(info.type_id().unwrap()))
|
||||
.and_then(|registration| registration.data::<ReflectComponent>())
|
||||
.and_then(|reflect_component| reflect_component.reflect(entity));
|
||||
|
||||
if let Some(reflect_component) = reflect_component {
|
||||
entry.components.push(reflect_component.clone_value());
|
||||
}
|
||||
let mut extract_and_push = || {
|
||||
let type_id = self
|
||||
.original_world
|
||||
.components()
|
||||
.get_info(component_id)?
|
||||
.type_id()?;
|
||||
let component = type_registry
|
||||
.get(type_id)?
|
||||
.data::<ReflectComponent>()?
|
||||
.reflect(entity)?;
|
||||
entry.components.push(component.clone_value());
|
||||
Some(())
|
||||
};
|
||||
extract_and_push();
|
||||
}
|
||||
self.extracted_scene.insert(index, entry);
|
||||
}
|
||||
|
@ -144,13 +157,59 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||
drop(type_registry);
|
||||
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)]
|
||||
mod tests {
|
||||
use bevy_app::AppTypeRegistry;
|
||||
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;
|
||||
|
@ -160,10 +219,15 @@ mod tests {
|
|||
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct ComponentA;
|
||||
|
||||
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
||||
#[reflect(Component)]
|
||||
struct ComponentB;
|
||||
|
||||
#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]
|
||||
#[reflect(Resource)]
|
||||
struct ResourceA;
|
||||
|
||||
#[test]
|
||||
fn extract_one_entity() {
|
||||
let mut world = World::default();
|
||||
|
@ -303,4 +367,41 @@ mod tests {
|
|||
assert_eq!(scene.entities.len(), 1);
|
||||
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>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use bevy_app::AppTypeRegistry;
|
||||
use bevy_ecs::{
|
||||
entity::EntityMap,
|
||||
reflect::{ReflectComponent, ReflectMapEntities},
|
||||
reflect::{ReflectComponent, ReflectMapEntities, ReflectResource},
|
||||
world::World,
|
||||
};
|
||||
use bevy_reflect::TypeUuid;
|
||||
|
@ -61,6 +61,33 @@ impl Scene {
|
|||
};
|
||||
|
||||
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 scene_entity in archetype.entities() {
|
||||
let entity = *instance_info
|
||||
|
|
|
@ -45,6 +45,8 @@ pub struct SceneSpawner {
|
|||
pub enum SceneSpawnError {
|
||||
#[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")]
|
||||
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>()`")]
|
||||
UnregisteredType { type_name: String },
|
||||
#[error("scene does not exist")]
|
||||
|
|
|
@ -15,6 +15,7 @@ use serde::{
|
|||
use std::fmt::Formatter;
|
||||
|
||||
pub const SCENE_STRUCT: &str = "Scene";
|
||||
pub const SCENE_RESOURCES: &str = "resources";
|
||||
pub const SCENE_ENTITIES: &str = "entities";
|
||||
|
||||
pub const ENTITY_STRUCT: &str = "Entity";
|
||||
|
@ -36,7 +37,14 @@ impl<'a> Serialize for SceneSerializer<'a> {
|
|||
where
|
||||
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(
|
||||
SCENE_ENTITIES,
|
||||
&EntitiesSerializer {
|
||||
|
@ -85,8 +93,8 @@ impl<'a> Serialize for EntitySerializer<'a> {
|
|||
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
|
||||
state.serialize_field(
|
||||
ENTITY_FIELD_COMPONENTS,
|
||||
&ComponentsSerializer {
|
||||
components: &self.entity.components,
|
||||
&SceneMapSerializer {
|
||||
entries: &self.entity.components,
|
||||
registry: self.registry,
|
||||
},
|
||||
)?;
|
||||
|
@ -94,21 +102,21 @@ impl<'a> Serialize for EntitySerializer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ComponentsSerializer<'a> {
|
||||
pub components: &'a [Box<dyn Reflect>],
|
||||
pub struct SceneMapSerializer<'a> {
|
||||
pub entries: &'a [Box<dyn Reflect>],
|
||||
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>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_map(Some(self.components.len()))?;
|
||||
for component in self.components {
|
||||
let mut state = serializer.serialize_map(Some(self.entries.len()))?;
|
||||
for reflect in self.entries {
|
||||
state.serialize_entry(
|
||||
component.type_name(),
|
||||
&TypedReflectSerializer::new(&**component, &self.registry.read()),
|
||||
reflect.type_name(),
|
||||
&TypedReflectSerializer::new(&**reflect, &self.registry.read()),
|
||||
)?;
|
||||
}
|
||||
state.end()
|
||||
|
@ -118,6 +126,7 @@ impl<'a> Serialize for ComponentsSerializer<'a> {
|
|||
#[derive(Deserialize)]
|
||||
#[serde(field_identifier, rename_all = "lowercase")]
|
||||
enum SceneField {
|
||||
Resources,
|
||||
Entities,
|
||||
}
|
||||
|
||||
|
@ -140,7 +149,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> {
|
|||
{
|
||||
deserializer.deserialize_struct(
|
||||
SCENE_STRUCT,
|
||||
&[SCENE_ENTITIES],
|
||||
&[SCENE_RESOURCES, SCENE_ENTITIES],
|
||||
SceneVisitor {
|
||||
type_registry: self.type_registry,
|
||||
},
|
||||
|
@ -163,9 +172,18 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
|
|||
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));
|
||||
|
@ -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))?;
|
||||
|
||||
Ok(DynamicScene { entities })
|
||||
Ok(DynamicScene {
|
||||
resources,
|
||||
entities,
|
||||
})
|
||||
}
|
||||
|
||||
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 { entities })
|
||||
Ok(DynamicScene {
|
||||
resources,
|
||||
entities,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,7 +312,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
|
|||
A: SeqAccess<'de>,
|
||||
{
|
||||
let components = seq
|
||||
.next_element_seed(ComponentDeserializer {
|
||||
.next_element_seed(SceneMapDeserializer {
|
||||
registry: self.registry,
|
||||
})?
|
||||
.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));
|
||||
}
|
||||
|
||||
components = Some(map.next_value_seed(ComponentDeserializer {
|
||||
components = Some(map.next_value_seed(SceneMapDeserializer {
|
||||
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,
|
||||
}
|
||||
|
||||
impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> {
|
||||
impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> {
|
||||
type Value = Vec<Box<dyn Reflect>>;
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(ComponentVisitor {
|
||||
deserializer.deserialize_map(SceneMapVisitor {
|
||||
registry: self.registry,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ComponentVisitor<'a> {
|
||||
struct SceneMapVisitor<'a> {
|
||||
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>>;
|
||||
|
||||
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>
|
||||
|
@ -354,23 +385,23 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
|||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut added = HashSet::new();
|
||||
let mut components = Vec::new();
|
||||
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 component: `{}`",
|
||||
"duplicate reflect type: `{}`",
|
||||
registration.type_name()
|
||||
)));
|
||||
}
|
||||
|
||||
components.push(
|
||||
entries.push(
|
||||
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>
|
||||
|
@ -394,7 +425,7 @@ mod tests {
|
|||
use crate::{DynamicScene, DynamicSceneBuilder};
|
||||
use bevy_app::AppTypeRegistry;
|
||||
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 bincode::Options;
|
||||
use serde::de::DeserializeSeed;
|
||||
|
@ -429,6 +460,12 @@ mod tests {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Resource, Reflect, Default)]
|
||||
#[reflect(Resource)]
|
||||
struct MyResource {
|
||||
foo: i32,
|
||||
}
|
||||
|
||||
fn create_world() -> World {
|
||||
let mut world = World::new();
|
||||
let registry = AppTypeRegistry::default();
|
||||
|
@ -443,6 +480,7 @@ mod tests {
|
|||
registry.register_type_data::<String, ReflectSerialize>();
|
||||
registry.register::<[usize; 3]>();
|
||||
registry.register::<(f32, f32)>();
|
||||
registry.register::<MyResource>();
|
||||
}
|
||||
world.insert_resource(registry);
|
||||
world
|
||||
|
@ -456,11 +494,19 @@ mod tests {
|
|||
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 mut builder = DynamicSceneBuilder::from_world(&world);
|
||||
builder.extract_entities([a, b, c].into_iter());
|
||||
builder.extract_resources();
|
||||
let scene = builder.build();
|
||||
|
||||
let expected = r#"(
|
||||
resources: {
|
||||
"bevy_scene::serde::tests::MyResource": (
|
||||
foo: 123,
|
||||
),
|
||||
},
|
||||
entities: {
|
||||
0: (
|
||||
components: {
|
||||
|
@ -493,6 +539,11 @@ mod tests {
|
|||
let world = create_world();
|
||||
|
||||
let input = r#"(
|
||||
resources: {
|
||||
"bevy_scene::serde::tests::MyResource": (
|
||||
foo: 123,
|
||||
),
|
||||
},
|
||||
entities: {
|
||||
0: (
|
||||
components: {
|
||||
|
@ -520,6 +571,11 @@ mod tests {
|
|||
};
|
||||
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(),
|
||||
|
@ -530,6 +586,11 @@ mod tests {
|
|||
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());
|
||||
|
@ -554,10 +615,10 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
vec![
|
||||
1, 0, 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
|
||||
0, 1, 0, 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
|
||||
);
|
||||
|
@ -594,11 +655,11 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
vec![
|
||||
145, 129, 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
|
||||
146, 128, 129, 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
|
||||
);
|
||||
|
@ -635,12 +696,12 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
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, 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
|
||||
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,
|
||||
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
|
||||
);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
//! This example illustrates loading scenes from files.
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration};
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
|
@ -14,6 +12,7 @@ fn main() {
|
|||
}))
|
||||
.register_type::<ComponentA>()
|
||||
.register_type::<ComponentB>()
|
||||
.register_type::<ResourceA>()
|
||||
.add_systems(
|
||||
Startup,
|
||||
(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
|
||||
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
|
||||
// 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 {
|
||||
info!(" Entity({})", entity.index());
|
||||
info!(
|
||||
|
@ -83,6 +92,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
|
|||
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) {
|
||||
|
@ -97,6 +111,7 @@ fn save_scene_system(world: &mut World) {
|
|||
Transform::IDENTITY,
|
||||
));
|
||||
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
|
||||
// components). This is used to construct scenes.
|
||||
|
|
Loading…
Reference in a new issue