From 3ea410824c34aec9dc9631a49233d70d8e482555 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 19 Apr 2023 21:45:11 -0400 Subject: [PATCH 01/10] Add skeleton for new CI check --- tools/ci/src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c41fb4ead..5475cb6b9 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -8,6 +8,7 @@ use std::process; +use bevy::prelude::*; use bevy::utils::HashSet; use xshell::{cmd, Shell}; @@ -20,6 +21,7 @@ enum Check { DocTest, DocCheck, CompileCheck, + ValidateAssets, } impl Check { @@ -32,6 +34,7 @@ impl Check { Check::DocTest, Check::DocCheck, Check::CompileCheck, + Check::ValidateAssets, ] .iter() .copied() @@ -47,6 +50,7 @@ impl Check { Check::DocTest => "doctest", Check::DocCheck => "doccheck", Check::CompileCheck => "compilecheck", + Check::ValidateAssets => "assets", } } @@ -59,6 +63,7 @@ impl Check { "doctest" => Some(Check::DocTest), "doccheck" => Some(Check::DocCheck), "compilecheck" => Some(Check::CompileCheck), + "assets" => Some(Check::ValidateAssets), _ => None, } } @@ -140,6 +145,11 @@ fn main() { .run() .expect("Please fix compiler errors in above output."); } + + if what_to_run.contains(&Check::ValidateAssets) { + info!("Starting Bevy app"); + App::new().run(); + } } #[cfg(test)] From 9a79aa7cb70be4c8920b21d7bb6d0727e51510f0 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 6 May 2023 13:52:42 -0400 Subject: [PATCH 02/10] Skeleton for asset loading --- tools/ci/src/asset_loading.rs | 136 ++++++++++++++++++++++++++++++++++ tools/ci/src/main.rs | 6 +- 2 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 tools/ci/src/asset_loading.rs diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs new file mode 100644 index 000000000..a1bfd2bdd --- /dev/null +++ b/tools/ci/src/asset_loading.rs @@ -0,0 +1,136 @@ +use bevy::{ + app::AppExit, + asset::LoadState, + prelude::*, + reflect::TypeUuid, + utils::{Duration, HashMap, Instant}, +}; + +use std::fmt::{Display, Formatter}; + +pub(super) fn verify_assets_load() { + App::new() + .init_resource::() + .insert_resource(TimeOut { + start: Instant::now(), + max: Duration::from_secs(10), + }) + .add_plugins(MinimalPlugins) + .add_plugin(AssetPlugin::default()) + .add_startup_system(load_assets) + .add_system(check_if_assets_loaded) + .run() +} + +#[derive(Default, Resource, Debug, Clone, PartialEq)] +struct AssetHandles { + font_handles: HashMap>, +} + +impl Display for AssetHandles { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut string = String::new(); + + string += "Fonts\n"; + for (name, handle) in self.font_handles.iter() { + string += &format!(" {} - {:?}\n", name, handle.load_state); + } + + write!(f, "{string}") + } +} + +impl AssetHandles { + fn all_loaded(&self) -> bool { + self.font_handles + .values() + .all(|handle| handle.load_state == LoadState::Loaded) + } +} + +#[derive(Debug)] +struct HandleStatus { + handle: Handle, + load_state: LoadState, +} + +impl Clone for HandleStatus { + fn clone(&self) -> Self { + Self { + handle: self.handle.clone(), + load_state: self.load_state, + } + } +} + +impl Default for HandleStatus { + fn default() -> Self { + Self { + handle: Handle::default(), + load_state: LoadState::NotLoaded, + } + } +} + +impl PartialEq for HandleStatus { + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.load_state == other.load_state + } +} + +#[derive(Resource, Debug)] +struct TimeOut { + start: Instant, + max: Duration, +} + +impl TimeOut { + fn timed_out(&self) -> bool { + self.start.elapsed() > self.max + } +} + +fn load_assets(asset_server: Res, mut asset_handles: ResMut) { + let all_fonts = vec!["fonts/FiraSans-Bold.ttf"]; + + for font in all_fonts { + let font_handle = asset_server.load(font); + asset_handles.font_handles.insert( + font.to_string(), + HandleStatus { + handle: font_handle, + load_state: LoadState::NotLoaded, + }, + ); + } +} + +fn check_if_assets_loaded( + mut asset_handles: ResMut, + asset_server: Res, + mut app_exit_events: ResMut>, + time_out: Res, + mut previous_asset_handles: Local, +) { + *previous_asset_handles = asset_handles.clone(); + + for mut handle_status in asset_handles.font_handles.values_mut() { + if handle_status.load_state == LoadState::NotLoaded { + handle_status.load_state = + asset_server.get_load_state(handle_status.handle.clone_weak()); + } + } + + if asset_handles.all_loaded() { + println!("All assets loaded successfully, exiting."); + app_exit_events.send(AppExit); + } else { + if *asset_handles != *previous_asset_handles { + println!("{}", *asset_handles); + } + + if time_out.timed_out() { + panic!("Timed out waiting for assets to load."); + } + } +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 5475cb6b9..8b137311c 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -12,6 +12,8 @@ use bevy::prelude::*; use bevy::utils::HashSet; use xshell::{cmd, Shell}; +mod asset_loading; + /// The checks that can be run in CI. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum Check { @@ -147,8 +149,8 @@ fn main() { } if what_to_run.contains(&Check::ValidateAssets) { - info!("Starting Bevy app"); - App::new().run(); + info!("Starting Bevy app to check assets..."); + asset_loading::verify_assets_load(); } } From 8dcc151707c673d987aa84e33536d8732bf81539 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 6 May 2023 14:04:55 -0400 Subject: [PATCH 03/10] Debugging --- tools/ci/src/asset_loading.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index a1bfd2bdd..afb67f18e 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -8,6 +8,8 @@ use bevy::{ use std::fmt::{Display, Formatter}; +const ASSET_FOLDER: &str = "emergence_game/assets"; + pub(super) fn verify_assets_load() { App::new() .init_resource::() @@ -16,7 +18,10 @@ pub(super) fn verify_assets_load() { max: Duration::from_secs(10), }) .add_plugins(MinimalPlugins) - .add_plugin(AssetPlugin::default()) + .add_plugin(AssetPlugin { + asset_folder: ASSET_FOLDER.into(), + watch_for_changes: false, + }) .add_startup_system(load_assets) .add_system(check_if_assets_loaded) .run() @@ -91,18 +96,24 @@ impl TimeOut { } fn load_assets(asset_server: Res, mut asset_handles: ResMut) { - let all_fonts = vec!["fonts/FiraSans-Bold.ttf"]; - - for font in all_fonts { - let font_handle = asset_server.load(font); - asset_handles.font_handles.insert( - font.to_string(), - HandleStatus { - handle: font_handle, - load_state: LoadState::NotLoaded, - }, - ); + // Debug the current directory + println!("Current directory: {:?}", std::env::current_dir()); + + // Change directory to the asset folder + std::env::set_current_dir(ASSET_FOLDER).unwrap(); + + // Debug the current directory + println!("New directory: {:?}", std::env::current_dir()); + + // List the files in the current directory + println!("Files in current directory:"); + for entry in std::fs::read_dir(".").unwrap() { + let entry = entry.unwrap(); + println!(" {:?}", entry.path()); } + + // Print the list of all folders and files in the asset folder + let all_fonts = asset_server.load_folder(ASSET_FOLDER).unwrap(); } fn check_if_assets_loaded( From 60a0395b082b6eba0ede4b1c098914c7786c1051 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 6 May 2023 14:21:18 -0400 Subject: [PATCH 04/10] Try to load assets? --- tools/ci/src/asset_loading.rs | 60 ++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index afb67f18e..1a0a34a3b 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -8,7 +8,13 @@ use bevy::{ use std::fmt::{Display, Formatter}; -const ASSET_FOLDER: &str = "emergence_game/assets"; +/// The path to the asset folder, from the root of the repository. +const ROOT_ASSET_FOLDER: &str = "emergence_game/assets"; + +/// The path to the asset folder from the perspective of the [`AssetPlugin`] is specified relative to the executable. +/// +/// As a result, we need to go up two levels to translate. +const PATH_ADAPTOR: &str = "../../"; pub(super) fn verify_assets_load() { App::new() @@ -19,7 +25,7 @@ pub(super) fn verify_assets_load() { }) .add_plugins(MinimalPlugins) .add_plugin(AssetPlugin { - asset_folder: ASSET_FOLDER.into(), + asset_folder: format!("{}{}", PATH_ADAPTOR, ROOT_ASSET_FOLDER), watch_for_changes: false, }) .add_startup_system(load_assets) @@ -96,24 +102,40 @@ impl TimeOut { } fn load_assets(asset_server: Res, mut asset_handles: ResMut) { - // Debug the current directory - println!("Current directory: {:?}", std::env::current_dir()); - - // Change directory to the asset folder - std::env::set_current_dir(ASSET_FOLDER).unwrap(); - - // Debug the current directory - println!("New directory: {:?}", std::env::current_dir()); - - // List the files in the current directory - println!("Files in current directory:"); - for entry in std::fs::read_dir(".").unwrap() { - let entry = entry.unwrap(); - println!(" {:?}", entry.path()); + // Change folder into the asset folder + std::env::set_current_dir(ROOT_ASSET_FOLDER).unwrap(); + + // List the files in the font directory + let files = std::fs::read_dir("fonts") + .unwrap() + .map(|entry| entry.unwrap().path()) + .collect::>(); + + for path_from_root in files { + // Filter by file extension + if let Some(extension) = path_from_root.extension() { + if extension == "ttf" { + // Load the font + let name = path_from_root + .file_stem() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + let bevy_assets_path = + format!("{}{}", PATH_ADAPTOR, path_from_root.to_str().unwrap()); + + let handle = asset_server.load(bevy_assets_path); + asset_handles.font_handles.insert( + name, + HandleStatus { + handle, + load_state: LoadState::NotLoaded, + }, + ); + } + } } - - // Print the list of all folders and files in the asset folder - let all_fonts = asset_server.load_folder(ASSET_FOLDER).unwrap(); } fn check_if_assets_loaded( From 84c82aedf4aa2685fee5f59fdd75c385e1f2c286 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 6 May 2023 14:41:36 -0400 Subject: [PATCH 05/10] Try and fail to just use load_folder --- tools/ci/src/asset_loading.rs | 86 ++++++++--------------------------- 1 file changed, 20 insertions(+), 66 deletions(-) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index 1a0a34a3b..f300f8907 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -2,7 +2,6 @@ use bevy::{ app::AppExit, asset::LoadState, prelude::*, - reflect::TypeUuid, utils::{Duration, HashMap, Instant}, }; @@ -35,15 +34,14 @@ pub(super) fn verify_assets_load() { #[derive(Default, Resource, Debug, Clone, PartialEq)] struct AssetHandles { - font_handles: HashMap>, + handles: HashMap, } impl Display for AssetHandles { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut string = String::new(); - string += "Fonts\n"; - for (name, handle) in self.font_handles.iter() { + for (name, handle) in self.handles.iter() { string += &format!(" {} - {:?}\n", name, handle.load_state); } @@ -53,42 +51,18 @@ impl Display for AssetHandles { impl AssetHandles { fn all_loaded(&self) -> bool { - self.font_handles + self.handles .values() .all(|handle| handle.load_state == LoadState::Loaded) } } -#[derive(Debug)] -struct HandleStatus { - handle: Handle, +#[derive(Debug, Clone, PartialEq)] +struct HandleStatus { + handle: HandleUntyped, load_state: LoadState, } -impl Clone for HandleStatus { - fn clone(&self) -> Self { - Self { - handle: self.handle.clone(), - load_state: self.load_state, - } - } -} - -impl Default for HandleStatus { - fn default() -> Self { - Self { - handle: Handle::default(), - load_state: LoadState::NotLoaded, - } - } -} - -impl PartialEq for HandleStatus { - fn eq(&self, other: &Self) -> bool { - self.handle == other.handle && self.load_state == other.load_state - } -} - #[derive(Resource, Debug)] struct TimeOut { start: Instant, @@ -102,39 +76,18 @@ impl TimeOut { } fn load_assets(asset_server: Res, mut asset_handles: ResMut) { - // Change folder into the asset folder - std::env::set_current_dir(ROOT_ASSET_FOLDER).unwrap(); - - // List the files in the font directory - let files = std::fs::read_dir("fonts") - .unwrap() - .map(|entry| entry.unwrap().path()) - .collect::>(); - - for path_from_root in files { - // Filter by file extension - if let Some(extension) = path_from_root.extension() { - if extension == "ttf" { - // Load the font - let name = path_from_root - .file_stem() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - let bevy_assets_path = - format!("{}{}", PATH_ADAPTOR, path_from_root.to_str().unwrap()); - - let handle = asset_server.load(bevy_assets_path); - asset_handles.font_handles.insert( - name, - HandleStatus { - handle, - load_state: LoadState::NotLoaded, - }, - ); - } - } + // Try to load all assets + let all_handles = asset_server.load_folder(".").unwrap(); + assert!(all_handles.len() > 0); + for handle in all_handles { + let asset_path = asset_server.get_handle_path(&handle).unwrap(); + asset_handles.handles.insert( + asset_path.path().to_str().unwrap().to_string(), + HandleStatus { + handle, + load_state: LoadState::NotLoaded, + }, + ); } } @@ -147,7 +100,7 @@ fn check_if_assets_loaded( ) { *previous_asset_handles = asset_handles.clone(); - for mut handle_status in asset_handles.font_handles.values_mut() { + for mut handle_status in asset_handles.handles.values_mut() { if handle_status.load_state == LoadState::NotLoaded { handle_status.load_state = asset_server.get_load_state(handle_status.handle.clone_weak()); @@ -155,6 +108,7 @@ fn check_if_assets_loaded( } if asset_handles.all_loaded() { + println!("{}", *asset_handles); println!("All assets loaded successfully, exiting."); app_exit_events.send(AppExit); } else { From c7b53740ac1064280cfb546994ac8d44c87f09ed Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 7 May 2023 12:26:13 -0400 Subject: [PATCH 06/10] Register asset loaders --- tools/ci/src/asset_loading.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index f300f8907..73876192c 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -1,6 +1,8 @@ use bevy::{ app::AppExit, asset::LoadState, + audio::AudioPlugin, + gltf::GltfPlugin, prelude::*, utils::{Duration, HashMap, Instant}, }; @@ -23,10 +25,17 @@ pub(super) fn verify_assets_load() { max: Duration::from_secs(10), }) .add_plugins(MinimalPlugins) + // This must come before the asset format plugins for AssetServer to exist .add_plugin(AssetPlugin { asset_folder: format!("{}{}", PATH_ADAPTOR, ROOT_ASSET_FOLDER), watch_for_changes: false, }) + // These plugins are required for the asset loaders to be detected. + // Without this, AssetServer::load_folder will return an empty list + // as file types without an associated loader registered are silently skipped. + .add_plugin(ImagePlugin::default()) + .add_plugin(GltfPlugin) + .add_plugin(AudioPlugin) .add_startup_system(load_assets) .add_system(check_if_assets_loaded) .run() From 04466a08f92618dd752c31e6493537ab4cf318a7 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 7 May 2023 17:46:58 -0400 Subject: [PATCH 07/10] Add more plugins to make sure things can load --- tools/ci/src/asset_loading.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index 73876192c..eff3863a8 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -2,9 +2,14 @@ use bevy::{ app::AppExit, asset::LoadState, audio::AudioPlugin, + core_pipeline::CorePipelinePlugin, gltf::GltfPlugin, + pbr::PbrPlugin, prelude::*, + render::RenderPlugin, + scene::ScenePlugin, utils::{Duration, HashMap, Instant}, + window::{WindowClosed, WindowCreated, WindowResized}, }; use std::fmt::{Display, Formatter}; @@ -33,9 +38,16 @@ pub(super) fn verify_assets_load() { // These plugins are required for the asset loaders to be detected. // Without this, AssetServer::load_folder will return an empty list // as file types without an associated loader registered are silently skipped. + .add_plugin(ScenePlugin) + .add_plugin(AudioPlugin) + .init_resource::>() + .init_resource::>() + .init_resource::>() + .add_plugin(RenderPlugin::default()) .add_plugin(ImagePlugin::default()) + .add_plugin(CorePipelinePlugin) + .add_plugin(PbrPlugin::default()) .add_plugin(GltfPlugin) - .add_plugin(AudioPlugin) .add_startup_system(load_assets) .add_system(check_if_assets_loaded) .run() From c47246288b644e7664ef4a1a1a6421e40fedb60f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 7 May 2023 17:49:22 -0400 Subject: [PATCH 08/10] Clippy --- tools/ci/src/asset_loading.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index eff3863a8..3517a0f3e 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -32,7 +32,7 @@ pub(super) fn verify_assets_load() { .add_plugins(MinimalPlugins) // This must come before the asset format plugins for AssetServer to exist .add_plugin(AssetPlugin { - asset_folder: format!("{}{}", PATH_ADAPTOR, ROOT_ASSET_FOLDER), + asset_folder: format!("{PATH_ADAPTOR}{ROOT_ASSET_FOLDER}"), watch_for_changes: false, }) // These plugins are required for the asset loaders to be detected. @@ -99,7 +99,7 @@ impl TimeOut { fn load_assets(asset_server: Res, mut asset_handles: ResMut) { // Try to load all assets let all_handles = asset_server.load_folder(".").unwrap(); - assert!(all_handles.len() > 0); + assert!(!all_handles.is_empty()); for handle in all_handles { let asset_path = asset_server.get_handle_path(&handle).unwrap(); asset_handles.handles.insert( From 75888147a5722c59d192dfb849277c45ad929971 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 7 May 2023 17:55:41 -0400 Subject: [PATCH 09/10] Sort handles by name --- Cargo.lock | 1 + tools/ci/Cargo.toml | 1 + tools/ci/src/asset_loading.rs | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7f1152d1b..451e1498d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,6 +1253,7 @@ name = "ci" version = "0.1.0" dependencies = [ "bevy", + "itertools", "xshell", ] diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 70455dbaf..e4a2b2559 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -9,3 +9,4 @@ license = "MIT OR Apache-2.0" [dependencies] xshell = "0.2" bevy = "0.10" +itertools = "0.10" diff --git a/tools/ci/src/asset_loading.rs b/tools/ci/src/asset_loading.rs index 3517a0f3e..298b572ab 100644 --- a/tools/ci/src/asset_loading.rs +++ b/tools/ci/src/asset_loading.rs @@ -11,6 +11,7 @@ use bevy::{ utils::{Duration, HashMap, Instant}, window::{WindowClosed, WindowCreated, WindowResized}, }; +use itertools::Itertools; use std::fmt::{Display, Formatter}; @@ -62,7 +63,10 @@ impl Display for AssetHandles { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut string = String::new(); - for (name, handle) in self.handles.iter() { + // Sort the handles alphabetically by name + for name in self.handles.keys().sorted() { + let handle = self.handles.get(name).unwrap(); + string += &format!(" {} - {:?}\n", name, handle.load_state); } From 029249615d5897c46a1d41ede25df04c82641a1a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 7 May 2023 18:23:34 -0400 Subject: [PATCH 10/10] Add assets check to CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c914fad2..81a5e6b31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,7 @@ jobs: - doccheck - doctest - test + - assets include: - ci-argument: clippy toolchain-components: clippy