@@ -8,8 +8,11 @@ use bevy::{
8
8
app:: { App , PreUpdate } ,
9
9
asset:: { Asset , AssetEvent , AssetId , AssetLoader , Assets } ,
10
10
ecs:: system:: Resource ,
11
- log:: { error, info, trace} ,
12
- prelude:: { Commands , EventReader , IntoSystemConfigs , IntoSystemSetConfigs , Res , ResMut } ,
11
+ log:: { debug, error, info, trace} ,
12
+ prelude:: {
13
+ Commands , Event , EventReader , EventWriter , IntoSystemConfigs , IntoSystemSetConfigs , Res ,
14
+ ResMut ,
15
+ } ,
13
16
reflect:: TypePath ,
14
17
utils:: HashMap ,
15
18
} ;
@@ -49,6 +52,13 @@ pub struct ScriptAsset {
49
52
pub asset_path : PathBuf ,
50
53
}
51
54
55
+ #[ derive( Event , Debug , Clone ) ]
56
+ pub ( crate ) enum ScriptAssetEvent {
57
+ Added ( ScriptMetadata ) ,
58
+ Removed ( ScriptMetadata ) ,
59
+ Modified ( ScriptMetadata ) ,
60
+ }
61
+
52
62
#[ derive( Default ) ]
53
63
pub struct ScriptAssetLoader {
54
64
/// The file extensions this loader should handle
@@ -140,6 +150,7 @@ pub struct ScriptMetadataStore {
140
150
141
151
#[ derive( Debug , Clone , PartialEq , Eq ) ]
142
152
pub struct ScriptMetadata {
153
+ pub asset_id : AssetId < ScriptAsset > ,
143
154
pub script_id : ScriptId ,
144
155
pub language : Language ,
145
156
}
@@ -157,78 +168,94 @@ impl ScriptMetadataStore {
157
168
pub fn remove ( & mut self , id : AssetId < ScriptAsset > ) -> Option < ScriptMetadata > {
158
169
self . map . remove ( & id)
159
170
}
171
+
172
+ pub fn contains ( & self , id : AssetId < ScriptAsset > ) -> bool {
173
+ self . map . contains_key ( & id)
174
+ }
160
175
}
161
176
162
- /// Listens to `AssetEvent<ScriptAsset>::Added` events and populates the script metadata store
163
- pub fn insert_script_metadata (
177
+ /// Converts incoming asset events, into internal script asset events, also loads and inserts metadata for newly added scripts
178
+ pub ( crate ) fn dispatch_script_asset_events (
164
179
mut events : EventReader < AssetEvent < ScriptAsset > > ,
165
- script_assets : Res < Assets < ScriptAsset > > ,
166
- mut asset_path_map : ResMut < ScriptMetadataStore > ,
180
+ mut script_asset_events : EventWriter < ScriptAssetEvent > ,
181
+ assets : Res < Assets < ScriptAsset > > ,
182
+ mut metadata_store : ResMut < ScriptMetadataStore > ,
167
183
settings : Res < ScriptAssetSettings > ,
168
184
) {
169
185
for event in events. read ( ) {
170
- if let AssetEvent :: LoadedWithDependencies { id } = event {
171
- let asset = script_assets. get ( * id) ;
172
- if let Some ( asset) = asset {
173
- let path = & asset. asset_path ;
174
- let converter = settings. script_id_mapper . map ;
175
- let script_id = converter ( path) ;
176
-
177
- let language = settings. select_script_language ( path) ;
178
- let metadata = ScriptMetadata {
179
- script_id,
180
- language,
181
- } ;
182
- info ! ( "Populating script metadata for script: {:?}:" , metadata) ;
183
- asset_path_map. insert ( * id, metadata) ;
184
- } else {
185
- error ! ( "A script was added but it's asset was not found, failed to compute metadata. This script will not be loaded. {}" , id) ;
186
+ match event {
187
+ AssetEvent :: LoadedWithDependencies { id } | AssetEvent :: Added { id } => {
188
+ // these can occur multiple times, we only send one added event though
189
+ if !metadata_store. contains ( * id) {
190
+ let asset = assets. get ( * id) ;
191
+ if let Some ( asset) = asset {
192
+ let path = & asset. asset_path ;
193
+ let converter = settings. script_id_mapper . map ;
194
+ let script_id = converter ( path) ;
195
+
196
+ let language = settings. select_script_language ( path) ;
197
+ let metadata = ScriptMetadata {
198
+ asset_id : * id,
199
+ script_id,
200
+ language,
201
+ } ;
202
+ debug ! ( "Script loaded, populating metadata: {:?}:" , metadata) ;
203
+ script_asset_events. send ( ScriptAssetEvent :: Added ( metadata. clone ( ) ) ) ;
204
+ metadata_store. insert ( * id, metadata) ;
205
+ } else {
206
+ error ! ( "A script was added but it's asset was not found, failed to compute metadata. This script will not be loaded. {}" , id) ;
207
+ }
208
+ }
209
+ }
210
+ AssetEvent :: Removed { id } => {
211
+ if let Some ( metadata) = metadata_store. get ( * id) {
212
+ debug ! ( "Script removed: {:?}" , metadata) ;
213
+ script_asset_events. send ( ScriptAssetEvent :: Removed ( metadata. clone ( ) ) ) ;
214
+ } else {
215
+ error ! ( "Script metadata not found for removed script asset: {}. Cannot properly clean up script" , id) ;
216
+ }
186
217
}
218
+ AssetEvent :: Modified { id } => {
219
+ if let Some ( metadata) = metadata_store. get ( * id) {
220
+ debug ! ( "Script modified: {:?}" , metadata) ;
221
+ script_asset_events. send ( ScriptAssetEvent :: Modified ( metadata. clone ( ) ) ) ;
222
+ } else {
223
+ error ! ( "Script metadata not found for modified script asset: {}. Cannot properly update script" , id) ;
224
+ }
225
+ }
226
+ _ => { }
187
227
}
188
228
}
189
229
}
190
230
191
- /// Listens to [`AssetEvent<ScriptAsset> ::Removed`] events and removes the corresponding script metadata
192
- pub fn remove_script_metadata (
193
- mut events : EventReader < AssetEvent < ScriptAsset > > ,
231
+ /// Listens to [`ScriptAssetEvent ::Removed`] events and removes the corresponding script metadata
232
+ pub ( crate ) fn remove_script_metadata (
233
+ mut events : EventReader < ScriptAssetEvent > ,
194
234
mut asset_path_map : ResMut < ScriptMetadataStore > ,
195
235
) {
196
236
for event in events. read ( ) {
197
- if let AssetEvent :: Removed { id } = event {
198
- let previous = asset_path_map. remove ( * id ) ;
237
+ if let ScriptAssetEvent :: Removed ( metadata ) = event {
238
+ let previous = asset_path_map. remove ( metadata . asset_id ) ;
199
239
if let Some ( previous) = previous {
200
- info ! ( "Removed script metadata for removed script : {:?}" , previous) ;
240
+ debug ! ( "Removed script metadata: {:?}" , previous) ;
201
241
}
202
242
}
203
243
}
204
244
}
205
245
206
- /// Listens to [`AssetEvent<ScriptAsset> `] events and dispatches [`CreateOrUpdateScript`] and [`DeleteScript`] commands accordingly.
246
+ /// Listens to [`ScriptAssetEvent `] events and dispatches [`CreateOrUpdateScript`] and [`DeleteScript`] commands accordingly.
207
247
///
208
248
/// Allows for hot-reloading of scripts.
209
- pub fn sync_script_data < P : IntoScriptPluginParams > (
210
- mut events : EventReader < AssetEvent < ScriptAsset > > ,
249
+ pub ( crate ) fn sync_script_data < P : IntoScriptPluginParams > (
250
+ mut events : EventReader < ScriptAssetEvent > ,
211
251
script_assets : Res < Assets < ScriptAsset > > ,
212
- script_metadata : Res < ScriptMetadataStore > ,
213
252
mut commands : Commands ,
214
253
) {
215
254
for event in events. read ( ) {
216
255
trace ! ( "{}: Received script asset event: {:?}" , P :: LANGUAGE , event) ;
217
256
match event {
218
257
// emitted when a new script asset is loaded for the first time
219
- AssetEvent :: LoadedWithDependencies { id } | AssetEvent :: Modified { id } => {
220
- let metadata = match script_metadata. get ( * id) {
221
- Some ( m) => m,
222
- None => {
223
- error ! (
224
- "{}: Script metadata not found for script asset with id: {}. Cannot load script." ,
225
- P :: LANGUAGE ,
226
- id
227
- ) ;
228
- continue ;
229
- }
230
- } ;
231
-
258
+ ScriptAssetEvent :: Added ( metadata) | ScriptAssetEvent :: Modified ( metadata) => {
232
259
if metadata. language != P :: LANGUAGE {
233
260
trace ! (
234
261
"{}: Script asset with id: {} is for a different langauge than this sync system. Skipping." ,
@@ -238,43 +265,20 @@ pub fn sync_script_data<P: IntoScriptPluginParams>(
238
265
continue ;
239
266
}
240
267
241
- info ! (
242
- "{}: Dispatching Creation/Modification command for script: {:?}. Asset Id: {}" ,
243
- P :: LANGUAGE ,
244
- metadata,
245
- id
246
- ) ;
268
+ info ! ( "{}: Loading Script: {:?}" , P :: LANGUAGE , metadata. script_id, ) ;
247
269
248
- if let Some ( asset) = script_assets. get ( * id ) {
270
+ if let Some ( asset) = script_assets. get ( metadata . asset_id ) {
249
271
commands. queue ( CreateOrUpdateScript :: < P > :: new (
250
272
metadata. script_id . clone ( ) ,
251
273
asset. content . clone ( ) ,
252
274
Some ( script_assets. reserve_handle ( ) . clone_weak ( ) ) ,
253
275
) ) ;
254
276
}
255
277
}
256
- AssetEvent :: Removed { id } => {
257
- let metadata = match script_metadata. get ( * id) {
258
- Some ( m) => m,
259
- None => {
260
- error ! (
261
- "{}: Script metadata not found for script asset with id: {}. Cannot delete script." ,
262
- P :: LANGUAGE ,
263
- id
264
- ) ;
265
- return ;
266
- }
267
- } ;
268
-
269
- info ! (
270
- "{}: Dispatching Deletion command for script: {:?}. Asset Id: {}" ,
271
- P :: LANGUAGE ,
272
- metadata,
273
- id
274
- ) ;
278
+ ScriptAssetEvent :: Removed ( metadata) => {
279
+ info ! ( "{}: Deleting Script: {:?}" , P :: LANGUAGE , metadata. script_id, ) ;
275
280
commands. queue ( DeleteScript :: < P > :: new ( metadata. script_id . clone ( ) ) ) ;
276
281
}
277
- _ => return ,
278
282
} ;
279
283
}
280
284
}
@@ -286,21 +290,22 @@ pub(crate) fn configure_asset_systems(app: &mut App) -> &mut App {
286
290
app. add_systems (
287
291
PreUpdate ,
288
292
(
289
- insert_script_metadata . in_set ( ScriptingSystemSet :: ScriptMetadataInsertion ) ,
293
+ dispatch_script_asset_events . in_set ( ScriptingSystemSet :: ScriptAssetDispatch ) ,
290
294
remove_script_metadata. in_set ( ScriptingSystemSet :: ScriptMetadataRemoval ) ,
291
295
) ,
292
296
)
293
297
. configure_sets (
294
298
PreUpdate ,
295
299
(
296
- ScriptingSystemSet :: ScriptMetadataInsertion . after ( bevy:: asset:: TrackAssets ) ,
300
+ ScriptingSystemSet :: ScriptAssetDispatch . after ( bevy:: asset:: TrackAssets ) ,
297
301
ScriptingSystemSet :: ScriptCommandDispatch
298
- . after ( ScriptingSystemSet :: ScriptMetadataInsertion )
302
+ . after ( ScriptingSystemSet :: ScriptAssetDispatch )
299
303
. before ( ScriptingSystemSet :: ScriptMetadataRemoval ) ,
300
304
) ,
301
305
)
302
306
. init_resource :: < ScriptMetadataStore > ( )
303
- . init_resource :: < ScriptAssetSettings > ( ) ;
307
+ . init_resource :: < ScriptAssetSettings > ( )
308
+ . add_event :: < ScriptAssetEvent > ( ) ;
304
309
305
310
app
306
311
}
@@ -455,6 +460,7 @@ mod tests {
455
460
let mut store = ScriptMetadataStore :: default ( ) ;
456
461
let id = AssetId :: invalid ( ) ;
457
462
let meta = ScriptMetadata {
463
+ asset_id : AssetId :: invalid ( ) ,
458
464
script_id : "test" . into ( ) ,
459
465
language : Language :: Lua ,
460
466
} ;
@@ -544,7 +550,7 @@ mod tests {
544
550
}
545
551
546
552
#[ test]
547
- fn test_asset_metadata_insert_remove_systems ( ) {
553
+ fn test_asset_metadata_systems ( ) {
548
554
// test metadata flow
549
555
let mut app = init_loader_test ( ScriptAssetLoader {
550
556
extensions : & [ ] ,
0 commit comments