From df7ec3d0eb7d0dce9fbfde14a9db7310ae4047f6 Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Sun, 4 Dec 2022 12:36:14 +0100 Subject: [PATCH 1/9] feat: (de) serialize resources in scenes --- crates/bevy_scene/src/dynamic_scene.rs | 19 ++++ .../bevy_scene/src/dynamic_scene_builder.rs | 26 ++++++ crates/bevy_scene/src/scene.rs | 28 ++++++ crates/bevy_scene/src/scene_spawner.rs | 2 + crates/bevy_scene/src/serde.rs | 91 ++++++++++++++++--- 5 files changed, 151 insertions(+), 15 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 92f8cae312f4e..e5cd82087b211 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -10,6 +10,7 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; +use bevy_ecs::reflect::ReflectResource; #[cfg(feature = "serialize")] use serde::Serialize; @@ -23,6 +24,7 @@ use serde::Serialize; #[derive(Default, TypeUuid)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { + pub resources: Vec>, pub entities: Vec, } @@ -64,6 +66,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::().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 diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 841af6756f3ba..b42cb1c0750c2 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,6 +1,8 @@ use crate::{DynamicEntity, DynamicScene}; use bevy_app::AppTypeRegistry; +use bevy_ecs::reflect::ReflectResource; use bevy_ecs::{prelude::Entity, reflect::ReflectComponent, world::World}; +use bevy_reflect::Reflect; use bevy_utils::default; use std::collections::BTreeMap; @@ -31,6 +33,7 @@ use std::collections::BTreeMap; /// let dynamic_scene = builder.build(); /// ``` pub struct DynamicSceneBuilder<'w> { + resources: Vec>, entities: BTreeMap, type_registry: AppTypeRegistry, world: &'w World, @@ -41,6 +44,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 { + resources: default(), entities: default(), type_registry: world.resource::().clone(), world, @@ -51,6 +55,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 { + resources: default(), entities: default(), type_registry, world, @@ -60,6 +65,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// Consume the builder, producing a [`DynamicScene`]. pub fn build(self) -> DynamicScene { DynamicScene { + resources: self.resources, entities: self.entities.into_values().collect(), } } @@ -131,6 +137,26 @@ impl<'w> DynamicSceneBuilder<'w> { drop(type_registry); self } + + pub fn extract_resources(&mut self, world: &'w World) -> &mut Self { + let type_registry = self.type_registry.read(); + for (component_id, _) in world.storages().resources.iter() { + let reflect_resource = world + .components() + .get_info(component_id) + .and_then(|info| info.type_id()) + .and_then(|type_id| type_registry.get(type_id)) + .and_then(|registration| registration.data::()); + if let Some(reflect_resource) = reflect_resource { + if let Some(resource) = reflect_resource.reflect(world) { + self.resources.push(resource.clone_value()); + } + } + } + + drop(type_registry); + self + } } #[cfg(test)] diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 4df757aa5838f..583603652e381 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,4 +1,5 @@ use bevy_app::AppTypeRegistry; +use bevy_ecs::reflect::ReflectResource; use bevy_ecs::{ entity::EntityMap, reflect::{ReflectComponent, ReflectMapEntities}, @@ -61,6 +62,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"); + + if let Some(registration) = type_registry.get(type_id) { + let reflect_resource = registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredResource { + type_name: component_info.name().to_string(), + } + })?; + reflect_resource.copy(&self.world, world); + } else { + return Err(SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }); + } + } + for archetype in self.world.archetypes().iter() { for scene_entity in archetype.entities() { let entity = *instance_info diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index f8f581a609d65..c825b06e77512 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -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::()`")] UnregisteredType { type_name: String }, #[error("scene does not exist")] diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 0a225690168d2..dd3c00ac260ac 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -12,6 +12,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"; @@ -33,7 +34,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, + &ComponentsSerializer { + components: &self.scene.resources, + registry: self.registry, + }, + )?; state.serialize_field( SCENE_ENTITIES, &EntitiesSerializer { @@ -115,6 +123,7 @@ impl<'a> Serialize for ComponentsSerializer<'a> { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "lowercase")] enum SceneField { + Resources, Entities, } @@ -137,7 +146,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, }, @@ -160,9 +169,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(ComponentDeserializer { + registry: self.type_registry, + })?); + } SceneField::Entities => { if entities.is_some() { return Err(Error::duplicate_field(SCENE_ENTITIES)); @@ -174,22 +192,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(self, mut seq: A) -> Result where A: SeqAccess<'de>, { + let resources = seq + .next_element_seed(ComponentDeserializer { + 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, + }) } } @@ -390,7 +421,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; @@ -423,6 +454,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(); @@ -437,6 +474,7 @@ mod tests { registry.register_type_data::(); registry.register::<[usize; 3]>(); registry.register::<(f32, f32)>(); + registry.register::(); } world.insert_resource(registry); world @@ -450,11 +488,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(&world); let scene = builder.build(); let expected = r#"( + resources: { + "bevy_scene::serde::tests::MyResource": ( + foo: 123, + ), + }, entities: { 0: ( components: { @@ -487,6 +533,11 @@ mod tests { let world = create_world(); let input = r#"( + resources: { + "bevy_scene::serde::tests::MyResource": ( + foo: 123, + ), + }, entities: { 0: ( components: { @@ -514,6 +565,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(), @@ -524,6 +580,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::(); + 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()); @@ -548,10 +609,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 ); @@ -586,12 +647,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 ); From 6695daef6026982a22d77f35265546f859281018 Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Tue, 6 Dec 2022 23:12:54 +0100 Subject: [PATCH 2/9] fix: add extract_resources call in DynamicScene::from_world refactor: apply review suggestions feat: add example in scene.rs for resource de/serialization --- assets/scenes/load_scene_example.scn.ron | 5 ++ crates/bevy_scene/src/dynamic_scene.rs | 1 + .../bevy_scene/src/dynamic_scene_builder.rs | 38 +++++++++++--- crates/bevy_scene/src/scene.rs | 25 +++++----- crates/bevy_scene/src/serde.rs | 50 +++++++++---------- examples/scene/scene.rs | 19 ++++++- 6 files changed, 91 insertions(+), 47 deletions(-) diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index de4d3ce9280a0..4c03d1c53cc1a 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -1,4 +1,9 @@ ( + resources: { + "scene::ResourceA": ( + score: 2, + ), + }, entities: { 0: ( components: { diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index e5cd82087b211..054b004785eff 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -49,6 +49,7 @@ impl DynamicScene { DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone()); builder.extract_entities(world.iter_entities()); + builder.extract_resources(world); builder.build() } diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index b42cb1c0750c2..618c7e5fc57e0 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,7 +1,10 @@ use crate::{DynamicEntity, DynamicScene}; use bevy_app::AppTypeRegistry; -use bevy_ecs::reflect::ReflectResource; -use bevy_ecs::{prelude::Entity, reflect::ReflectComponent, world::World}; +use bevy_ecs::{ + prelude::Entity, + reflect::{ReflectComponent, ReflectResource}, + world::World, +}; use bevy_reflect::Reflect; use bevy_utils::default; use std::collections::BTreeMap; @@ -138,19 +141,38 @@ impl<'w> DynamicSceneBuilder<'w> { self } + /// Extract resources from the builder's [`World`]. + /// + /// Only resources registered in the builder's [`AppTypeRegistry`] will be extracted. + /// ``` + /// # 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::(); + /// let _resource = world.insert_resource(MyResource); + /// + /// let mut builder = DynamicSceneBuilder::from_world(&world); + /// builder.extract_resources(&world); + /// let scene = builder.build(); + /// pub fn extract_resources(&mut self, world: &'w World) -> &mut Self { let type_registry = self.type_registry.read(); for (component_id, _) in world.storages().resources.iter() { - let reflect_resource = world + if let Some(resource) = world .components() .get_info(component_id) .and_then(|info| info.type_id()) .and_then(|type_id| type_registry.get(type_id)) - .and_then(|registration| registration.data::()); - if let Some(reflect_resource) = reflect_resource { - if let Some(resource) = reflect_resource.reflect(world) { - self.resources.push(resource.clone_value()); - } + .and_then(|registration| registration.data::()) + .and_then(|reflect_resource| reflect_resource.reflect(world)) + { + self.resources.push(resource.clone_value()); } } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 583603652e381..460f7cf8b2f3a 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,8 +1,7 @@ use bevy_app::AppTypeRegistry; -use bevy_ecs::reflect::ReflectResource; use bevy_ecs::{ entity::EntityMap, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; use bevy_reflect::TypeUuid; @@ -73,20 +72,20 @@ impl Scene { let type_id = component_info .type_id() - .expect("Reflected resources must have a type_id"); + .expect("reflected resources must have a type_id"); - if let Some(registration) = type_registry.get(type_id) { - let reflect_resource = registration.data::().ok_or_else(|| { - SceneSpawnError::UnregisteredResource { + let registration = + type_registry + .get(type_id) + .ok_or_else(|| SceneSpawnError::UnregisteredType { type_name: component_info.name().to_string(), - } - })?; - reflect_resource.copy(&self.world, world); - } else { - return Err(SceneSpawnError::UnregisteredType { + })?; + let reflect_resource = registration.data::().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() { diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index dd3c00ac260ac..65de1efedab36 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -37,8 +37,8 @@ impl<'a> Serialize for SceneSerializer<'a> { let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?; state.serialize_field( SCENE_RESOURCES, - &ComponentsSerializer { - components: &self.scene.resources, + &ReflectArraySerializer { + reflect_array: &self.scene.resources, registry: self.registry, }, )?; @@ -90,8 +90,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, + &ReflectArraySerializer { + reflect_array: &self.entity.components, registry: self.registry, }, )?; @@ -99,21 +99,21 @@ impl<'a> Serialize for EntitySerializer<'a> { } } -pub struct ComponentsSerializer<'a> { - pub components: &'a [Box], +pub struct ReflectArraySerializer<'a> { + pub reflect_array: &'a [Box], pub registry: &'a TypeRegistryArc, } -impl<'a> Serialize for ComponentsSerializer<'a> { +impl<'a> Serialize for ReflectArraySerializer<'a> { fn serialize(&self, serializer: S) -> Result 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.reflect_array.len()))?; + for reflect in self.reflect_array { state.serialize_entry( - component.type_name(), - &TypedReflectSerializer::new(&**component, &self.registry.read()), + reflect.type_name(), + &TypedReflectSerializer::new(&**reflect, &self.registry.read()), )?; } state.end() @@ -177,7 +177,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { if resources.is_some() { return Err(Error::duplicate_field(SCENE_RESOURCES)); } - resources = Some(map.next_value_seed(ComponentDeserializer { + resources = Some(map.next_value_seed(ReflectDeserializer { registry: self.type_registry, })?); } @@ -206,7 +206,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { A: SeqAccess<'de>, { let resources = seq - .next_element_seed(ComponentDeserializer { + .next_element_seed(ReflectDeserializer { registry: self.type_registry, })? .ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?; @@ -309,7 +309,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { A: SeqAccess<'de>, { let components = seq - .next_element_seed(ComponentDeserializer { + .next_element_seed(ReflectDeserializer { registry: self.registry, })? .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; @@ -332,7 +332,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(ReflectDeserializer { registry: self.registry, })?); } @@ -349,32 +349,32 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { } } -pub struct ComponentDeserializer<'a> { +pub struct ReflectDeserializer<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> { +impl<'a, 'de> DeserializeSeed<'de> for ReflectDeserializer<'a> { type Value = Vec>; fn deserialize(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_map(ComponentVisitor { + deserializer.deserialize_map(ReflectVisitor { registry: self.registry, }) } } -struct ComponentVisitor<'a> { +struct ReflectVisitor<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { +impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { type Value = Vec>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("map of components") + formatter.write_str("map of impl reflect") } fn visit_map(self, mut map: A) -> std::result::Result @@ -382,22 +382,22 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { A: MapAccess<'de>, { let mut added = HashSet::new(); - let mut components = Vec::new(); + let mut reflect_array = Vec::new(); while let Some(key) = map.next_key::<&str>()? { if !added.insert(key) { - return Err(Error::custom(format!("duplicate component: `{key}`"))); + return Err(Error::custom(format!("duplicate impl reflect: `{key}`"))); } let registration = self .registry .get_with_name(key) .ok_or_else(|| Error::custom(format!("no registration found for `{key}`")))?; - components.push( + reflect_array.push( map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?, ); } - Ok(components) + Ok(reflect_array) } fn visit_seq(self, mut seq: A) -> Result diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index a3210214ad7b9..35fe030f9b840 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -14,6 +14,7 @@ fn main() { })) .register_type::() .register_type::() + .register_type::() .add_startup_system(save_scene_system) .add_startup_system(load_scene_system) .add_startup_system(infotext_system) @@ -56,6 +57,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"; @@ -74,7 +82,10 @@ fn load_scene_system(mut commands: Commands, asset_server: Res) { // 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>) { +fn log_system( + query: Query<(Entity, &ComponentA), Changed>, + res: Option>, +) { for (entity, component_a) in &query { info!(" Entity({})", entity.index()); info!( @@ -82,6 +93,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed>) { 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) { @@ -96,6 +112,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. From c6a88778da9c5c549e3b084c72173ba82099cc1c Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Tue, 6 Dec 2022 23:35:26 +0100 Subject: [PATCH 3/9] fix: adds references to the resources field to DynamicScene / DynamicSceneBuilder doc blocks --- crates/bevy_scene/src/dynamic_scene.rs | 8 ++++---- crates/bevy_scene/src/dynamic_scene_builder.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 8868fc6e7c9b3..5811ab7135b8a 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -14,7 +14,7 @@ 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 of the latter with 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 @@ -54,11 +54,11 @@ impl DynamicScene { 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, @@ -124,7 +124,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 diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 618c7e5fc57e0..509505204bfec 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -9,7 +9,7 @@ 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 /// From 332216bfebf69c074273e74476558070a9e0d511 Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Thu, 8 Dec 2022 12:15:47 +0100 Subject: [PATCH 4/9] fix: oxford comma in doc blocks refactor: ReflectArraySerializer -> SceneMapSerializer, ReflectDeserializer -> SceneMapDeserializer, ReflectVisitor -> SceneMapVisitor --- crates/bevy_scene/src/dynamic_scene.rs | 4 +-- .../bevy_scene/src/dynamic_scene_builder.rs | 4 +-- crates/bevy_scene/src/serde.rs | 26 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 5811ab7135b8a..7049fbcbb8382 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -54,7 +54,7 @@ impl DynamicScene { builder.build() } - /// Write the resources, 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 @@ -124,7 +124,7 @@ impl DynamicScene { Ok(()) } - /// Write the resources, 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 diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 509505204bfec..83c8e3e1aafa3 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -155,12 +155,12 @@ impl<'w> DynamicSceneBuilder<'w> { /// /// # let mut world = World::default(); /// # world.init_resource::(); - /// let _resource = world.insert_resource(MyResource); + /// world.insert_resource(MyResource); /// /// let mut builder = DynamicSceneBuilder::from_world(&world); /// builder.extract_resources(&world); /// let scene = builder.build(); - /// + /// ``` pub fn extract_resources(&mut self, world: &'w World) -> &mut Self { let type_registry = self.type_registry.read(); for (component_id, _) in world.storages().resources.iter() { diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 65de1efedab36..4a0bb8cdd4728 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -37,7 +37,7 @@ impl<'a> Serialize for SceneSerializer<'a> { let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?; state.serialize_field( SCENE_RESOURCES, - &ReflectArraySerializer { + &SceneMapSerializer { reflect_array: &self.scene.resources, registry: self.registry, }, @@ -90,7 +90,7 @@ impl<'a> Serialize for EntitySerializer<'a> { let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?; state.serialize_field( ENTITY_FIELD_COMPONENTS, - &ReflectArraySerializer { + &SceneMapSerializer { reflect_array: &self.entity.components, registry: self.registry, }, @@ -99,12 +99,12 @@ impl<'a> Serialize for EntitySerializer<'a> { } } -pub struct ReflectArraySerializer<'a> { +pub struct SceneMapSerializer<'a> { pub reflect_array: &'a [Box], pub registry: &'a TypeRegistryArc, } -impl<'a> Serialize for ReflectArraySerializer<'a> { +impl<'a> Serialize for SceneMapSerializer<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -177,7 +177,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { if resources.is_some() { return Err(Error::duplicate_field(SCENE_RESOURCES)); } - resources = Some(map.next_value_seed(ReflectDeserializer { + resources = Some(map.next_value_seed(SceneMapDeserializer { registry: self.type_registry, })?); } @@ -206,7 +206,7 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { A: SeqAccess<'de>, { let resources = seq - .next_element_seed(ReflectDeserializer { + .next_element_seed(SceneMapDeserializer { registry: self.type_registry, })? .ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?; @@ -309,7 +309,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { A: SeqAccess<'de>, { let components = seq - .next_element_seed(ReflectDeserializer { + .next_element_seed(SceneMapDeserializer { registry: self.registry, })? .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; @@ -332,7 +332,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS)); } - components = Some(map.next_value_seed(ReflectDeserializer { + components = Some(map.next_value_seed(SceneMapDeserializer { registry: self.registry, })?); } @@ -349,28 +349,28 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { } } -pub struct ReflectDeserializer<'a> { +pub struct SceneMapDeserializer<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> DeserializeSeed<'de> for ReflectDeserializer<'a> { +impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> { type Value = Vec>; fn deserialize(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_map(ReflectVisitor { + deserializer.deserialize_map(SceneMapVisitor { registry: self.registry, }) } } -struct ReflectVisitor<'a> { +struct SceneMapVisitor<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { +impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { type Value = Vec>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { From c13c456d50323aa2e6959bf825b93a0c74478402 Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Sun, 11 Dec 2022 15:22:54 +0100 Subject: [PATCH 5/9] refactor: cleanup of confusing documentation and reflect_array -> entries renaming --- crates/bevy_scene/src/dynamic_scene.rs | 4 +++- crates/bevy_scene/src/serde.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 7049fbcbb8382..254ad69a45a39 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -14,7 +14,9 @@ use bevy_ecs::reflect::ReflectResource; #[cfg(feature = "serialize")] use serde::Serialize; -/// A collection of serializable resources and dynamic entities, each of the latter 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 diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 4a0bb8cdd4728..b4d55e51bd3cd 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -38,7 +38,7 @@ impl<'a> Serialize for SceneSerializer<'a> { state.serialize_field( SCENE_RESOURCES, &SceneMapSerializer { - reflect_array: &self.scene.resources, + entries: &self.scene.resources, registry: self.registry, }, )?; @@ -91,7 +91,7 @@ impl<'a> Serialize for EntitySerializer<'a> { state.serialize_field( ENTITY_FIELD_COMPONENTS, &SceneMapSerializer { - reflect_array: &self.entity.components, + entries: &self.entity.components, registry: self.registry, }, )?; @@ -100,7 +100,7 @@ impl<'a> Serialize for EntitySerializer<'a> { } pub struct SceneMapSerializer<'a> { - pub reflect_array: &'a [Box], + pub entries: &'a [Box], pub registry: &'a TypeRegistryArc, } @@ -109,8 +109,8 @@ impl<'a> Serialize for SceneMapSerializer<'a> { where S: serde::Serializer, { - let mut state = serializer.serialize_map(Some(self.reflect_array.len()))?; - for reflect in self.reflect_array { + let mut state = serializer.serialize_map(Some(self.entries.len()))?; + for reflect in self.entries { state.serialize_entry( reflect.type_name(), &TypedReflectSerializer::new(&**reflect, &self.registry.read()), @@ -382,7 +382,7 @@ impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { A: MapAccess<'de>, { let mut added = HashSet::new(); - let mut reflect_array = Vec::new(); + let mut entries = Vec::new(); while let Some(key) = map.next_key::<&str>()? { if !added.insert(key) { return Err(Error::custom(format!("duplicate impl reflect: `{key}`"))); @@ -392,12 +392,12 @@ impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { .registry .get_with_name(key) .ok_or_else(|| Error::custom(format!("no registration found for `{key}`")))?; - reflect_array.push( + entries.push( map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?, ); } - Ok(reflect_array) + Ok(entries) } fn visit_seq(self, mut seq: A) -> Result From 7f74821339fef436a0a763e02c65f890f0f5a0ce Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Thu, 22 Dec 2022 11:29:08 +0100 Subject: [PATCH 6/9] refactor: removed world parameter from DynamicSceneBuilder extract_resource feat: specific test for DynamicSceneBuilder extract_resource --- crates/bevy_scene/src/dynamic_scene.rs | 2 +- .../bevy_scene/src/dynamic_scene_builder.rs | 35 +++++++++++++++---- crates/bevy_scene/src/serde.rs | 2 +- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 254ad69a45a39..8a9822e0b17db 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -51,7 +51,7 @@ 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(world); + builder.extract_resources(); builder.build() } diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 8a3b66bed432d..ec87a9eab58b9 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -172,19 +172,20 @@ impl<'w> DynamicSceneBuilder<'w> { /// world.insert_resource(MyResource); /// /// let mut builder = DynamicSceneBuilder::from_world(&world); - /// builder.extract_resources(&world); + /// builder.extract_resources(); /// let scene = builder.build(); /// ``` - pub fn extract_resources(&mut self, world: &'w World) -> &mut Self { + pub fn extract_resources(&mut self) -> &mut Self { let type_registry = self.type_registry.read(); - for (component_id, _) in world.storages().resources.iter() { - if let Some(resource) = world + for (component_id, _) in self.original_world.storages().resources.iter() { + if let Some(resource) = self + .original_world .components() .get_info(component_id) .and_then(|info| info.type_id()) .and_then(|type_id| type_registry.get(type_id)) .and_then(|registration| registration.data::()) - .and_then(|reflect_resource| reflect_resource.reflect(world)) + .and_then(|reflect_resource| reflect_resource.reflect(self.original_world)) { self.resources.push(resource.clone_value()); } @@ -199,7 +200,8 @@ impl<'w> DynamicSceneBuilder<'w> { 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; @@ -212,6 +214,9 @@ mod tests { #[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() { @@ -352,4 +357,22 @@ 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::(); + 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::()); + } } diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index b4d55e51bd3cd..ba234dfb6992e 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -492,7 +492,7 @@ mod tests { let mut builder = DynamicSceneBuilder::from_world(&world); builder.extract_entities([a, b, c].into_iter()); - builder.extract_resources(&world); + builder.extract_resources(); let scene = builder.build(); let expected = r#"( From ff7c405a8d06829da1c0ccdede7db6bce5b4e2cd Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Thu, 5 Jan 2023 23:39:31 +0100 Subject: [PATCH 7/9] refactor: convert and_then calls to a closure, impl reflect -> reflect types --- .../bevy_scene/src/dynamic_scene_builder.rs | 52 +++++++++++-------- crates/bevy_scene/src/serde.rs | 16 +++--- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index ec87a9eab58b9..1c55fd9ae441b 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -134,19 +134,20 @@ impl<'w> DynamicSceneBuilder<'w> { }; for component_id in self.original_world.entity(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::()); - - if let Some(reflect_component) = reflect_component { - if let Some(component) = reflect_component.reflect(self.original_world, entity) - { - entry.components.push(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::()? + .reflect(self.original_world, entity)?; + entry.components.push(component.clone_value()); + Some(()) + }; + extract_and_push(); } self.extracted_scene.insert(index, entry); } @@ -178,17 +179,20 @@ impl<'w> DynamicSceneBuilder<'w> { 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() { - if let Some(resource) = self - .original_world - .components() - .get_info(component_id) - .and_then(|info| info.type_id()) - .and_then(|type_id| type_registry.get(type_id)) - .and_then(|registration| registration.data::()) - .and_then(|reflect_resource| reflect_resource.reflect(self.original_world)) - { + 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::()? + .reflect(self.original_world)?; self.resources.push(resource.clone_value()); - } + Some(()) + }; + extract_and_push(); } drop(type_registry); @@ -211,9 +215,11 @@ 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; diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 02170de88de76..c70a40ab5cf79 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -375,7 +375,7 @@ impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { type Value = Vec>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("map of impl reflect") + formatter.write_str("map of reflect types") } fn visit_map(self, mut map: A) -> std::result::Result @@ -383,10 +383,10 @@ impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { A: MapAccess<'de>, { let mut added = HashSet::new(); - let mut components = Vec::new(); + let mut entries = Vec::new(); while let Some(BorrowableCowStr(key)) = map.next_key()? { if !added.insert(key.clone()) { - return Err(Error::custom(format!("duplicate component: `{key}`"))); + return Err(Error::custom(format!("duplicate reflect type: `{key}`"))); } let registration = self @@ -659,11 +659,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 ); From 197945987f674b93332d8240b8b2dcbf1dd4c23e Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Wed, 8 Feb 2023 18:44:55 +0100 Subject: [PATCH 8/9] tweak: duplicate resource extraction is now avoided and will have no effect if attempted. --- .../bevy_scene/src/dynamic_scene_builder.rs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 4632415e4a904..0aa4b24365ba2 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -8,6 +8,7 @@ use bevy_ecs::{ use bevy_reflect::Reflect; use bevy_utils::default; use std::collections::BTreeMap; +use bevy_ecs::component::ComponentId; /// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources. /// @@ -36,7 +37,7 @@ use std::collections::BTreeMap; /// let dynamic_scene = builder.build(); /// ``` pub struct DynamicSceneBuilder<'w> { - resources: Vec>, + extracted_resources: BTreeMap>, extracted_scene: BTreeMap, type_registry: AppTypeRegistry, original_world: &'w World, @@ -47,7 +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 { - resources: default(), + extracted_resources: default(), extracted_scene: default(), type_registry: world.resource::().clone(), original_world: world, @@ -58,7 +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 { - resources: default(), + extracted_resources: default(), extracted_scene: default(), type_registry, original_world: world, @@ -71,7 +72,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// [`Self::remove_empty_entities`] before building the scene. pub fn build(self) -> DynamicScene { DynamicScene { - resources: self.resources, + resources: self.extracted_resources.into_values().collect(), entities: self.extracted_scene.into_values().collect(), } } @@ -144,7 +145,7 @@ impl<'w> DynamicSceneBuilder<'w> { let component = type_registry .get(type_id)? .data::()? - .reflect(self.original_world, entity)?; + .reflect(entity)?; entry.components.push(component.clone_value()); Some(()) }; @@ -160,6 +161,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// 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; @@ -190,7 +192,7 @@ impl<'w> DynamicSceneBuilder<'w> { .get(type_id)? .data::()? .reflect(self.original_world)?; - self.resources.push(resource.clone_value()); + self.extracted_resources.insert(component_id, resource.clone_value()); Some(()) }; extract_and_push(); @@ -382,4 +384,23 @@ mod tests { assert_eq!(scene.resources.len(), 1); assert!(scene.resources[0].represents::()); } + + #[test] + fn extract_one_resource_twice() { + let mut world = World::default(); + + let atr = AppTypeRegistry::default(); + atr.write().register::(); + 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::()); + } } From 9c6f37d8f887e5dd0d1cb215672ea7f9e9675455 Mon Sep 17 00:00:00 2001 From: Carbonhell Date: Wed, 8 Feb 2023 18:47:51 +0100 Subject: [PATCH 9/9] fix: missing cargo fmt --- crates/bevy_scene/src/dynamic_scene_builder.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 0aa4b24365ba2..0f25b1e484ba3 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,5 +1,6 @@ use crate::{DynamicEntity, DynamicScene}; use bevy_app::AppTypeRegistry; +use bevy_ecs::component::ComponentId; use bevy_ecs::{ prelude::Entity, reflect::{ReflectComponent, ReflectResource}, @@ -8,7 +9,6 @@ use bevy_ecs::{ use bevy_reflect::Reflect; use bevy_utils::default; use std::collections::BTreeMap; -use bevy_ecs::component::ComponentId; /// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources. /// @@ -192,7 +192,8 @@ impl<'w> DynamicSceneBuilder<'w> { .get(type_id)? .data::()? .reflect(self.original_world)?; - self.extracted_resources.insert(component_id, resource.clone_value()); + self.extracted_resources + .insert(component_id, resource.clone_value()); Some(()) }; extract_and_push();