diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index 1f9de9dae7da8..59a51c1e9fe8f 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -1,64 +1,67 @@ -[ - ( - entity: 0, - components: [ - { - "type": "bevy_transform::components::transform::Transform", - "struct": { - "translation": { - "type": "glam::vec3::Vec3", - "value": (0.0, 0.0, 0.0), - }, - "rotation": { - "type": "glam::quat::Quat", - "value": (0.0, 0.0, 0.0, 1.0), - }, - "scale": { - "type": "glam::vec3::Vec3", - "value": (1.0, 1.0, 1.0), +( + resources: [], + entities: [ + ( + entity: 0, + components: [ + { + "type": "bevy_transform::components::transform::Transform", + "struct": { + "translation": { + "type": "glam::vec3::Vec3", + "value": (0.0, 0.0, 0.0), + }, + "rotation": { + "type": "glam::quat::Quat", + "value": (0.0, 0.0, 0.0, 1.0), + }, + "scale": { + "type": "glam::vec3::Vec3", + "value": (1.0, 1.0, 1.0), + }, }, }, - }, - { - "type": "scene::ComponentB", - "struct": { - "value": { - "type": "alloc::string::String", - "value": "hello", + { + "type": "scene::ComponentB", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "hello", + }, }, }, - }, - { - "type": "scene::ComponentA", - "struct": { - "x": { - "type": "f32", - "value": 1.0, - }, - "y": { - "type": "f32", - "value": 2.0, + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 1.0, + }, + "y": { + "type": "f32", + "value": 2.0, + }, }, }, - }, - ], - ), - ( - entity: 1, - components: [ - { - "type": "scene::ComponentA", - "struct": { - "x": { - "type": "f32", - "value": 3.0, - }, - "y": { - "type": "f32", - "value": 4.0, + ], + ), + ( + entity: 1, + components: [ + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 3.0, + }, + "y": { + "type": "f32", + "value": 4.0, + }, }, }, - }, - ], - ), -] + ], + ), + ], +) \ No newline at end of file diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index e944e2c53cbd5..d7046630bdd0f 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -19,4 +19,4 @@ bevy_asset = { path = "../bevy_asset", version = "0.5.0" } bevy_core = { path = "../bevy_core", version = "0.5.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" } bevy_render = { path = "../bevy_render", version = "0.5.0" } - +bevy_reflect = { path = "../bevy_reflect", version = "0.5.0" } diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index 709c4564879c4..7e9bba6804d44 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -18,6 +18,7 @@ pub use main_pass_driver::*; use bevy_app::{App, Plugin}; use bevy_core::FloatOrd; use bevy_ecs::prelude::*; +use bevy_reflect::Reflect; use bevy_render::{ camera::{ActiveCameras, CameraPlugin}, color::Color, @@ -34,7 +35,8 @@ use bevy_render::{ }; /// Resource that configures the clear color -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct ClearColor(pub Color); impl Default for ClearColor { @@ -86,7 +88,8 @@ pub struct CorePipelinePlugin; impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { - app.init_resource::(); + app.register_type::() + .init_resource::(); let render_app = app.sub_app_mut(RenderApp); render_app diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index d7dd0067fe75e..027a13fae89a1 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -188,7 +188,7 @@ change_detection_impl!(Mut<'a, T>, T,); impl_into_inner!(Mut<'a, T>, T,); impl_debug!(Mut<'a, T>,); -/// Unique mutable borrow of a Reflected component +/// Unique mutable borrow of a reflected component or resource #[cfg(feature = "bevy_reflect")] pub struct ReflectMut<'a> { pub(crate) value: &'a mut dyn Reflect, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 30d70ca17efab..5c3e8cd5b742f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -18,7 +18,7 @@ pub mod world; pub mod prelude { #[doc(hidden)] #[cfg(feature = "bevy_reflect")] - pub use crate::reflect::ReflectComponent; + pub use crate::reflect::{ReflectComponent, ReflectResource}; #[doc(hidden)] pub use crate::{ bundle::Bundle, diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 4881dd83f0edd..22921784f0829 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -4,6 +4,7 @@ pub use crate::change_detection::ReflectMut; use crate::{ component::Component, entity::{Entity, EntityMap, MapEntities, MapEntitiesError}, + system::Resource, world::{FromWorld, World}, }; use bevy_reflect::{ @@ -16,7 +17,7 @@ pub struct ReflectComponent { apply_component: fn(&mut World, Entity, &dyn Reflect), remove_component: fn(&mut World, Entity), reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>, - reflect_component_mut: unsafe fn(&World, Entity) -> Option, + reflect_component_unchecked_mut: unsafe fn(&World, Entity) -> Option, copy_component: fn(&World, &mut World, Entity, Entity), } @@ -47,7 +48,7 @@ impl ReflectComponent { entity: Entity, ) -> Option> { // SAFE: unique world access - unsafe { (self.reflect_component_mut)(world, entity) } + unsafe { (self.reflect_component_unchecked_mut)(world, entity) } } /// # Safety @@ -61,7 +62,7 @@ impl ReflectComponent { world: &'a World, entity: Entity, ) -> Option> { - (self.reflect_component_mut)(world, entity) + (self.reflect_component_unchecked_mut)(world, entity) } pub fn copy_component( @@ -109,7 +110,7 @@ impl FromType for ReflectComponent { .get::() .map(|c| c as &dyn Reflect) }, - reflect_component_mut: |world, entity| unsafe { + reflect_component_unchecked_mut: |world, entity| unsafe { world .get_entity(entity)? .get_unchecked_mut::(world.last_change_tick(), world.read_change_tick()) @@ -122,6 +123,90 @@ impl FromType for ReflectComponent { } } +#[derive(Clone)] +pub struct ReflectResource { + insert_resource: fn(&mut World, &dyn Reflect), + apply_resource: fn(&mut World, &dyn Reflect), + remove_resource: fn(&mut World), + reflect_resource: fn(&World) -> Option<&dyn Reflect>, + reflect_resource_unchecked_mut: unsafe fn(&World) -> Option, + copy_resource: fn(&World, &mut World), +} + +impl ReflectResource { + pub fn insert_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.insert_resource)(world, resource); + } + + pub fn apply_resource(&self, world: &mut World, resource: &dyn Reflect) { + (self.apply_resource)(world, resource); + } + + pub fn remove_resource(&self, world: &mut World) { + (self.remove_resource)(world); + } + + pub fn reflect_resource<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> { + (self.reflect_resource)(world) + } + + pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option> { + // SAFE: unique world access + unsafe { (self.reflect_resource_unchecked_mut)(world) } + } + + /// # Safety + /// This method does not prevent you from having two mutable pointers to the same data, + /// violating Rust's aliasing rules. To avoid this: + /// * Only call this method in an exclusive system to avoid sharing across threads (or use a + /// scheduler that enforces safe memory access). + /// * Don't call this method more than once in the same scope for a given resource. + pub unsafe fn reflect_resource_unckecked_mut<'a>( + &self, + world: &'a World, + ) -> Option> { + (self.reflect_resource_unchecked_mut)(world) + } + + pub fn copy_resource(&self, source_world: &World, destination_world: &mut World) { + (self.copy_resource)(source_world, destination_world); + } +} + +impl FromType for ReflectResource { + fn from_type() -> Self { + ReflectResource { + insert_resource: |world, reflected_resource| { + let mut resource = C::from_world(world); + resource.apply(reflected_resource); + world.insert_resource(resource); + }, + apply_resource: |world, reflected_resource| { + let mut resource = world.get_resource_mut::().unwrap(); + resource.apply(reflected_resource); + }, + remove_resource: |world| { + world.remove_resource::(); + }, + reflect_resource: |world| world.get_resource::().map(|res| res as &dyn Reflect), + reflect_resource_unchecked_mut: |world| unsafe { + world + .get_resource_unchecked_mut::() + .map(|res| ReflectMut { + value: res.value as &mut dyn Reflect, + ticks: res.ticks, + }) + }, + copy_resource: |source_world, destination_world| { + let source_resource = source_world.get_resource::().unwrap(); + let mut destination_resource = C::from_world(destination_world); + destination_resource.apply(source_resource); + destination_world.insert_resource(destination_resource); + }, + } + } +} + impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); impl_from_reflect_value!(Entity); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 739a1d9d30f31..e072800cf358f 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -644,16 +644,27 @@ impl World { Some(unsafe { ptr.cast::().read() }) } + /// Returns `true` if a resource of a given [`ComponentId`] exists. Otherwise returns `false`. + #[inline] + pub fn contains_resource_with_id(&self, component_id: ComponentId) -> bool { + self.get_populated_resource_column(component_id).is_some() + } + + /// Returns `true` if a resource of a given [`TypeId`] exists. Otherwise returns `false`. + #[inline] + pub fn contains_resource_with_type(&self, type_id: TypeId) -> bool { + let component_id = if let Some(component_id) = self.components.get_resource_id(type_id) { + component_id + } else { + return false; + }; + self.contains_resource_with_id(component_id) + } + /// Returns `true` if a resource of type `T` exists. Otherwise returns `false`. #[inline] pub fn contains_resource(&self) -> bool { - let component_id = - if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) { - component_id - } else { - return false; - }; - self.get_populated_resource_column(component_id).is_some() + self.contains_resource_with_type(TypeId::of::()) } /// Gets a reference to the resource of the given type, if it exists. Otherwise returns [None] diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 2e4fddbc821bd..17b98a5759f02 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -69,10 +69,12 @@ impl Plugin for PbrPlugin { app.add_plugin(MeshRenderPlugin) .add_plugin(MaterialPlugin::::default()) + .register_type::() + .register_type::() + .register_type::() .init_resource::() .init_resource::() .init_resource::() - .init_resource::() .init_resource::() .add_system_to_stage( CoreStage::PostUpdate, diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index f77501bb93cfb..fae2624473c24 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use bevy_ecs::prelude::*; use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; +use bevy_reflect::Reflect; use bevy_render::{ camera::{Camera, CameraProjection, OrthographicProjection}, color::Color, @@ -67,7 +68,8 @@ impl PointLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct PointLightShadowMap { pub size: usize, } @@ -144,7 +146,8 @@ impl DirectionalLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Reflect)] +#[reflect(Resource)] pub struct DirectionalLightShadowMap { pub size: usize, } @@ -159,7 +162,8 @@ impl Default for DirectionalLightShadowMap { } /// An ambient light, which lights the entire scene equally. -#[derive(Debug)] +#[derive(Debug, Reflect)] +#[reflect(Resource)] pub struct AmbientLight { pub color: Color, /// A direct scale factor multiplied with `color` before being passed to the shader. diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 02b5d9dbee6eb..25169f1dbbdad 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -29,7 +29,8 @@ impl Plugin for WireframePlugin { Shader::from_wgsl(include_str!("render/wireframe.wgsl")), ); - app.init_resource::(); + app.register_type::() + .init_resource::(); app.sub_app_mut(RenderApp) .add_render_command::() @@ -58,7 +59,8 @@ fn extract_wireframes(mut commands: Commands, query: Query().add_plugin(VisibilityPlugin); + app.register_type::() + .init_resource::() + .add_plugin(VisibilityPlugin); app.sub_app_mut(RenderApp) .init_resource::() @@ -37,7 +40,8 @@ impl Plugin for ViewPlugin { } } -#[derive(Clone)] +#[derive(Clone, Reflect)] +#[reflect(Resource)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// smoother edges. Note that WGPU currently only supports 1 or 4 samples. diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index ab15cbd61dbdd..b3a847a8e87c9 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -2,7 +2,7 @@ use crate::{serde::SceneSerializer, Scene, SceneSpawnError}; use anyhow::Result; use bevy_ecs::{ entity::EntityMap, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; @@ -12,6 +12,7 @@ use serde::Serialize; #[derive(Default, TypeUuid)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { + pub resources: Vec>, pub entities: Vec, } @@ -35,7 +36,28 @@ impl DynamicScene { let mut scene = DynamicScene::default(); let type_registry = type_registry.read(); + // Resources archetype + let resources_archetype = world.archetypes().resource(); + for component_id in resources_archetype.components() { + 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_resource(world) { + scene.resources.push(resource.clone_value()); + } + } + } + + // Other archetypes for archetype in world.archetypes().iter() { + if archetype.entities().is_empty() { + continue; + } + let entities_offset = scene.entities.len(); // Create a new dynamic entity for each entity of the given archetype @@ -52,7 +74,8 @@ impl DynamicScene { let reflect_component = world .components() .get_info(component_id) - .and_then(|info| type_registry.get(info.type_id().unwrap())) + .and_then(|info| info.type_id()) + .and_then(|type_id| type_registry.get(type_id)) .and_then(|registration| registration.data::()); if let Some(reflect_component) = reflect_component { for (i, entity) in archetype.entities().iter().enumerate() { @@ -78,9 +101,30 @@ impl DynamicScene { &self, world: &mut World, entity_map: &mut EntityMap, + type_registry: &TypeRegistryArc, ) -> Result<(), SceneSpawnError> { - let registry = world.get_resource::().unwrap().clone(); - let type_registry = registry.read(); + let type_registry = type_registry.read(); + + for resource in self.resources.iter() { + 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 + if world.contains_resource_with_type(registration.type_id()) { + reflect_resource.apply_resource(world, &**resource); + } else { + reflect_resource.insert_resource(world, &**resource); + } + } for scene_entity in self.entities.iter() { // Fetch the entity with the given entity id from the `entity_map` diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 7015195757d79..30a0e49898c03 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -3,7 +3,7 @@ use bevy_app::{Events, ManualEventReader}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, system::Command, world::{Mut, World}, }; @@ -43,6 +43,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")] @@ -123,7 +125,9 @@ impl SceneSpawner { .ok_or_else(|| SceneSpawnError::NonExistentScene { handle: scene_handle.clone_weak(), })?; - scene.write_to_world(world, entity_map) + world.resource_scope(|world, type_registry: Mut| { + scene.write_to_world(world, entity_map, &type_registry) + }) }) } @@ -154,6 +158,35 @@ impl SceneSpawner { handle: scene_handle.clone(), })?; + // Resources archetype + let resources_archetype = scene.world.archetypes().resource(); + for component_id in resources_archetype.components() { + let component_info = scene + .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_resource(&scene.world, world); + } else { + return Err(SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }); + } + } + + // Other archetypes for archetype in scene.world.archetypes().iter() { for scene_entity in archetype.entities() { let entity = *instance_info diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 38af61851033f..defbd60ec314d 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -26,8 +26,43 @@ impl<'a> Serialize for SceneSerializer<'a> { where S: serde::Serializer, { - let mut state = serializer.serialize_seq(Some(self.scene.entities.len()))?; - for entity in self.scene.entities.iter() { + let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?; + state.serialize_field( + SCENE_FIELD_RESOURCES, + &ComponentsSerializer { + components: &self.scene.resources, + registry: self.registry, + }, + )?; + state.serialize_field( + SCENE_FIELD_ENTITIES, + &EntitiesSerializer { + entities: &self.scene.entities, + registry: self.registry, + }, + )?; + state.end() + } +} + +pub struct EntitiesSerializer<'a> { + pub entities: &'a [DynamicEntity], + pub registry: &'a TypeRegistryArc, +} + +impl<'a> EntitiesSerializer<'a> { + pub fn new(entities: &'a [DynamicEntity], registry: &'a TypeRegistryArc) -> Self { + EntitiesSerializer { entities, registry } + } +} + +impl<'a> Serialize for EntitiesSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_seq(Some(self.entities.len()))?; + for entity in self.entities.iter() { state.serialize_element(&EntitySerializer { entity, registry: self.registry, @@ -92,19 +127,102 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> { where D: serde::Deserializer<'de>, { - Ok(DynamicScene { - entities: deserializer.deserialize_seq(SceneEntitySeqVisitor { + deserializer.deserialize_struct( + SCENE_STRUCT, + &[SCENE_FIELD_RESOURCES, SCENE_FIELD_ENTITIES], + SceneVisitor { type_registry: self.type_registry, - })?, + }, + ) + } +} + +#[derive(Deserialize)] +#[serde(field_identifier, rename_all = "lowercase")] +enum SceneField { + Resources, + Entities, +} + +pub const SCENE_STRUCT: &str = "Scene"; +pub const SCENE_FIELD_RESOURCES: &str = "resources"; +pub const SCENE_FIELD_ENTITIES: &str = "entities"; + +struct SceneVisitor<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { + type Value = DynamicScene; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("scene") + } + + fn visit_map(self, mut map: A) -> Result + 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_FIELD_RESOURCES)); + } + resources = Some(map.next_value_seed(ComponentVecDeserializer { + registry: self.type_registry, + })?); + } + SceneField::Entities => { + if entities.is_some() { + return Err(Error::duplicate_field(SCENE_FIELD_ENTITIES)); + } + + entities = Some(map.next_value_seed(EntityVecDeserializer { + type_registry: self.type_registry, + })?); + } + } + } + + let resources = resources + .take() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_ENTITY))?; + + let entities = entities + .take() + .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; + Ok(DynamicScene { + resources, + entities, + }) + } +} + +pub struct EntityVecDeserializer<'a> { + pub type_registry: &'a TypeRegistry, +} + +impl<'a, 'de> DeserializeSeed<'de> for EntityVecDeserializer<'a> { + type Value = Vec; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(EntitySeqVisitor { + type_registry: self.type_registry, }) } } -struct SceneEntitySeqVisitor<'a> { +struct EntitySeqVisitor<'a> { pub type_registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { +impl<'a, 'de> Visitor<'de> for EntitySeqVisitor<'a> { type Value = Vec; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -116,7 +234,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { A: SeqAccess<'de>, { let mut entities = Vec::new(); - while let Some(entity) = seq.next_element_seed(SceneEntityDeserializer { + while let Some(entity) = seq.next_element_seed(EntityDeserializer { type_registry: self.type_registry, })? { entities.push(entity); @@ -126,11 +244,11 @@ impl<'a, 'de> Visitor<'de> for SceneEntitySeqVisitor<'a> { } } -pub struct SceneEntityDeserializer<'a> { +pub struct EntityDeserializer<'a> { pub type_registry: &'a TypeRegistry, } -impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { +impl<'a, 'de> DeserializeSeed<'de> for EntityDeserializer<'a> { type Value = DynamicEntity; fn deserialize(self, deserializer: D) -> Result @@ -140,7 +258,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneEntityDeserializer<'a> { deserializer.deserialize_struct( ENTITY_STRUCT, &[ENTITY_FIELD_ENTITY, ENTITY_FIELD_COMPONENTS], - SceneEntityVisitor { + EntityVisitor { registry: self.type_registry, }, ) @@ -158,11 +276,11 @@ pub const ENTITY_STRUCT: &str = "Entity"; pub const ENTITY_FIELD_ENTITY: &str = "entity"; pub const ENTITY_FIELD_COMPONENTS: &str = "components"; -struct SceneEntityVisitor<'a> { +struct EntityVisitor<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { +impl<'a, 'de> Visitor<'de> for EntityVisitor<'a> { type Value = DynamicEntity; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {