@@ -62,6 +62,15 @@ pub(crate) struct AssetRefCounter {
62
62
pub ( crate ) mark_unused_assets : Arc < Mutex < Vec < HandleId > > > ,
63
63
}
64
64
65
+ #[ derive( Clone ) ]
66
+ enum MaybeAssetLoader {
67
+ Ready ( Arc < dyn AssetLoader > ) ,
68
+ Pending {
69
+ sender : async_channel:: Sender < ( ) > ,
70
+ receiver : async_channel:: Receiver < ( ) > ,
71
+ } ,
72
+ }
73
+
65
74
/// Internal data for the asset server.
66
75
///
67
76
/// [`AssetServer`] is the public API for interacting with the asset server.
@@ -70,7 +79,7 @@ pub struct AssetServerInternal {
70
79
pub ( crate ) asset_ref_counter : AssetRefCounter ,
71
80
pub ( crate ) asset_sources : Arc < RwLock < HashMap < SourcePathId , SourceInfo > > > ,
72
81
pub ( crate ) asset_lifecycles : Arc < RwLock < HashMap < Uuid , Box < dyn AssetLifecycle > > > > ,
73
- loaders : RwLock < Vec < Arc < dyn AssetLoader > > > ,
82
+ loaders : RwLock < Vec < MaybeAssetLoader > > ,
74
83
extension_to_loader_index : RwLock < HashMap < String , usize > > ,
75
84
handle_to_path : Arc < RwLock < HashMap < HandleId , AssetPath < ' static > > > > ,
76
85
}
@@ -157,6 +166,28 @@ impl AssetServer {
157
166
Assets :: new ( self . server . asset_ref_counter . channel . sender . clone ( ) )
158
167
}
159
168
169
+ /// Pre-register a loader that will later be added.
170
+ ///
171
+ /// Assets loaded with matching extensions will be blocked until the
172
+ /// real loader is added.
173
+ pub fn preregister_loader ( & self , extensions : & [ & str ] ) {
174
+ let mut loaders = self . server . loaders . write ( ) ;
175
+ let loader_index = loaders. len ( ) ;
176
+ for extension in extensions {
177
+ if self
178
+ . server
179
+ . extension_to_loader_index
180
+ . write ( )
181
+ . insert ( extension. to_string ( ) , loader_index)
182
+ . is_some ( )
183
+ {
184
+ warn ! ( "duplicate preregistration for `{extension}`, any assets loaded with the previous loader will never complete." ) ;
185
+ }
186
+ }
187
+ let ( sender, receiver) = async_channel:: bounded ( 1 ) ;
188
+ loaders. push ( MaybeAssetLoader :: Pending { sender, receiver } ) ;
189
+ }
190
+
160
191
/// Adds the provided asset loader to the server.
161
192
///
162
193
/// If `loader` has one or more supported extensions in conflict with loaders that came before
@@ -166,14 +197,50 @@ impl AssetServer {
166
197
T : AssetLoader ,
167
198
{
168
199
let mut loaders = self . server . loaders . write ( ) ;
169
- let loader_index = loaders. len ( ) ;
200
+ let next_loader_index = loaders. len ( ) ;
201
+ let mut maybe_existing_loader_index = None ;
202
+ let mut loader_map = self . server . extension_to_loader_index . write ( ) ;
203
+ let mut maybe_sender = None ;
204
+
170
205
for extension in loader. extensions ( ) {
171
- self . server
172
- . extension_to_loader_index
173
- . write ( )
174
- . insert ( extension. to_string ( ) , loader_index) ;
206
+ if let Some ( & extension_index) = loader_map. get ( * extension) {
207
+ // replacing an existing entry
208
+ match maybe_existing_loader_index {
209
+ None => {
210
+ match & loaders[ extension_index] {
211
+ MaybeAssetLoader :: Ready ( _) => {
212
+ // replacing an existing loader, nothing special to do
213
+ }
214
+ MaybeAssetLoader :: Pending { sender, .. } => {
215
+ // the loader was pre-registered, store the channel to notify pending assets
216
+ maybe_sender = Some ( sender. clone ( ) ) ;
217
+ }
218
+ }
219
+ }
220
+ Some ( index) => {
221
+ // ensure the loader extensions are consistent
222
+ if index != extension_index {
223
+ warn ! ( "inconsistent extensions between loader preregister_loader and add_loader, \
224
+ loading `{extension}` assets will never complete.") ;
225
+ }
226
+ }
227
+ }
228
+
229
+ maybe_existing_loader_index = Some ( extension_index) ;
230
+ } else {
231
+ loader_map. insert ( extension. to_string ( ) , next_loader_index) ;
232
+ }
233
+ }
234
+
235
+ if let Some ( existing_index) = maybe_existing_loader_index {
236
+ loaders[ existing_index] = MaybeAssetLoader :: Ready ( Arc :: new ( loader) ) ;
237
+ if let Some ( sender) = maybe_sender {
238
+ // notify after replacing the loader
239
+ let _ = sender. send_blocking ( ( ) ) ;
240
+ }
241
+ } else {
242
+ loaders. push ( MaybeAssetLoader :: Ready ( Arc :: new ( loader) ) ) ;
175
243
}
176
- loaders. push ( Arc :: new ( loader) ) ;
177
244
}
178
245
179
246
/// Gets a strong handle for an asset with the provided id.
@@ -188,7 +255,7 @@ impl AssetServer {
188
255
HandleUntyped :: strong ( id. into ( ) , sender)
189
256
}
190
257
191
- fn get_asset_loader ( & self , extension : & str ) -> Result < Arc < dyn AssetLoader > , AssetServerError > {
258
+ fn get_asset_loader ( & self , extension : & str ) -> Result < MaybeAssetLoader , AssetServerError > {
192
259
let index = {
193
260
// scope map to drop lock as soon as possible
194
261
let map = self . server . extension_to_loader_index . read ( ) ;
@@ -204,7 +271,8 @@ impl AssetServer {
204
271
fn get_path_asset_loader < P : AsRef < Path > > (
205
272
& self ,
206
273
path : P ,
207
- ) -> Result < Arc < dyn AssetLoader > , AssetServerError > {
274
+ include_pending : bool ,
275
+ ) -> Result < MaybeAssetLoader , AssetServerError > {
208
276
let s = path
209
277
. as_ref ( )
210
278
. file_name ( )
@@ -223,7 +291,9 @@ impl AssetServer {
223
291
ext = & ext[ idx + 1 ..] ;
224
292
exts. push ( ext) ;
225
293
if let Ok ( loader) = self . get_asset_loader ( ext) {
226
- return Ok ( loader) ;
294
+ if include_pending || matches ! ( loader, MaybeAssetLoader :: Ready ( _) ) {
295
+ return Ok ( loader) ;
296
+ }
227
297
}
228
298
}
229
299
Err ( AssetServerError :: MissingAssetLoader {
@@ -354,12 +424,21 @@ impl AssetServer {
354
424
} ;
355
425
356
426
// get the according asset loader
357
- let asset_loader = match self . get_path_asset_loader ( asset_path. path ( ) ) {
358
- Ok ( loader) => loader,
427
+ let mut maybe_asset_loader = self . get_path_asset_loader ( asset_path. path ( ) , true ) ;
428
+
429
+ // if it's still pending, block until notified and refetch the new asset loader
430
+ if let Ok ( MaybeAssetLoader :: Pending { receiver, .. } ) = maybe_asset_loader {
431
+ let _ = receiver. recv ( ) . await ;
432
+ maybe_asset_loader = self . get_path_asset_loader ( asset_path. path ( ) , false ) ;
433
+ }
434
+
435
+ let asset_loader = match maybe_asset_loader {
436
+ Ok ( MaybeAssetLoader :: Ready ( loader) ) => loader,
359
437
Err ( err) => {
360
438
set_asset_failed ( ) ;
361
439
return Err ( err) ;
362
440
}
441
+ Ok ( MaybeAssetLoader :: Pending { .. } ) => unreachable ! ( ) ,
363
442
} ;
364
443
365
444
// load the asset bytes
@@ -492,7 +571,7 @@ impl AssetServer {
492
571
if self . asset_io ( ) . is_dir ( & child_path) {
493
572
handles. extend ( self . load_folder ( & child_path) ?) ;
494
573
} else {
495
- if self . get_path_asset_loader ( & child_path) . is_err ( ) {
574
+ if self . get_path_asset_loader ( & child_path, true ) . is_err ( ) {
496
575
continue ;
497
576
}
498
577
let handle =
@@ -711,23 +790,28 @@ mod test {
711
790
let asset_server = setup ( "." ) ;
712
791
asset_server. add_loader ( FakePngLoader ) ;
713
792
714
- let t = asset_server. get_path_asset_loader ( "test.png" ) ;
715
- assert_eq ! ( t. unwrap( ) . extensions( ) [ 0 ] , "png" ) ;
793
+ let Ok ( MaybeAssetLoader :: Ready ( t) ) = asset_server. get_path_asset_loader ( "test.png" , true ) else {
794
+ panic ! ( ) ;
795
+ } ;
796
+
797
+ assert_eq ! ( t. extensions( ) [ 0 ] , "png" ) ;
716
798
}
717
799
718
800
#[ test]
719
801
fn case_insensitive_extensions ( ) {
720
802
let asset_server = setup ( "." ) ;
721
803
asset_server. add_loader ( FakePngLoader ) ;
722
804
723
- let t = asset_server. get_path_asset_loader ( "test.PNG" ) ;
724
- assert_eq ! ( t. unwrap( ) . extensions( ) [ 0 ] , "png" ) ;
805
+ let Ok ( MaybeAssetLoader :: Ready ( t) ) = asset_server. get_path_asset_loader ( "test.PNG" , true ) else {
806
+ panic ! ( ) ;
807
+ } ;
808
+ assert_eq ! ( t. extensions( ) [ 0 ] , "png" ) ;
725
809
}
726
810
727
811
#[ test]
728
812
fn no_loader ( ) {
729
813
let asset_server = setup ( "." ) ;
730
- let t = asset_server. get_path_asset_loader ( "test.pong" ) ;
814
+ let t = asset_server. get_path_asset_loader ( "test.pong" , true ) ;
731
815
assert ! ( t. is_err( ) ) ;
732
816
}
733
817
@@ -736,7 +820,7 @@ mod test {
736
820
let asset_server = setup ( "." ) ;
737
821
738
822
assert ! (
739
- match asset_server. get_path_asset_loader( "test.v1.2.3.pong" ) {
823
+ match asset_server. get_path_asset_loader( "test.v1.2.3.pong" , true ) {
740
824
Err ( AssetServerError :: MissingAssetLoader { extensions } ) =>
741
825
extensions == vec![ "v1.2.3.pong" , "2.3.pong" , "3.pong" , "pong" ] ,
742
826
_ => false ,
@@ -771,17 +855,21 @@ mod test {
771
855
let asset_server = setup ( "." ) ;
772
856
asset_server. add_loader ( FakePngLoader ) ;
773
857
774
- let t = asset_server. get_path_asset_loader ( "test-v1.2.3.png" ) ;
775
- assert_eq ! ( t. unwrap( ) . extensions( ) [ 0 ] , "png" ) ;
858
+ let Ok ( MaybeAssetLoader :: Ready ( t) ) = asset_server. get_path_asset_loader ( "test-v1.2.3.png" , true ) else {
859
+ panic ! ( ) ;
860
+ } ;
861
+ assert_eq ! ( t. extensions( ) [ 0 ] , "png" ) ;
776
862
}
777
863
778
864
#[ test]
779
865
fn multiple_extensions ( ) {
780
866
let asset_server = setup ( "." ) ;
781
867
asset_server. add_loader ( FakeMultipleDotLoader ) ;
782
868
783
- let t = asset_server. get_path_asset_loader ( "test.test.png" ) ;
784
- assert_eq ! ( t. unwrap( ) . extensions( ) [ 0 ] , "test.png" ) ;
869
+ let Ok ( MaybeAssetLoader :: Ready ( t) ) = asset_server. get_path_asset_loader ( "test.test.png" , true ) else {
870
+ panic ! ( ) ;
871
+ } ;
872
+ assert_eq ! ( t. extensions( ) [ 0 ] , "test.png" ) ;
785
873
}
786
874
787
875
fn create_dir_and_file ( file : impl AsRef < Path > ) -> tempfile:: TempDir {
0 commit comments