@@ -62,6 +62,15 @@ pub(crate) struct AssetRefCounter {
6262 pub ( crate ) mark_unused_assets : Arc < Mutex < Vec < HandleId > > > ,
6363}
6464
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+
6574/// Internal data for the asset server.
6675///
6776/// [`AssetServer`] is the public API for interacting with the asset server.
@@ -70,7 +79,7 @@ pub struct AssetServerInternal {
7079 pub ( crate ) asset_ref_counter : AssetRefCounter ,
7180 pub ( crate ) asset_sources : Arc < RwLock < HashMap < SourcePathId , SourceInfo > > > ,
7281 pub ( crate ) asset_lifecycles : Arc < RwLock < HashMap < Uuid , Box < dyn AssetLifecycle > > > > ,
73- loaders : RwLock < Vec < Arc < dyn AssetLoader > > > ,
82+ loaders : RwLock < Vec < MaybeAssetLoader > > ,
7483 extension_to_loader_index : RwLock < HashMap < String , usize > > ,
7584 handle_to_path : Arc < RwLock < HashMap < HandleId , AssetPath < ' static > > > > ,
7685}
@@ -157,6 +166,28 @@ impl AssetServer {
157166 Assets :: new ( self . server . asset_ref_counter . channel . sender . clone ( ) )
158167 }
159168
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+
160191 /// Adds the provided asset loader to the server.
161192 ///
162193 /// If `loader` has one or more supported extensions in conflict with loaders that came before
@@ -166,14 +197,50 @@ impl AssetServer {
166197 T : AssetLoader ,
167198 {
168199 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+
170205 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) ) ) ;
175243 }
176- loaders. push ( Arc :: new ( loader) ) ;
177244 }
178245
179246 /// Gets a strong handle for an asset with the provided id.
@@ -188,7 +255,7 @@ impl AssetServer {
188255 HandleUntyped :: strong ( id. into ( ) , sender)
189256 }
190257
191- fn get_asset_loader ( & self , extension : & str ) -> Result < Arc < dyn AssetLoader > , AssetServerError > {
258+ fn get_asset_loader ( & self , extension : & str ) -> Result < MaybeAssetLoader , AssetServerError > {
192259 let index = {
193260 // scope map to drop lock as soon as possible
194261 let map = self . server . extension_to_loader_index . read ( ) ;
@@ -204,7 +271,8 @@ impl AssetServer {
204271 fn get_path_asset_loader < P : AsRef < Path > > (
205272 & self ,
206273 path : P ,
207- ) -> Result < Arc < dyn AssetLoader > , AssetServerError > {
274+ include_pending : bool ,
275+ ) -> Result < MaybeAssetLoader , AssetServerError > {
208276 let s = path
209277 . as_ref ( )
210278 . file_name ( )
@@ -223,7 +291,9 @@ impl AssetServer {
223291 ext = & ext[ idx + 1 ..] ;
224292 exts. push ( ext) ;
225293 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+ }
227297 }
228298 }
229299 Err ( AssetServerError :: MissingAssetLoader {
@@ -354,12 +424,21 @@ impl AssetServer {
354424 } ;
355425
356426 // 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,
359437 Err ( err) => {
360438 set_asset_failed ( ) ;
361439 return Err ( err) ;
362440 }
441+ Ok ( MaybeAssetLoader :: Pending { .. } ) => unreachable ! ( ) ,
363442 } ;
364443
365444 // load the asset bytes
@@ -492,7 +571,7 @@ impl AssetServer {
492571 if self . asset_io ( ) . is_dir ( & child_path) {
493572 handles. extend ( self . load_folder ( & child_path) ?) ;
494573 } else {
495- if self . get_path_asset_loader ( & child_path) . is_err ( ) {
574+ if self . get_path_asset_loader ( & child_path, true ) . is_err ( ) {
496575 continue ;
497576 }
498577 let handle =
@@ -711,23 +790,28 @@ mod test {
711790 let asset_server = setup ( "." ) ;
712791 asset_server. add_loader ( FakePngLoader ) ;
713792
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" ) ;
716798 }
717799
718800 #[ test]
719801 fn case_insensitive_extensions ( ) {
720802 let asset_server = setup ( "." ) ;
721803 asset_server. add_loader ( FakePngLoader ) ;
722804
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" ) ;
725809 }
726810
727811 #[ test]
728812 fn no_loader ( ) {
729813 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 ) ;
731815 assert ! ( t. is_err( ) ) ;
732816 }
733817
@@ -736,7 +820,7 @@ mod test {
736820 let asset_server = setup ( "." ) ;
737821
738822 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 ) {
740824 Err ( AssetServerError :: MissingAssetLoader { extensions } ) =>
741825 extensions == vec![ "v1.2.3.pong" , "2.3.pong" , "3.pong" , "pong" ] ,
742826 _ => false ,
@@ -771,17 +855,21 @@ mod test {
771855 let asset_server = setup ( "." ) ;
772856 asset_server. add_loader ( FakePngLoader ) ;
773857
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" ) ;
776862 }
777863
778864 #[ test]
779865 fn multiple_extensions ( ) {
780866 let asset_server = setup ( "." ) ;
781867 asset_server. add_loader ( FakeMultipleDotLoader ) ;
782868
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" ) ;
785873 }
786874
787875 fn create_dir_and_file ( file : impl AsRef < Path > ) -> tempfile:: TempDir {
0 commit comments