From 58556d2b4a9b9886bd45d187ed4c65a12b474717 Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Sun, 19 Nov 2023 02:37:08 +0000
Subject: [PATCH] Use Intermediary Asset To Load External Levels

---
 src/assets/asset_events.rs | 30 +++++++++++++++++++---
 src/ldtk/mod.rs            | 51 ++++++++++++++++++++++++--------------
 src/lib.rs                 | 11 ++++++--
 3 files changed, 68 insertions(+), 24 deletions(-)

diff --git a/src/assets/asset_events.rs b/src/assets/asset_events.rs
index f09be5d..1bc892e 100644
--- a/src/assets/asset_events.rs
+++ b/src/assets/asset_events.rs
@@ -3,7 +3,7 @@ use std::collections::HashMap;
 use bevy::prelude::*;
 
 use crate::assets::{LevelIndex, TileMetadata, TilesetIndex};
-use crate::ldtk::{Level, Project};
+use crate::ldtk::{Level, LevelSet, Project};
 use crate::{LdtkLevel, LevelDataUpdated};
 
 pub fn handle_ldtk_project_events(
@@ -15,7 +15,7 @@ pub fn handle_ldtk_project_events(
 ) {
 	for event in events.read() {
 		match event {
-			AssetEvent::Added { id } | AssetEvent::Modified { id } => {
+			AssetEvent::LoadedWithDependencies { id } | AssetEvent::Modified { id } => {
 				let handle = Handle::Weak(*id);
 				if let Some(project) = assets.get(handle) {
 					for level in project.get_all_levels() {
@@ -43,6 +43,30 @@ pub fn handle_ldtk_project_events(
 	}
 }
 
+pub(crate) fn handle_ldtk_level_set_events(
+	mut events: EventReader<AssetEvent<LevelSet>>,
+	level_sets: Res<Assets<LevelSet>>,
+	assets: Res<Assets<Level>>,
+	mut level_index: ResMut<LevelIndex>,
+	mut update_events: EventWriter<LevelDataUpdated>,
+) {
+	for event in events.read() {
+		match event {
+			AssetEvent::LoadedWithDependencies { id } | AssetEvent::Modified { id } => {
+				let handle = Handle::Weak(*id);
+				if let Some(level) = level_sets.get(handle) {
+					for level in level.0.iter().flat_map(|hd| assets.get(hd)) {
+						level_index
+							.insert(level.identifier.clone(), LdtkLevel::from(level.clone()));
+						update_events.send(LevelDataUpdated(level.identifier.clone()));
+					}
+				}
+			}
+			_ => {}
+		}
+	}
+}
+
 pub fn handle_ldtk_level_events(
 	mut events: EventReader<AssetEvent<Level>>,
 	assets: Res<Assets<Level>>,
@@ -51,7 +75,7 @@ pub fn handle_ldtk_level_events(
 ) {
 	for event in events.read() {
 		match event {
-			AssetEvent::Added { id } | AssetEvent::Modified { id } => {
+			AssetEvent::LoadedWithDependencies { id } | AssetEvent::Modified { id } => {
 				let handle = Handle::Weak(*id);
 				if let Some(level) = assets.get(handle) {
 					level_index.insert(level.identifier.clone(), LdtkLevel::from(level.clone()));
diff --git a/src/ldtk/mod.rs b/src/ldtk/mod.rs
index d581c00..f60fabd 100644
--- a/src/ldtk/mod.rs
+++ b/src/ldtk/mod.rs
@@ -21,7 +21,7 @@ use bevy::asset::io::Reader;
 use bevy::asset::{
 	AssetLoader, AsyncReadExt, BoxedFuture, LoadContext, UntypedAssetId, VisitAssetDependencies,
 };
-use bevy::prelude::Asset;
+use bevy::prelude::{Asset, Handle};
 use bevy::reflect::{TypePath, TypeUuid, Uuid};
 
 use crate::ldtk;
@@ -178,6 +178,10 @@ pub enum LdtkLoadError {
 
 pub type LdtkProject = Project;
 
+#[derive(Asset, TypePath, TypeUuid)]
+#[uuid = "905609d0-8687-11ee-9e30-4705d421a1e2"]
+pub(crate) struct LevelSet(pub Vec<Handle<Level>>);
+
 #[derive(Default)]
 pub struct LdtkLoader;
 impl AssetLoader for LdtkLoader {
@@ -196,29 +200,38 @@ impl AssetLoader for LdtkLoader {
 			reader.read_to_end(&mut bytes).await?;
 			let project = Project::from_bytes(bytes.as_slice())?;
 
-			let levels = project.levels.iter().flat_map(|level| {
-				log::debug!(
-					"Checking if level is external: {} [{}]",
-					level.identifier,
-					level.external_rel_path.is_some()
-				);
-
-				level
-					.external_rel_path
-					.as_ref()
-					.map(|path| (level.identifier.clone(), path))
-			});
+			let levels = project
+				.levels
+				.iter()
+				.flat_map(|level| {
+					log::debug!(
+						"Checking if level is external: {} [{}]",
+						level.identifier,
+						level.external_rel_path.is_some()
+					);
+
+					level
+						.external_rel_path
+						.as_ref()
+						.map(|path| (level.identifier.clone(), path))
+				})
+				.collect::<Vec<(String, &String)>>();
 
 			let parent_path = load_context.path().parent().map(|pp| pp.to_path_buf());
-			for (id, path) in levels {
-				load_context.labeled_asset_scope(id, |lc| {
-					match &parent_path {
-						Some(parent) => lc.load::<Level>(parent.join(path)),
-						None => lc.load::<Level>(path),
-					};
+			let mut level_set = Vec::with_capacity(levels.len());
+
+			for (_, path) in levels {
+				level_set.push(match &parent_path {
+					Some(parent) => load_context.load::<Level>(parent.join(path)),
+					None => load_context.load::<Level>(path),
 				});
 			}
 
+			load_context.add_labeled_asset(
+				format!("{}ExternalLevels", project.iid),
+				LevelSet(level_set),
+			);
+
 			Ok(project)
 		})
 	}
diff --git a/src/lib.rs b/src/lib.rs
index 8e01210..7b21a56 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -82,12 +82,19 @@ mod __plugin {
 				app.add_event::<super::system::LevelDataUpdated>()
 					.init_asset::<super::ldtk::Project>()
 					.init_asset::<super::ldtk::Level>()
+					.init_asset::<super::ldtk::LevelSet>()
 					.init_asset_loader::<super::ldtk::LdtkLoader>()
 					.init_asset_loader::<super::ldtk::LdtkLevelLoader>()
 					.init_resource::<super::assets::TilesetIndex>()
 					.init_resource::<super::assets::LevelIndex>()
-					.add_systems(Update, super::assets::handle_ldtk_project_events)
-					.add_systems(Update, super::assets::handle_ldtk_level_events);
+					.add_systems(
+						Update,
+						(
+							super::assets::handle_ldtk_project_events,
+							super::assets::handle_ldtk_level_events,
+							super::assets::handle_ldtk_level_set_events,
+						),
+					);
 			}
 		}
 	}
-- 
GitLab