diff --git a/crates/bevy_legion/legion_core/src/guid_entity_allocator.rs b/crates/bevy_legion/legion_core/src/guid_entity_allocator.rs index bb8ace9445..99d424d62f 100644 --- a/crates/bevy_legion/legion_core/src/guid_entity_allocator.rs +++ b/crates/bevy_legion/legion_core/src/guid_entity_allocator.rs @@ -20,13 +20,17 @@ impl GuidEntityAllocator { let entity = if !self.next_ids.read().is_empty() { self.next_ids.write().pop_front().unwrap() } else { - Entity::new(rand::random::(), Wrapping(1)) + Entity::new(Self::new_entity_id(), Wrapping(1)) }; self.entities.write().insert(entity.index()); entity } + pub fn new_entity_id() -> u32 { + rand::random::() + } + /// Creates an iterator which allocates new `Entity` IDs. pub fn create_entities(&self) -> GuidCreateEntityIter { GuidCreateEntityIter { allocator: self } diff --git a/crates/bevy_scene/Cargo.toml b/crates/bevy_scene/Cargo.toml index e45502b5a5..ea1c60f028 100644 --- a/crates/bevy_scene/Cargo.toml +++ b/crates/bevy_scene/Cargo.toml @@ -9,8 +9,10 @@ bevy_app = { path = "../bevy_app" } bevy_asset = { path = "../bevy_asset" } bevy_type_registry = { path = "../bevy_type_registry" } bevy_property = { path = "../bevy_property" } + legion = { path = "../bevy_legion", features = ["serialize"] } serde = { version = "1.0", features = ["derive"]} ron = { git = "https://github.com/ron-rs/ron", rev = "b43c1074d517131fd0cfc1deb96e11f95d6f42d8" } +uuid = { version = "0.8", features = ["v4", "serde"] } anyhow = "1.0" thiserror = "1.0" \ No newline at end of file diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index efd49bad35..09877ef169 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,10 +1,9 @@ -use anyhow::Result; -use bevy_type_registry::ComponentRegistry; -use bevy_property::{PropertyTypeRegistry, DynamicProperties}; -use legion::prelude::{Resources, World}; -use serde::Serialize; -use std::num::Wrapping; use crate::serde::SceneSerializer; +use anyhow::Result; +use bevy_property::{DynamicProperties, PropertyTypeRegistry}; +use bevy_type_registry::ComponentRegistry; +use legion::{entity::EntityIndex, prelude::World}; +use serde::Serialize; #[derive(Default)] pub struct Scene { @@ -12,7 +11,7 @@ pub struct Scene { } pub struct Entity { - pub entity: u32, + pub entity: EntityIndex, pub components: Vec, } @@ -38,10 +37,8 @@ impl Scene { }) } - let properties = component_registration.get_component_properties( - &component_resource_set, - index, - ); + let properties = component_registration + .get_component_properties(&component_resource_set, index); entities[index].components.push(properties.to_dynamic()); } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 26c490defd..78000e11e7 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -2,14 +2,34 @@ use crate::Scene; use bevy_app::{EventReader, Events, FromResources, GetEventReader}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_type_registry::TypeRegistry; -use legion::prelude::{Entity, Resources, World}; +use legion::{ + entity::EntityIndex, + guid_entity_allocator::GuidEntityAllocator, + prelude::{Entity, Resources, World}, +}; use std::{ collections::{HashMap, HashSet}, num::Wrapping, }; use thiserror::Error; +use uuid::Uuid; + +struct InstanceInfo { + entity_map: HashMap, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +struct InstanceId(Uuid); + +impl InstanceId { + pub fn new() -> Self { + InstanceId(Uuid::new_v4()) + } +} pub struct SceneSpawner { - loaded_scene_entities: HashMap, Vec>, + loaded_scenes: HashSet>, + spawned_scenes: HashMap, Vec>, + spawned_instances: HashMap, scene_asset_event_reader: EventReader>, scenes_to_spawn: Vec>, scenes_to_load: Vec>, @@ -19,7 +39,9 @@ impl FromResources for SceneSpawner { fn from_resources(resources: &Resources) -> Self { SceneSpawner { scene_asset_event_reader: resources.get_event_reader::>(), - loaded_scene_entities: Default::default(), + spawned_scenes: Default::default(), + spawned_instances: Default::default(), + loaded_scenes: Default::default(), scenes_to_spawn: Default::default(), scenes_to_load: Default::default(), } @@ -35,12 +57,12 @@ pub enum SceneSpawnError { } impl SceneSpawner { - pub fn spawn(&mut self, scene: Handle) { - self.scenes_to_spawn.push(scene); + pub fn spawn(&mut self, scene_handle: Handle) { + self.scenes_to_spawn.push(scene_handle); } - pub fn load(&mut self, scene: Handle) { - self.scenes_to_load.push(scene); + pub fn load(&mut self, scene_handle: Handle) { + self.scenes_to_load.push(scene_handle); } pub fn load_sync( @@ -48,6 +70,34 @@ impl SceneSpawner { world: &mut World, resources: &Resources, scene_handle: Handle, + ) -> Result<(), SceneSpawnError> { + Self::load_internal(world, resources, scene_handle, None)?; + self.loaded_scenes.insert(scene_handle); + Ok(()) + } + + pub fn spawn_sync( + &mut self, + world: &mut World, + resources: &Resources, + scene_handle: Handle, + ) -> Result<(), SceneSpawnError> { + let instance_id = InstanceId::new(); + let mut instance_info = InstanceInfo { + entity_map: HashMap::default(), + }; + Self::load_internal(world, resources, scene_handle, Some(&mut instance_info))?; + self.spawned_instances.insert(instance_id, instance_info); + let spawned = self.spawned_scenes.entry(scene_handle).or_insert_with(|| Vec::new()); + spawned.push(instance_id); + Ok(()) + } + + fn load_internal( + world: &mut World, + resources: &Resources, + scene_handle: Handle, + mut instance_info: Option<&mut InstanceInfo>, ) -> Result<(), SceneSpawnError> { let type_registry = resources.get::().unwrap(); let component_registry = type_registry.component.read().unwrap(); @@ -58,16 +108,23 @@ impl SceneSpawner { handle: scene_handle, })?; - // TODO: this vec might not be needed - let mut entity_ids = Vec::with_capacity(scene.entities.len()); for scene_entity in scene.entities.iter() { + let entity_id = if let Some(ref mut instance_info) = instance_info { + *instance_info + .entity_map + .entry(scene_entity.entity) + .or_insert_with(|| GuidEntityAllocator::new_entity_id()) + } else { + scene_entity.entity + }; + let mut entity = Entity::new(entity_id, Wrapping(1)); // TODO: use EntityEntry when legion refactor is finished - let mut entity = Entity::new(scene_entity.entity, Wrapping(1)); if world.get_entity_location(entity).is_none() { - world.entity_allocator.push_next_ids((&[entity]).iter().cloned()); + world + .entity_allocator + .push_next_ids((&[entity]).iter().cloned()); entity = world.insert((), vec![()])[0]; } - entity_ids.push(entity); for component in scene_entity.components.iter() { let component_registration = component_registry .get_with_name(&component.type_name) @@ -77,12 +134,23 @@ impl SceneSpawner { component_registration.add_component_to_entity(world, resources, entity, component); } } - - self.loaded_scene_entities.insert(scene_handle, entity_ids); Ok(()) } - pub fn load_queued_scenes(&mut self, world: &mut World, resources: &Resources) { + pub fn update_spawned_scenes(&mut self, world: &mut World, resources: &Resources, scene_handles: &[Handle]) -> Result<(), SceneSpawnError> { + for scene_handle in scene_handles { + if let Some(spawned_instances) = self.spawned_scenes.get(scene_handle) { + for instance_id in spawned_instances.iter() { + if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) { + Self::load_internal(world, resources, *scene_handle, Some(instance_info))?; + } + } + } + } + Ok(()) + } + + pub fn load_queued_scenes(&mut self, world: &mut World, resources: &Resources) -> Result<(), SceneSpawnError> { let scenes_to_load = self.scenes_to_load.drain(..).collect::>(); let mut non_existent_scenes = Vec::new(); for scene_handle in scenes_to_load { @@ -91,11 +159,29 @@ impl SceneSpawner { Err(SceneSpawnError::NonExistentScene { .. }) => { non_existent_scenes.push(scene_handle) } - Err(err) => panic!("{:?}", err), + Err(err) => return Err(err), } } self.scenes_to_load = non_existent_scenes; + Ok(()) + } + + pub fn spawn_queued_scenes(&mut self, world: &mut World, resources: &Resources) -> Result<(), SceneSpawnError> { + let scenes_to_spawn = self.scenes_to_spawn.drain(..).collect::>(); + let mut non_existent_scenes = Vec::new(); + for scene_handle in scenes_to_spawn { + match self.spawn_sync(world, resources, scene_handle) { + Ok(_) => {} + Err(SceneSpawnError::NonExistentScene { .. }) => { + non_existent_scenes.push(scene_handle) + } + Err(err) => return Err(err), + } + } + + self.scenes_to_spawn = non_existent_scenes; + Ok(()) } } @@ -103,16 +189,22 @@ pub fn scene_spawner_system(world: &mut World, resources: &mut Resources) { let mut scene_spawner = resources.get_mut::().unwrap(); let scene_asset_events = resources.get::>>().unwrap(); + let mut updated_spawned_scenes = Vec::new(); for event in scene_spawner .scene_asset_event_reader .iter(&scene_asset_events) { if let AssetEvent::Modified { handle } = event { - if scene_spawner.loaded_scene_entities.contains_key(handle) { + if scene_spawner.loaded_scenes.contains(handle) { scene_spawner.load(*handle); } + if scene_spawner.spawned_scenes.contains_key(handle) { + updated_spawned_scenes.push(*handle); + } } } - scene_spawner.load_queued_scenes(world, resources); + scene_spawner.load_queued_scenes(world, resources).unwrap(); + scene_spawner.spawn_queued_scenes(world, resources).unwrap(); + scene_spawner.update_spawned_scenes(world, resources, &updated_spawned_scenes).unwrap(); } diff --git a/examples/scene/load_scene.rs b/examples/scene/load_scene.rs index 24f25ac391..5a69945d45 100644 --- a/examples/scene/load_scene.rs +++ b/examples/scene/load_scene.rs @@ -56,13 +56,15 @@ fn load_scene_system(asset_server: Res, mut scene_spawner: ResMut