@@ -55,6 +55,10 @@ pub enum ScriptingSystemSet {
5555
5656/// Types which act like scripting plugins, by selecting a context and runtime
5757/// Each individual combination of context and runtime has specific infrastructure built for it and does not interact with other scripting plugins
58+ ///
59+ /// When implementing a new scripting plugin, also ensure the following implementations exist:
60+ /// - [`Plugin`] for the plugin, both [`Plugin::build`] and [`Plugin::finish`] methods need to be dispatched to the underlying [`ScriptingPlugin`] struct
61+ /// - [`AsMut<ScriptingPlugin<Self>`] for the plugin struct
5862pub trait IntoScriptPluginParams : ' static {
5963 /// The language of the scripts
6064 const LANGUAGE : Language ;
@@ -85,6 +89,9 @@ pub struct ScriptingPlugin<P: IntoScriptPluginParams> {
8589 pub context_initializers : Vec < ContextInitializer < P > > ,
8690 /// initializers for the contexts run every time before handling events
8791 pub context_pre_handling_initializers : Vec < ContextPreHandlingInitializer < P > > ,
92+
93+ /// Supported extensions to be added to the asset settings without the dot
94+ pub supported_extensions : & ' static [ & ' static str ] ,
8895}
8996
9097impl < P : IntoScriptPluginParams > Plugin for ScriptingPlugin < P > {
@@ -107,6 +114,8 @@ impl<P: IntoScriptPluginParams> Plugin for ScriptingPlugin<P> {
107114 register_script_plugin_systems :: < P > ( app) ;
108115 once_per_app_init ( app) ;
109116
117+ app. add_supported_script_extensions ( self . supported_extensions ) ;
118+
110119 app. world_mut ( )
111120 . resource_mut :: < ScriptAssetSettings > ( )
112121 . as_mut ( )
@@ -115,6 +124,10 @@ impl<P: IntoScriptPluginParams> Plugin for ScriptingPlugin<P> {
115124
116125 register_types ( app) ;
117126 }
127+
128+ fn finish ( & self , app : & mut App ) {
129+ once_per_app_finalize ( app) ;
130+ }
118131}
119132
120133impl < P : IntoScriptPluginParams > ScriptingPlugin < P > {
@@ -197,6 +210,33 @@ impl<P: IntoScriptPluginParams + AsMut<ScriptingPlugin<P>>> ConfigureScriptPlugi
197210 }
198211}
199212
213+ fn once_per_app_finalize ( app : & mut App ) {
214+ #[ derive( Resource ) ]
215+ struct BMSFinalized ;
216+
217+ if app. world ( ) . contains_resource :: < BMSFinalized > ( ) {
218+ return ;
219+ }
220+ app. insert_resource ( BMSFinalized ) ;
221+
222+ // read extensions from asset settings
223+ let asset_settings_extensions = app
224+ . world_mut ( )
225+ . get_resource_or_init :: < ScriptAssetSettings > ( )
226+ . supported_extensions ;
227+
228+ // convert extensions to static array
229+ bevy:: log:: info!(
230+ "Initializing BMS with Supported extensions: {:?}" ,
231+ asset_settings_extensions
232+ ) ;
233+
234+ app. register_asset_loader ( ScriptAssetLoader {
235+ extensions : asset_settings_extensions,
236+ preprocessor : None ,
237+ } ) ;
238+ }
239+
200240// One of registration of things that need to be done only once per app
201241fn once_per_app_init ( app : & mut App ) {
202242 #[ derive( Resource ) ]
@@ -205,19 +245,14 @@ fn once_per_app_init(app: &mut App) {
205245 if app. world ( ) . contains_resource :: < BMSInitialized > ( ) {
206246 return ;
207247 }
208-
209248 app. insert_resource ( BMSInitialized ) ;
210249
211250 app. add_event :: < ScriptErrorEvent > ( )
212251 . add_event :: < ScriptCallbackEvent > ( )
213252 . init_resource :: < AppReflectAllocator > ( )
214253 . init_resource :: < Scripts > ( )
215254 . init_asset :: < ScriptAsset > ( )
216- . init_resource :: < AppScriptFunctionRegistry > ( )
217- . register_asset_loader ( ScriptAssetLoader {
218- extensions : & [ ] ,
219- preprocessor : None ,
220- } ) ;
255+ . init_resource :: < AppScriptFunctionRegistry > ( ) ;
221256
222257 app. add_systems (
223258 PostUpdate ,
@@ -274,3 +309,63 @@ impl AddRuntimeInitializer for App {
274309 self
275310 }
276311}
312+
313+ /// Trait for adding a supported extension to the script asset settings.
314+ ///
315+ /// This is only valid in the plugin building phase, as the asset loader will be created in the `finalize` phase.
316+ /// Any changes to the asset settings after that will not be reflected in the asset loader.
317+ pub trait ConfigureScriptAssetSettings {
318+ /// Adds a supported extension to the asset settings
319+ fn add_supported_script_extensions ( & mut self , extensions : & [ & ' static str ] ) -> & mut Self ;
320+ }
321+
322+ impl ConfigureScriptAssetSettings for App {
323+ fn add_supported_script_extensions ( & mut self , extensions : & [ & ' static str ] ) -> & mut Self {
324+ let mut asset_settings = self
325+ . world_mut ( )
326+ . get_resource_or_init :: < ScriptAssetSettings > ( ) ;
327+
328+ let mut new_arr = Vec :: from ( asset_settings. supported_extensions ) ;
329+
330+ new_arr. extend ( extensions) ;
331+
332+ let new_arr_static = Vec :: leak ( new_arr) ;
333+
334+ asset_settings. supported_extensions = new_arr_static;
335+
336+ self
337+ }
338+ }
339+
340+ #[ cfg( test) ]
341+ mod test {
342+ use super :: * ;
343+
344+ #[ tokio:: test]
345+ async fn test_asset_extensions_correctly_accumulate ( ) {
346+ let mut app = App :: new ( ) ;
347+ app. init_resource :: < ScriptAssetSettings > ( ) ;
348+ app. add_plugins ( AssetPlugin :: default ( ) ) ;
349+
350+ app. world_mut ( )
351+ . resource_mut :: < ScriptAssetSettings > ( )
352+ . supported_extensions = & [ "lua" , "rhai" ] ;
353+
354+ once_per_app_finalize ( & mut app) ;
355+
356+ let asset_loader = app
357+ . world ( )
358+ . get_resource :: < AssetServer > ( )
359+ . expect ( "Asset loader not found" ) ;
360+
361+ asset_loader
362+ . get_asset_loader_with_extension ( "lua" )
363+ . await
364+ . expect ( "Lua loader not found" ) ;
365+
366+ asset_loader
367+ . get_asset_loader_with_extension ( "rhai" )
368+ . await
369+ . expect ( "Rhai loader not found" ) ;
370+ }
371+ }
0 commit comments