Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions crates/bevy_scene/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ pub struct ScenePlugin;
impl Plugin for ScenePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<QueuedScenes>()
.init_resource::<WaitingScenes>()
.init_asset::<ScenePatch>()
.init_asset::<SceneListPatch>()
.add_systems(
Expand All @@ -564,7 +565,9 @@ mod tests {
use bevy_asset::io::memory::{Dir, MemoryAssetReader};
use bevy_asset::io::{AssetSourceBuilder, AssetSourceId};
use bevy_asset::{Asset, AssetApp, AssetLoader, AssetPlugin, AssetServer, Assets, Handle};
use bevy_ecs::lifecycle::HookContext;
use bevy_ecs::prelude::*;
use bevy_ecs::world::DeferredWorld;
use bevy_reflect::TypePath;
use std::path::Path;
use std::sync::Mutex;
Expand Down Expand Up @@ -1342,6 +1345,33 @@ mod tests {
}
}

#[test]
fn queue_spawn_scene_during_spawn() {
#[derive(Component, Default, Clone)]
#[component(on_insert)]
struct SpawnOnInsert;

impl SpawnOnInsert {
fn on_insert(mut world: DeferredWorld, _context: HookContext) {
world.commands().queue_spawn_scene(scene2());
}
}

fn scene1() -> impl Scene {
bsn!(SpawnOnInsert)
}

fn scene2() -> impl Scene {
bsn!(#Name)
}

let mut app = test_app();
let world = app.world_mut();
world.queue_spawn_scene(scene1());

app.update();
}

#[test]
fn drop_is_called_for_uninserted_components() {
#[derive(Component, FromTemplate)]
Expand Down
55 changes: 32 additions & 23 deletions crates/bevy_scene/src/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ pub fn resolve_scene_patches(
assets: Res<AssetServer>,
mut patches: ResMut<Assets<ScenePatch>>,
mut list_patches: ResMut<Assets<SceneListPatch>>,
mut queued: ResMut<QueuedScenes>,
mut waiting: ResMut<WaitingScenes>,
) {
for event in events.read() {
match *event {
Expand All @@ -604,11 +604,11 @@ pub fn resolve_scene_patches(
}
}
AssetEvent::Removed { id } => {
if let Some(waiting) = queued.waiting_scene_entities.remove(&id)
&& !waiting.is_empty()
if let Some(waiting_entities) = waiting.scene_entities.remove(&id)
&& !waiting_entities.is_empty()
{
error!(
"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting:?}"
"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting_entities:?}"
);
}
}
Expand All @@ -628,18 +628,19 @@ pub fn resolve_scene_patches(
}
}
AssetEvent::Removed { id } => {
if let Some(waiting) = queued.waiting_scene_list_spawns.remove(&id)
&& waiting > 0
if let Some(waiting_scene_lists) = waiting.scene_list_spawns.remove(&id)
&& waiting_scene_lists > 0
{
error!(
"Failed to spawn scene list {id:?} {waiting} times because it was removed."
"Failed to spawn scene list {id:?} {waiting_scene_lists} times because it was removed."
);
}

if let Some(waiting) = queued.waiting_related_list_entities.remove(&id)
&& !waiting.is_empty()
if let Some(waiting_related) = waiting.related_list_entities.remove(&id)
&& !waiting_related.is_empty()
{
let waiting_entities = waiting.iter().map(|r| r.entity).collect::<Vec<_>>();
let waiting_entities =
waiting_related.iter().map(|r| r.entity).collect::<Vec<_>>();
error!(
"Failed to spawn related entities for scene list {id:?} because it was removed: {waiting_entities:?}"
);
Expand All @@ -656,9 +657,14 @@ pub struct QueuedScenes {
new_scene_entities: Vec<Entity>,
related_scene_list_spawns: Vec<(RelatedSceneListSpawn, Handle<SceneListPatch>)>,
scene_list_spawns: Vec<Handle<SceneListPatch>>,
waiting_scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,
waiting_related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,
waiting_scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,
}

/// A [`Resource`] that tracks entities / scenes that are waiting for an asset to load
#[derive(Resource, Default)]
pub struct WaitingScenes {
scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,
related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,
scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,
}

pub(crate) struct RelatedSceneListSpawn {
Expand All @@ -678,18 +684,21 @@ pub fn on_add_scene_patch_instance(
pub fn spawn_queued(
world: &mut World,
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
mut queued: Local<QueuedScenes>,
mut bundle_scratch: Local<BundleScratch>,
mut reader: Local<MessageCursor<AssetEvent<ScenePatch>>>,
mut list_reader: Local<MessageCursor<AssetEvent<SceneListPatch>>>,
) {
core::mem::swap(&mut *world.resource_mut::<QueuedScenes>(), &mut queued);
world.resource_scope(|world, mut list_patches: Mut<Assets<SceneListPatch>>| {
world.resource_scope(|world, mut queued: Mut<QueuedScenes>| {
world.resource_scope(|world, mut waiting: Mut<WaitingScenes>| {
loop {
if queued.is_empty() {
break;
}
queued.spawn_queued(
world,
&mut waiting,
scene_patch_instances,
&mut bundle_scratch,
&list_patches,
Expand All @@ -701,7 +710,7 @@ pub fn spawn_queued(
let patches = world.resource::<Assets<ScenePatch>>();
if let AssetEvent::LoadedWithDependencies { id } = event
&& let Some(resolved) = patches.get(*id).and_then(|p| p.resolved.clone())
&& let Some(entities) = queued.waiting_scene_entities.remove(id)
&& let Some(entities) = waiting.scene_entities.remove(id)
{
for entity in entities {
if let Ok(mut entity_mut) = world.get_entity_mut(entity)
Expand All @@ -724,7 +733,7 @@ pub fn spawn_queued(
&& let Some(list_patch) = list_patches.get_mut(*id)
{
if let Some(scene_list_spawns) =
queued.waiting_related_list_entities.remove(id)
waiting.related_list_entities.remove(id)
{
for scene_list_spawn in scene_list_spawns {
let result = list_patch.spawn_with(world, |entity| {
Expand All @@ -737,8 +746,7 @@ pub fn spawn_queued(
}
}

if let Some(waiting_list_spawns) =
queued.waiting_scene_list_spawns.remove(id)
if let Some(waiting_list_spawns) = waiting.scene_list_spawns.remove(id)
{
for _ in 0..waiting_list_spawns {
let result = list_patch.spawn(world);
Expand All @@ -765,6 +773,7 @@ impl QueuedScenes {
fn spawn_queued(
&mut self,
world: &mut World,
waiting_scenes: &mut WaitingScenes,
scene_patch_instances: &mut QueryState<&ScenePatchInstance>,
bundle_scratch: &mut BundleScratch,
list_patches: &Assets<SceneListPatch>,
Expand All @@ -787,8 +796,8 @@ impl QueuedScenes {
);
}
} else {
let entities = self
.waiting_scene_entities
let entities = waiting_scenes
.scene_entities
.entry(handle.clone())
.or_default();
entities.push(entity);
Expand All @@ -810,8 +819,8 @@ impl QueuedScenes {
);
}
} else {
let entities = self
.waiting_related_list_entities
let entities = waiting_scenes
.related_list_entities
.entry(handle)
.or_default();
entities.push(scene_list_spawn);
Expand All @@ -830,7 +839,7 @@ impl QueuedScenes {
);
}
} else {
let count = self.waiting_scene_list_spawns.entry(handle).or_default();
let count = waiting_scenes.scene_list_spawns.entry(handle).or_default();
*count += 1;
}
}
Expand Down