diff --git a/crates/bevy_mod_scripting_core/Cargo.toml b/crates/bevy_mod_scripting_core/Cargo.toml index bf64571a..73eae7c3 100644 --- a/crates/bevy_mod_scripting_core/Cargo.toml +++ b/crates/bevy_mod_scripting_core/Cargo.toml @@ -43,6 +43,7 @@ derivative = "2.2" profiling = { workspace = true } [dev-dependencies] test_utils = { workspace = true } +tokio = { version = "1", features = ["rt", "macros"] } [lints] workspace = true diff --git a/crates/bevy_mod_scripting_core/src/asset.rs b/crates/bevy_mod_scripting_core/src/asset.rs index 990539c4..951e4aef 100644 --- a/crates/bevy_mod_scripting_core/src/asset.rs +++ b/crates/bevy_mod_scripting_core/src/asset.rs @@ -112,6 +112,12 @@ pub struct ScriptAssetSettings { pub script_id_mapper: AssetPathToScriptIdMapper, /// Strategies for mapping asset paths to languages pub script_language_mappers: Vec, + + /// The currently supported asset extensions + /// Should be updated by each scripting plugin to include the extensions it supports. + /// + /// Will be used to populate the script asset loader with the supported extensions + pub supported_extensions: &'static [&'static str], } impl ScriptAssetSettings { @@ -136,6 +142,7 @@ impl Default for ScriptAssetSettings { map: (|path: &AssetPath| path.path().to_string_lossy().into_owned().into()), }, script_language_mappers: vec![], + supported_extensions: &[], } } } @@ -374,6 +381,7 @@ mod tests { fn make_test_settings() -> ScriptAssetSettings { ScriptAssetSettings { + supported_extensions: &[], script_id_mapper: AssetPathToScriptIdMapper { map: |path| path.path().to_string_lossy().into_owned().into(), }, diff --git a/crates/bevy_mod_scripting_core/src/lib.rs b/crates/bevy_mod_scripting_core/src/lib.rs index 2a2f585a..8b24f767 100644 --- a/crates/bevy_mod_scripting_core/src/lib.rs +++ b/crates/bevy_mod_scripting_core/src/lib.rs @@ -55,6 +55,10 @@ pub enum ScriptingSystemSet { /// Types which act like scripting plugins, by selecting a context and runtime /// Each individual combination of context and runtime has specific infrastructure built for it and does not interact with other scripting plugins +/// +/// When implementing a new scripting plugin, also ensure the following implementations exist: +/// - [`Plugin`] for the plugin, both [`Plugin::build`] and [`Plugin::finish`] methods need to be dispatched to the underlying [`ScriptingPlugin`] struct +/// - [`AsMut`] for the plugin struct pub trait IntoScriptPluginParams: 'static { /// The language of the scripts const LANGUAGE: Language; @@ -85,6 +89,9 @@ pub struct ScriptingPlugin { pub context_initializers: Vec>, /// initializers for the contexts run every time before handling events pub context_pre_handling_initializers: Vec>, + + /// Supported extensions to be added to the asset settings without the dot + pub supported_extensions: &'static [&'static str], } impl Plugin for ScriptingPlugin

{ @@ -107,6 +114,8 @@ impl Plugin for ScriptingPlugin

{ register_script_plugin_systems::

(app); once_per_app_init(app); + app.add_supported_script_extensions(self.supported_extensions); + app.world_mut() .resource_mut::() .as_mut() @@ -115,6 +124,10 @@ impl Plugin for ScriptingPlugin

{ register_types(app); } + + fn finish(&self, app: &mut App) { + once_per_app_finalize(app); + } } impl ScriptingPlugin

{ @@ -197,6 +210,33 @@ impl>> ConfigureScriptPlugi } } +fn once_per_app_finalize(app: &mut App) { + #[derive(Resource)] + struct BMSFinalized; + + if app.world().contains_resource::() { + return; + } + app.insert_resource(BMSFinalized); + + // read extensions from asset settings + let asset_settings_extensions = app + .world_mut() + .get_resource_or_init::() + .supported_extensions; + + // convert extensions to static array + bevy::log::info!( + "Initializing BMS with Supported extensions: {:?}", + asset_settings_extensions + ); + + app.register_asset_loader(ScriptAssetLoader { + extensions: asset_settings_extensions, + preprocessor: None, + }); +} + // One of registration of things that need to be done only once per app fn once_per_app_init(app: &mut App) { #[derive(Resource)] @@ -205,7 +245,6 @@ fn once_per_app_init(app: &mut App) { if app.world().contains_resource::() { return; } - app.insert_resource(BMSInitialized); app.add_event::() @@ -213,11 +252,7 @@ fn once_per_app_init(app: &mut App) { .init_resource::() .init_resource::() .init_asset::() - .init_resource::() - .register_asset_loader(ScriptAssetLoader { - extensions: &[], - preprocessor: None, - }); + .init_resource::(); app.add_systems( PostUpdate, @@ -274,3 +309,63 @@ impl AddRuntimeInitializer for App { self } } + +/// Trait for adding a supported extension to the script asset settings. +/// +/// This is only valid in the plugin building phase, as the asset loader will be created in the `finalize` phase. +/// Any changes to the asset settings after that will not be reflected in the asset loader. +pub trait ConfigureScriptAssetSettings { + /// Adds a supported extension to the asset settings + fn add_supported_script_extensions(&mut self, extensions: &[&'static str]) -> &mut Self; +} + +impl ConfigureScriptAssetSettings for App { + fn add_supported_script_extensions(&mut self, extensions: &[&'static str]) -> &mut Self { + let mut asset_settings = self + .world_mut() + .get_resource_or_init::(); + + let mut new_arr = Vec::from(asset_settings.supported_extensions); + + new_arr.extend(extensions); + + let new_arr_static = Vec::leak(new_arr); + + asset_settings.supported_extensions = new_arr_static; + + self + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[tokio::test] + async fn test_asset_extensions_correctly_accumulate() { + let mut app = App::new(); + app.init_resource::(); + app.add_plugins(AssetPlugin::default()); + + app.world_mut() + .resource_mut::() + .supported_extensions = &["lua", "rhai"]; + + once_per_app_finalize(&mut app); + + let asset_loader = app + .world() + .get_resource::() + .expect("Asset loader not found"); + + asset_loader + .get_asset_loader_with_extension("lua") + .await + .expect("Lua loader not found"); + + asset_loader + .get_asset_loader_with_extension("rhai") + .await + .expect("Rhai loader not found"); + } +} diff --git a/crates/languages/bevy_mod_scripting_lua/src/lib.rs b/crates/languages/bevy_mod_scripting_lua/src/lib.rs index 3b3e8937..bcec36a0 100644 --- a/crates/languages/bevy_mod_scripting_lua/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_lua/src/lib.rs @@ -134,6 +134,7 @@ impl Default for LuaScriptingPlugin { .map_err(ScriptError::from_mlua_error)?; Ok(()) }], + supported_extensions: &["lua"], }, } } @@ -150,6 +151,10 @@ impl Plugin for LuaScriptingPlugin { fn build(&self, app: &mut bevy::prelude::App) { self.scripting_plugin.build(app); } + + fn finish(&self, app: &mut bevy::app::App) { + self.scripting_plugin.finish(app); + } } #[profiling::function] /// Load a lua context from a script diff --git a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs index 29caefe9..576e86ec 100644 --- a/crates/languages/bevy_mod_scripting_rhai/src/lib.rs +++ b/crates/languages/bevy_mod_scripting_rhai/src/lib.rs @@ -155,6 +155,7 @@ impl Default for RhaiScriptingPlugin { context.scope.set_or_push("script_id", script.to_owned()); Ok(()) }], + supported_extensions: &["rhai"], }, } } @@ -171,6 +172,10 @@ impl Plugin for RhaiScriptingPlugin { fn build(&self, app: &mut bevy::prelude::App) { self.scripting_plugin.build(app); } + + fn finish(&self, app: &mut bevy::app::App) { + self.scripting_plugin.finish(app); + } } /// Load a rhai context from a script.