diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 4cd871fa7283a..b37135f2e523d 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,4 +1,11 @@ -use bevy_ecs::world::World; +use crate::SceneSpawnError; +use anyhow::Result; +use bevy_ecs::{ + entity::EntityMap, + reflect::{ReflectComponent, ReflectMapEntities}, + world::World, +}; +use bevy_reflect::TypeRegistryArc; use bevy_reflect::TypeUuid; /// To spawn a scene, you can use either: @@ -17,4 +24,49 @@ impl Scene { pub fn new(world: World) -> Self { Self { world } } + /// Write the scene's world into the provided world, given a entity mapping. + pub fn write_to_world( + &self, + world: &mut World, + entity_map: &mut EntityMap, + ) -> Result<(), SceneSpawnError> { + let type_registry = world.resource::().clone(); + let type_registry = type_registry.read(); + for archetype in self.world.archetypes().iter() { + for scene_entity in archetype.entities() { + let entity = entity_map + .entry(*scene_entity) + .or_insert_with(|| world.spawn().id()); + for component_id in archetype.components() { + let component_info = self + .world + .components() + .get_info(component_id) + .expect("component_ids in archetypes should have ComponentInfo"); + + let reflect_component = type_registry + .get(component_info.type_id().unwrap()) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + }) + .and_then(|registration| { + registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredComponent { + type_name: component_info.name().to_string(), + } + }) + })?; + reflect_component.copy_component(&self.world, world, *scene_entity, *entity); + } + } + } + for registration in type_registry.iter() { + if let Some(map_entities_reflect) = registration.data::() { + map_entities_reflect + .map_entities(world, entity_map) + .unwrap(); + } + } + Ok(()) + } } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index dd6cf489f195d..320884ff2dbcc 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -2,13 +2,11 @@ use crate::{DynamicScene, Scene}; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, - event::{Events, ManualEventReader}, - reflect::{ReflectComponent, ReflectMapEntities}, + event::ManualEventReader, system::Command, - world::{Mut, World}, + world::{EntityRef, Mut, World}, }; use bevy_hierarchy::{AddChild, Parent}; -use bevy_reflect::TypeRegistryArc; use bevy_utils::{tracing::error, HashMap}; use thiserror::Error; use uuid::Uuid; @@ -16,6 +14,7 @@ use uuid::Uuid; #[derive(Debug)] struct InstanceInfo { entity_map: EntityMap, + parent: Option, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -27,17 +26,86 @@ impl InstanceId { } } +#[derive(Hash, Debug, PartialEq, Eq)] +pub enum SceneHandle { + World(Handle), + Reflected(Handle), +} +impl Clone for SceneHandle { + fn clone(&self) -> Self { + match self { + Self::World(handle) => Self::World(handle.clone_weak()), + Self::Reflected(handle) => Self::Reflected(handle.clone_weak()), + } + } +} +impl SceneHandle { + fn write_to_world(&self, world: &mut World, entity_map: &mut EntityMap) -> SpawnResult<()> { + let err = || self.non_existing(); + match self { + Self::Reflected(scene) => { + world.resource_scope(|world, scenes: Mut>| { + let scene = scenes.get(scene).ok_or_else(err)?; + scene.write_to_world(world, entity_map) + })?; + } + Self::World(scene) => world.resource_scope(|world, scenes: Mut>| { + let scene = scenes.get(scene).ok_or_else(err)?; + scene.write_to_world(world, entity_map) + })?, + }; + Ok(()) + } + + fn non_existing(&self) -> SceneSpawnError { + SceneSpawnError::NonExistentScene { + handle: self.clone(), + } + } +} +impl From> for SceneHandle { + fn from(handle: Handle) -> Self { + Self::World(handle) + } +} +impl From> for SceneHandle { + fn from(handle: Handle) -> Self { + Self::Reflected(handle) + } +} + +/// Request given to [`SceneSpawner`] for spawning a scene. +#[derive(Debug, Clone)] +struct SceneToSpawn { + scene: SceneHandle, + instance: InstanceId, + parent: Option, +} + +impl SceneToSpawn { + fn new(scene: impl Into, instance: InstanceId, parent: Option) -> Self { + Self { + scene: scene.into(), + instance, + parent, + } + } +} #[derive(Default)] pub struct SceneSpawner { - spawned_scenes: HashMap, Vec>, - spawned_dynamic_scenes: HashMap, Vec>, - spawned_instances: HashMap, - scene_asset_event_reader: ManualEventReader>, - dynamic_scenes_to_spawn: Vec<(Handle, InstanceId)>, - scenes_to_spawn: Vec<(Handle, InstanceId)>, - scenes_to_despawn: Vec>, + instances: HashMap>, + instances_info: HashMap, + readers: SceneEventReaders, + scenes_to_spawn: Vec, instances_to_despawn: Vec, - scenes_with_parent: Vec<(InstanceId, Entity)>, +} + +/// Helper struct to wrap `ManualEventReader` for the various scene handle +/// types. +#[derive(Default)] +struct SceneEventReaders { + dynamic: ManualEventReader>, + real: ManualEventReader>, } #[derive(Error, Debug)] @@ -47,332 +115,300 @@ pub enum SceneSpawnError { #[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")] - NonExistentScene { handle: Handle }, - #[error("scene does not exist")] - NonExistentRealScene { handle: Handle }, + NonExistentScene { handle: SceneHandle }, } +pub type SpawnResult = Result; impl SceneSpawner { - pub fn spawn_dynamic(&mut self, scene_handle: Handle) { + /// Spawn a scene. + /// + /// This will only update the world when [`scene_spawner_system`] runs, see + /// [`SceneSpawner::spawn_sync`] for a method with immediate world update. + /// + /// The returned [`InstanceId`] can be used later to refer to the specific + /// instance of the scene you spawned. + pub fn spawn(&mut self, scene_handle: Handle) -> InstanceId { let instance_id = InstanceId::new(); - self.dynamic_scenes_to_spawn - .push((scene_handle, instance_id)); + self.scenes_to_spawn + .push(SceneToSpawn::new(scene_handle, instance_id, None)); + instance_id } - pub fn spawn_dynamic_as_child( - &mut self, - scene_handle: Handle, - parent: Entity, - ) -> InstanceId { + /// Spawn a dynamic scene. See [`SceneSpawner::spawn`]. + pub fn spawn_dynamic(&mut self, scene_handle: Handle) -> InstanceId { let instance_id = InstanceId::new(); - self.dynamic_scenes_to_spawn - .push((scene_handle, instance_id)); - self.scenes_with_parent.push((instance_id, parent)); + self.scenes_to_spawn + .push(SceneToSpawn::new(scene_handle, instance_id, None)); instance_id } - pub fn spawn(&mut self, scene_handle: Handle) -> InstanceId { + /// Spawn a scene as a child of an existing entity. + /// + /// The returned [`InstanceId`] can be used later to refer to the specific + /// instance of the scene you spawned. + pub fn spawn_as_child(&mut self, scene_handle: Handle, parent: Entity) -> InstanceId { let instance_id = InstanceId::new(); - self.scenes_to_spawn.push((scene_handle, instance_id)); + self.scenes_to_spawn + .push(SceneToSpawn::new(scene_handle, instance_id, Some(parent))); instance_id } - pub fn spawn_as_child(&mut self, scene_handle: Handle, parent: Entity) -> InstanceId { + /// Spawn a dynamic scene as a child of an existing entity. See + /// [`SceneSpawner::spawn_as_child`]. + pub fn spawn_dynamic_as_child( + &mut self, + scene_handle: Handle, + parent: Entity, + ) -> InstanceId { let instance_id = InstanceId::new(); - self.scenes_to_spawn.push((scene_handle, instance_id)); - self.scenes_with_parent.push((instance_id, parent)); + self.scenes_to_spawn + .push(SceneToSpawn::new(scene_handle, instance_id, Some(parent))); instance_id } - pub fn despawn(&mut self, scene_handle: Handle) { - self.scenes_to_despawn.push(scene_handle); + /// Despawn the provided scene. This will remove the scene and + /// all its related entities from the world. + /// + /// This will only update the world when [`scene_spawner_system`] runs, see + /// [`SceneSpawner::despawn_sync`] for a method with immediate world + /// update. + pub fn despawn(&mut self, scene_handle: Handle) -> SpawnResult<()> { + self.despawn_scene(scene_handle.into()) } - pub fn despawn_instance(&mut self, instance_id: InstanceId) { - self.instances_to_despawn.push(instance_id); + /// Despawn the provided dynamic scene. See [`SceneSpawner::despawn`]. + pub fn despawn_dynamic(&mut self, scene_handle: Handle) -> SpawnResult<()> { + self.despawn_scene(scene_handle.into()) } + fn despawn_scene(&mut self, scene_handle: SceneHandle) -> SpawnResult<()> { + let err = || scene_handle.non_existing(); + for instance_id in self.instances.get(&scene_handle).ok_or_else(err)? { + self.instances_to_despawn.push(*instance_id); + } + Ok(()) + } + + /// Despawns immediately the provided scene. + /// + /// This will remove the scene and all its related entities from the world. + /// The world will be updated before this method returns. Requires + /// exclusive world acces through `&mut World`. + /// + /// [`SceneSpawner::despawn`] does the same thing, but does not require + /// exclusive world access, it will update the world when + /// [`scene_spawner_system`] runs. pub fn despawn_sync( + &mut self, + world: &mut World, + scene_handle: Handle, + ) -> SpawnResult<()> { + self.despawn_scene_sync(world, scene_handle.into()) + } + + /// Despawns immediately the provided dynamic scene. See + /// [`SceneSpawner::despawn_sync`]. + pub fn despawn_dynamic_sync( &mut self, world: &mut World, scene_handle: Handle, - ) -> Result<(), SceneSpawnError> { - if let Some(instance_ids) = self.spawned_dynamic_scenes.remove(&scene_handle) { - for instance_id in instance_ids { - self.despawn_instance_sync(world, &instance_id); + ) -> SpawnResult<()> { + self.despawn_scene_sync(world, scene_handle.into()) + } + + fn despawn_scene_sync( + &mut self, + world: &mut World, + scene_handle: SceneHandle, + ) -> SpawnResult<()> { + let err = || scene_handle.non_existing(); + for instance_id in self.instances.get(&scene_handle).ok_or_else(err)? { + let instance_info = self.instances_info.remove(instance_id).ok_or_else(err)?; + for entity in instance_info.entity_map.values() { + // Ignore result: if it is already despawned, good! + let _ = world.despawn(entity); } } + self.instances.remove(&scene_handle); Ok(()) } + pub fn despawn_instance(&mut self, instance_id: InstanceId) { + self.instances_to_despawn.push(instance_id); + } + pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) { - if let Some(instance) = self.spawned_instances.remove(instance_id) { - for entity in instance.entity_map.values() { + if let Some(instance_info) = self.instances_info.remove(instance_id) { + for entity in instance_info.entity_map.values() { + // Ignore result: if it is already despawned, good! let _ = world.despawn(entity); } } } - pub fn spawn_dynamic_sync( + /// Spawn a scene into the world immediately. + /// + /// The world will be updated before this method returns. Requires + /// exclusive world acces through [`&mut World`]. + pub fn spawn_sync( &mut self, world: &mut World, - scene_handle: &Handle, - ) -> Result<(), SceneSpawnError> { - let mut entity_map = EntityMap::default(); - Self::spawn_dynamic_internal(world, scene_handle, &mut entity_map)?; - let instance_id = InstanceId::new(); - self.spawned_instances - .insert(instance_id, InstanceInfo { entity_map }); - let spawned = self - .spawned_dynamic_scenes - .entry(scene_handle.clone()) - .or_insert_with(Vec::new); - spawned.push(instance_id); - Ok(()) - } - - fn spawn_dynamic_internal( - world: &mut World, - scene_handle: &Handle, - entity_map: &mut EntityMap, - ) -> Result<(), SceneSpawnError> { - world.resource_scope(|world, scenes: Mut>| { - let scene = - scenes - .get(scene_handle) - .ok_or_else(|| SceneSpawnError::NonExistentScene { - handle: scene_handle.clone_weak(), - })?; - scene.write_to_world(world, entity_map) - }) + scene_handle: Handle, + ) -> SpawnResult { + let cmd = SceneToSpawn::new(scene_handle, InstanceId::new(), None); + self.spawn_scene_instance(world, cmd) } - pub fn spawn_sync( + /// Spawn a scene into the world immediately. See + /// [`SceneSpawner::spawn_sync`]. + pub fn spawn_dynamic_sync( &mut self, world: &mut World, - scene_handle: Handle, - ) -> Result { - self.spawn_sync_internal(world, scene_handle, InstanceId::new()) + scene_handle: Handle, + ) -> SpawnResult { + let cmd = SceneToSpawn::new(scene_handle, InstanceId::new(), None); + self.spawn_scene_instance(world, cmd) } - fn spawn_sync_internal( + /// Spawn a scene instance, using the provided [`InstanceId`]. + fn spawn_scene_instance( &mut self, world: &mut World, - scene_handle: Handle, - instance_id: InstanceId, - ) -> Result { - let mut instance_info = InstanceInfo { - entity_map: EntityMap::default(), - }; - let type_registry = world.resource::().clone(); - let type_registry = type_registry.read(); - world.resource_scope(|world, scenes: Mut>| { - let scene = - scenes - .get(&scene_handle) - .ok_or_else(|| SceneSpawnError::NonExistentRealScene { - handle: scene_handle.clone(), - })?; - - for archetype in scene.world.archetypes().iter() { - for scene_entity in archetype.entities() { - let entity = *instance_info - .entity_map - .entry(*scene_entity) - .or_insert_with(|| world.spawn().id()); - for component_id in archetype.components() { - let component_info = scene - .world - .components() - .get_info(component_id) - .expect("component_ids in archetypes should have ComponentInfo"); - - let reflect_component = type_registry - .get(component_info.type_id().unwrap()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - type_name: component_info.name().to_string(), - }) - .and_then(|registration| { - registration.data::().ok_or_else(|| { - SceneSpawnError::UnregisteredComponent { - type_name: component_info.name().to_string(), - } - }) - })?; - reflect_component.copy_component( - &scene.world, - world, - *scene_entity, - entity, - ); - } - } - } - for registration in type_registry.iter() { - if let Some(map_entities_reflect) = registration.data::() { - map_entities_reflect - .map_entities(world, &instance_info.entity_map) - .unwrap(); - } - } - 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(instance_id) - }) + SceneToSpawn { + scene, + instance, + parent, + }: SceneToSpawn, + ) -> SpawnResult { + let mut entity_map = EntityMap::default(); + scene.write_to_world(world, &mut entity_map)?; + + let info = InstanceInfo { entity_map, parent }; + self.instances_info.insert(instance, info); + let spawned = self.instances.entry(scene).or_default(); + spawned.push(instance); + Ok(instance) } pub fn update_spawned_scenes( &mut self, world: &mut World, - scene_handles: &[Handle], - ) -> Result<(), SceneSpawnError> { + scene_handles: &[SceneHandle], + ) -> SpawnResult<()> { for scene_handle in scene_handles { - if let Some(spawned_instances) = self.spawned_dynamic_scenes.get(scene_handle) { - for instance_id in spawned_instances.iter() { - if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) { - Self::spawn_dynamic_internal( - world, - scene_handle, - &mut instance_info.entity_map, - )?; - } + let err = || scene_handle.non_existing(); + for instance_id in self.instances.get(scene_handle).ok_or_else(err)? { + let instance_info = self.instances_info.get_mut(instance_id).ok_or_else(err)?; + scene_handle.write_to_world(world, &mut instance_info.entity_map)?; + if let Some(parent) = instance_info.parent { + self.set_scene_parent(world, *instance_id, parent); } } } Ok(()) } - pub fn despawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> { - let scenes_to_despawn = std::mem::take(&mut self.scenes_to_despawn); - - for scene_handle in scenes_to_despawn { - self.despawn_sync(world, scene_handle)?; - } - Ok(()) - } - - pub fn despawn_queued_instances(&mut self, world: &mut World) { + /// Manually despawn instances marked for elimination. + pub fn despawn_queued_instances(&mut self, world: &mut World) -> SpawnResult<()> { let instances_to_despawn = std::mem::take(&mut self.instances_to_despawn); - for instance_id in instances_to_despawn { - self.despawn_instance_sync(world, &instance_id); + for instance in instances_to_despawn { + self.despawn_instance_sync(world, &instance); } + Ok(()) } - pub fn spawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> { - let scenes_to_spawn = std::mem::take(&mut self.dynamic_scenes_to_spawn); - - for (scene_handle, instance_id) in scenes_to_spawn { - let mut entity_map = EntityMap::default(); - - match Self::spawn_dynamic_internal(world, &scene_handle, &mut entity_map) { + /// Manually spawn scenes marked for creation. + pub fn spawn_queued_scenes(&mut self, world: &mut World) -> SpawnResult<()> { + let scenes_to_spawn = std::mem::take(&mut self.scenes_to_spawn); + for cmd in scenes_to_spawn { + match self.spawn_scene_instance(world, cmd.clone()) { Ok(_) => { - self.spawned_instances - .insert(instance_id, InstanceInfo { entity_map }); - let spawned = self - .spawned_dynamic_scenes - .entry(scene_handle.clone()) - .or_insert_with(Vec::new); - spawned.push(instance_id); + if let Some(parent) = cmd.parent { + self.set_scene_parent(world, cmd.instance, parent); + } } + // The scene to spawn did not exist in the Assets (or + // Assets) collection, meaning it still didn't + // finish loading, so we keep it tucked into the spawn queue to + // try loading it later, once it fully loaded. Err(SceneSpawnError::NonExistentScene { .. }) => { - self.dynamic_scenes_to_spawn - .push((scene_handle, instance_id)); - } - Err(err) => return Err(err), - } - } - - let scenes_to_spawn = std::mem::take(&mut self.scenes_to_spawn); - - for (scene_handle, instance_id) in scenes_to_spawn { - match self.spawn_sync_internal(world, scene_handle, instance_id) { - Ok(_) => {} - Err(SceneSpawnError::NonExistentRealScene { handle }) => { - self.scenes_to_spawn.push((handle, instance_id)); + // NOTE: the handle in NonExistentScene is a weak handle, I + // found that cloning scene_handle **would break Scene (but + // not DynamicScene) loading** + self.scenes_to_spawn.push(cmd); } Err(err) => return Err(err), } } - Ok(()) } - pub(crate) fn set_scene_instance_parent_sync(&mut self, world: &mut World) { - let scenes_with_parent = std::mem::take(&mut self.scenes_with_parent); - - for (instance_id, parent) in scenes_with_parent { - if let Some(instance) = self.spawned_instances.get(&instance_id) { - for entity in instance.entity_map.values() { - // Add the `Parent` component to the scene root, and update the `Children` component of - // the scene parent - if !world - .get_entity(entity) - // This will filter only the scene root entity, as all other from the - // scene have a parent - .map(|entity| entity.contains::()) - // Default is true so that it won't run on an entity that wouldn't exist anymore - // this case shouldn't happen anyway - .unwrap_or(true) - { - AddChild { - parent, - child: entity, - } - .write(world); - } + fn set_scene_parent(&self, world: &mut World, instance_id: InstanceId, parent: Entity) { + // Only the root of the scene _does not_ have a parent. + let has_no_parents = |entity: EntityRef| !entity.contains::(); + let is_scene_root = + |entity, world: &World| world.get_entity(entity).map_or(false, has_no_parents); + + if let Some(instance) = self.instances_info.get(&instance_id) { + for entity in instance.entity_map.values() { + if is_scene_root(entity, world) { + let child = entity; + // FIXME: is this a bug in `AddChild`? + let (parent, child) = (child, parent); + AddChild { parent, child }.write(world); } - } else { - self.scenes_with_parent.push((instance_id, parent)); } } } - /// Check that an scene instance spawned previously is ready to use + /// Check that a scene instance spawned previously is ready to use. pub fn instance_is_ready(&self, instance_id: InstanceId) -> bool { - self.spawned_instances.contains_key(&instance_id) + self.instances_info.contains_key(&instance_id) } - /// Get an iterator over the entities in an instance, once it's spawned + /// Get an iterator over the entities in an instance, once it's spawned. pub fn iter_instance_entities( &'_ self, instance_id: InstanceId, ) -> Option + '_> { - self.spawned_instances + self.instances_info .get(&instance_id) .map(|instance| instance.entity_map.values()) } } +/// Update the world according to queued scene commands. +/// +/// This system runs at the very end of the +/// [`CoreStage::PreUpdate`](bevy_app::prelude::CoreStage::PreUpdate) stage. +/// Meaning That scene updates (typically in the case of hot-reloading) will +/// be visible in the `CoreStage::Update` (the default) stage. pub fn scene_spawner_system(world: &mut World) { world.resource_scope(|world, mut scene_spawner: Mut| { - let scene_asset_events = world.resource::>>(); - - let mut updated_spawned_scenes = Vec::new(); let scene_spawner = &mut *scene_spawner; - for event in scene_spawner - .scene_asset_event_reader - .iter(scene_asset_events) - { + let mut updated_spawned_scenes = Vec::new(); + for event in scene_spawner.readers.dynamic.iter(world.resource()) { if let AssetEvent::Modified { handle } = event { - if scene_spawner.spawned_dynamic_scenes.contains_key(handle) { - updated_spawned_scenes.push(handle.clone_weak()); + let scene_handle = SceneHandle::Reflected(handle.clone_weak()); + if scene_spawner.instances.contains_key(&scene_handle) { + updated_spawned_scenes.push(scene_handle); + } + } + } + for event in scene_spawner.readers.real.iter(world.resource()) { + if let AssetEvent::Modified { handle } = event { + let scene_handle = SceneHandle::World(handle.clone_weak()); + if scene_spawner.instances.contains_key(&scene_handle) { + updated_spawned_scenes.push(scene_handle); } } } - scene_spawner.despawn_queued_scenes(world).unwrap(); - scene_spawner.despawn_queued_instances(world); - scene_spawner - .spawn_queued_scenes(world) - .unwrap_or_else(|err| panic!("{}", err)); + scene_spawner.despawn_queued_instances(world).unwrap(); + scene_spawner.spawn_queued_scenes(world).unwrap(); scene_spawner .update_spawned_scenes(world, &updated_spawned_scenes) .unwrap(); - scene_spawner.set_scene_instance_parent_sync(world); }); }