From 45668b926841764a37bf782f3be869c3c70505d9 Mon Sep 17 00:00:00 2001 From: andriyDev Date: Sat, 22 Aug 2026 10:04:40 -0700 Subject: [PATCH 1/5] Stop picking loaders based on their asset type. --- crates/bevy_asset/src/server/loaders.rs | 281 +----------------------- crates/bevy_asset/src/server/mod.rs | 4 +- 2 files changed, 14 insertions(+), 271 deletions(-) diff --git a/crates/bevy_asset/src/server/loaders.rs b/crates/bevy_asset/src/server/loaders.rs index 892521b37a271..dcbb4edaf6198 100644 --- a/crates/bevy_asset/src/server/loaders.rs +++ b/crates/bevy_asset/src/server/loaders.rs @@ -152,52 +152,15 @@ impl AssetLoaders { } /// Find an [`AssetLoader`] based on provided search criteria - pub(crate) fn find( - &self, - asset_type_id: Option, - asset_path: &AssetPath<'_>, - ) -> Option { - // The presence of a label will affect loader choice - let label = asset_path.label(); - - // Try by asset type - let candidates = if let Some(type_id) = asset_type_id { - if label.is_none() { - Some(self.type_id_to_loaders.get(&type_id)?) - } else { - None - } - } else { - None - }; - - if let Some(candidates) = candidates { - if candidates.is_empty() { - return None; - } else if candidates.len() == 1 { - let index = candidates.first().copied().unwrap(); - return self.get_by_index(index); - } - } - - // Asset type is insufficient, use extension information + pub(crate) fn find(&self, asset_path: &AssetPath<'_>) -> Option { let try_extension = |extension| { - if let Some(indices) = self.extension_to_loaders.get(extension) { - if let Some(candidates) = candidates { - if candidates.is_empty() { - indices.last() - } else { - indices - .iter() - .rev() - .find(|index| candidates.contains(index)) - } - } else { - indices.last() - } - } else { - None - } + self.extension_to_loaders + .get(extension) + // Resolve the last loader even though there's ambiguity. Chances are that if there + // are multiple loaders for the same extension, the user was trying to replace the + // default loader for an extension, which would come after the default loader was + // registered. + .and_then(|indices| indices.last()) }; // Try extracting the extension from the path @@ -214,27 +177,8 @@ impl AssetLoaders { } } - // Fallback if no resolution step was conclusive - match candidates? - .last() - .copied() - .and_then(|index| self.get_by_index(index)) - { - Some(loader) => { - warn!( - "Multiple AssetLoaders found for Asset: {:?}; Path: {:?};", - asset_type_id, asset_path - ); - Some(loader) - } - None => { - warn!( - "No AssetLoader found for Asset: {:?}; Path: {:?};", - asset_type_id, asset_path - ); - None - } - } + warn!("No AssetLoader found for AssetPath: {:?};", asset_path); + None } /// Get the [`AssetLoader`] for a given asset type @@ -553,171 +497,6 @@ mod tests { assert!(rx_c1.try_recv().is_ok()); } - /// Full resolution algorithm - #[test] - fn total_resolution() { - let mut loaders = AssetLoaders::default(); - - let (loader_a1_a, rx_a1_a) = Loader::::new(); - - let (loader_b1_b, rx_b1_b) = Loader::::new(); - - let (loader_c1_a, rx_c1_a) = Loader::::new(); - let (loader_c1_b, rx_c1_b) = Loader::::new(); - let (loader_c1_c, rx_c1_c) = Loader::::new(); - - loaders.push(loader_a1_a); - loaders.push(loader_b1_b); - loaders.push(loader_c1_a); - loaders.push(loader_c1_b); - loaders.push(loader_c1_c); - - assert!(rx_a1_a.try_recv().is_ok()); - assert!(rx_b1_b.try_recv().is_ok()); - assert!(rx_c1_a.try_recv().is_ok()); - assert!(rx_c1_b.try_recv().is_ok()); - assert!(rx_c1_c.try_recv().is_ok()); - - // Type and Extension agree - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.a")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_ok()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_err()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.b")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_b1_b.try_recv().is_ok()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_err()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.c")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_ok()); - - // Type should override Extension - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.a")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_ok()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_err()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.b")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_ok()); - assert!(rx_c1_c.try_recv().is_err()); - - // Type should override bad / missing extension - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.x")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_ok()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_err()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_ok()); - assert!(rx_b1_b.try_recv().is_err()); - assert!(rx_c1_a.try_recv().is_err()); - assert!(rx_c1_b.try_recv().is_err()); - assert!(rx_c1_c.try_recv().is_err()); - } - /// Ensure that if there is a complete ambiguity in [`AssetLoader`] to use, prefer most recently registered by asset type. #[test] fn ambiguity_resolution() { @@ -737,44 +516,7 @@ mod tests { let loader = block_on( loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.a")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_a2_a.try_recv().is_err()); - assert!(rx_a3_a.try_recv().is_ok()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset.x")), - ) - .unwrap() - .get(), - ) - .unwrap(); - - loader.extensions(); - - assert!(rx_a1_a.try_recv().is_err()); - assert!(rx_a2_a.try_recv().is_err()); - assert!(rx_a3_a.try_recv().is_ok()); - - let loader = block_on( - loaders - .find( - Some(TypeId::of::()), - &AssetPath::from_path(Path::new("asset")), - ) + .find(&AssetPath::from_path(Path::new("asset.a"))) .unwrap() .get(), ) @@ -782,6 +524,7 @@ mod tests { loader.extensions(); + // The last loader registered was run. assert!(rx_a1_a.try_recv().is_err()); assert!(rx_a2_a.try_recv().is_err()); assert!(rx_a3_a.try_recv().is_ok()); diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index b4263c92a9708..c28121c029a11 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -1447,7 +1447,7 @@ impl AssetServer { } Err(AssetReaderError::NotFound(_)) => { // TODO: Handle error transformation - let loader = { self.read_loaders().find(asset_type_id, asset_path) }; + let loader = { self.read_loaders().find(asset_path) }; let error = || AssetLoadError::MissingAssetLoader { asset_type_id, @@ -1462,7 +1462,7 @@ impl AssetServer { Err(err) => return Err(err.into()), } } else { - let loader = { self.read_loaders().find(asset_type_id, asset_path) }; + let loader = { self.read_loaders().find(asset_path) }; let error = || AssetLoadError::MissingAssetLoader { asset_type_id, From ba5a7704a557f84b7ce126d4bc6167a8184e8844 Mon Sep 17 00:00:00 2001 From: andriyDev Date: Sat, 22 Aug 2026 13:46:24 -0700 Subject: [PATCH 2/5] Remove mapping from asset type to loader. --- crates/bevy_asset/src/loader_builders.rs | 28 ++--- crates/bevy_asset/src/server/loaders.rs | 124 ++--------------------- crates/bevy_asset/src/server/mod.rs | 19 ---- 3 files changed, 15 insertions(+), 156 deletions(-) diff --git a/crates/bevy_asset/src/loader_builders.rs b/crates/bevy_asset/src/loader_builders.rs index e702c78ea32ec..8b9d2dc784cb3 100644 --- a/crates/bevy_asset/src/loader_builders.rs +++ b/crates/bevy_asset/src/loader_builders.rs @@ -282,25 +282,15 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { .stats .started_load_tasks += 1; let (mut meta, loader, mut reader) = if let Some(reader) = reader { - let loader = if let Some(type_id) = type_id { - self.load_context - .asset_server - .get_asset_loader_with_asset_type_id(type_id) - .await - .map_err(|error| LoadDirectError::LoadError { - dependency: path.clone(), - error: Box::new(error.into()), - })? - } else { - self.load_context - .asset_server - .get_path_asset_loader(path) - .await - .map_err(|error| LoadDirectError::LoadError { - dependency: path.clone(), - error: Box::new(error.into()), - })? - }; + let loader = self + .load_context + .asset_server + .get_path_asset_loader(path) + .await + .map_err(|error| LoadDirectError::LoadError { + dependency: path.clone(), + error: Box::new(error.into()), + })?; let meta = loader.default_meta(); (meta, loader, ReaderRef::Borrowed(reader)) } else { diff --git a/crates/bevy_asset/src/server/loaders.rs b/crates/bevy_asset/src/server/loaders.rs index dcbb4edaf6198..44e04d045801d 100644 --- a/crates/bevy_asset/src/server/loaders.rs +++ b/crates/bevy_asset/src/server/loaders.rs @@ -6,15 +6,12 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use async_broadcast::RecvError; use bevy_platform::collections::HashMap; use bevy_tasks::IoTaskPool; -use bevy_utils::TypeIdHashMap; -use core::any::TypeId; use thiserror::Error; use tracing::warn; #[derive(Default)] pub(crate) struct AssetLoaders { loaders: Vec, - type_id_to_loaders: TypeIdHashMap>, extension_to_loaders: HashMap, Vec>, type_path_to_loader: HashMap<&'static str, usize>, type_path_to_preregistered_loader: HashMap<&'static str, usize>, @@ -29,9 +26,6 @@ impl AssetLoaders { /// Registers a new [`AssetLoader`]. [`AssetLoader`]s must be registered before they can be used. pub(crate) fn push(&mut self, loader: L) { let type_path = L::type_path(); - // TODO: Allow using the short path of loaders. - let loader_asset_type = TypeId::of::(); - let loader_asset_type_name = core::any::type_name::(); let loader = Arc::new(loader); @@ -43,7 +37,6 @@ impl AssetLoaders { }; if is_new { - let existing_loaders_for_type_id = self.type_id_to_loaders.get(&loader_asset_type); let mut duplicate_extensions = Vec::new(); for extension in AssetLoader::extensions(&*loader) { let list = self @@ -51,29 +44,18 @@ impl AssetLoaders { .entry((*extension).into()) .or_default(); - if !list.is_empty() - && let Some(existing_loaders_for_type_id) = existing_loaders_for_type_id - && list - .iter() - .any(|index| existing_loaders_for_type_id.contains(index)) - { + if !list.is_empty() { duplicate_extensions.push(extension); } list.push(loader_index); } if !duplicate_extensions.is_empty() { - warn!("Duplicate AssetLoader registered for Asset type `{loader_asset_type_name}` with extensions `{duplicate_extensions:?}`. \ - Loader must be specified in a .meta file in order to load assets of this type with these extensions."); + warn!("Duplicate AssetLoader registered for extensions `{duplicate_extensions:?}`. \ + Loader must be specified in a .meta file in order to load assets with these extensions."); } self.type_path_to_loader.insert(type_path, loader_index); - - self.type_id_to_loaders - .entry(loader_asset_type) - .or_default() - .push(loader_index); - self.loaders.push(MaybeAssetLoader::Ready(loader)); } else { let maybe_loader = core::mem::replace( @@ -98,8 +80,6 @@ impl AssetLoaders { /// Assets loaded with matching extensions will be blocked until the /// real loader is added. pub(crate) fn reserve(&mut self, extensions: &[&str]) { - let loader_asset_type = TypeId::of::(); - let loader_asset_type_name = core::any::type_name::(); let type_path = L::type_path(); // TODO: Allow using the short path of loaders. @@ -109,7 +89,6 @@ impl AssetLoaders { .insert(type_path, loader_index); self.type_path_to_loader.insert(type_path, loader_index); - let existing_loaders_for_type_id = self.type_id_to_loaders.get(&loader_asset_type); let mut duplicate_extensions = Vec::new(); for extension in extensions { let list = self @@ -117,27 +96,17 @@ impl AssetLoaders { .entry((*extension).into()) .or_default(); - if !list.is_empty() - && let Some(existing_loaders_for_type_id) = existing_loaders_for_type_id - && list - .iter() - .any(|index| existing_loaders_for_type_id.contains(index)) - { + if !list.is_empty() { duplicate_extensions.push(extension); } list.push(loader_index); } if !duplicate_extensions.is_empty() { - warn!("Duplicate AssetLoader preregistered for Asset type `{loader_asset_type_name}` with extensions `{duplicate_extensions:?}`. \ - Loader must be specified in a .meta file in order to load assets of this type with these extensions."); + warn!("Duplicate AssetLoader preregistered for extensions `{duplicate_extensions:?}`. \ + Loader must be specified in a .meta file in order to load assets with these extensions."); } - self.type_id_to_loaders - .entry(loader_asset_type) - .or_default() - .push(loader_index); - let (mut sender, receiver) = async_broadcast::broadcast(1); sender.set_overflow(true); self.loaders @@ -181,13 +150,6 @@ impl AssetLoaders { None } - /// Get the [`AssetLoader`] for a given asset type - pub(crate) fn get_by_type(&self, type_id: TypeId) -> Option { - let index = self.type_id_to_loaders.get(&type_id)?.last().copied()?; - - self.get_by_index(index) - } - /// Get the [`AssetLoader`] for a given extension pub(crate) fn get_by_extension(&self, extension: &str) -> Option { let index = self.extension_to_loaders.get(extension)?.last().copied()?; @@ -251,12 +213,6 @@ mod tests { #[derive(Asset, TypePath, Debug)] struct A; - #[derive(Asset, TypePath, Debug)] - struct B; - - #[derive(Asset, TypePath, Debug)] - struct C; - #[derive(TypePath)] struct Loader { sender: Sender<()>, @@ -339,74 +295,6 @@ mod tests { assert!(rx.try_recv().is_err()); } - /// Ensure that if multiple loaders have different types but no extensions, they can be found - #[test] - fn type_resolution() { - let mut loaders = AssetLoaders::default(); - - let (loader_a1, rx_a1) = Loader::::new(); - let (loader_b1, rx_b1) = Loader::::new(); - let (loader_c1, rx_c1) = Loader::::new(); - - loaders.push(loader_a1); - loaders.push(loader_b1); - loaders.push(loader_c1); - - assert!(rx_a1.try_recv().is_ok()); - assert!(rx_b1.try_recv().is_ok()); - assert!(rx_c1.try_recv().is_ok()); - - let loader = block_on(loaders.get_by_type(TypeId::of::()).unwrap().get()).unwrap(); - - loader.extensions(); - - assert!(rx_a1.try_recv().is_ok()); - assert!(rx_b1.try_recv().is_err()); - assert!(rx_c1.try_recv().is_err()); - - let loader = block_on(loaders.get_by_type(TypeId::of::()).unwrap().get()).unwrap(); - - loader.extensions(); - - assert!(rx_a1.try_recv().is_err()); - assert!(rx_b1.try_recv().is_ok()); - assert!(rx_c1.try_recv().is_err()); - - let loader = block_on(loaders.get_by_type(TypeId::of::()).unwrap().get()).unwrap(); - - loader.extensions(); - - assert!(rx_a1.try_recv().is_err()); - assert!(rx_b1.try_recv().is_err()); - assert!(rx_c1.try_recv().is_ok()); - } - - /// Ensure that the last loader added is selected - #[test] - fn type_resolution_shadow() { - let mut loaders = AssetLoaders::default(); - - let (loader_a1, rx_a1) = Loader::::new(); - let (loader_a2, rx_a2) = Loader::::new(); - let (loader_a3, rx_a3) = Loader::::new(); - - loaders.push(loader_a1); - loaders.push(loader_a2); - loaders.push(loader_a3); - - assert!(rx_a1.try_recv().is_ok()); - assert!(rx_a2.try_recv().is_ok()); - assert!(rx_a3.try_recv().is_ok()); - - let loader = block_on(loaders.get_by_type(TypeId::of::()).unwrap().get()).unwrap(); - - loader.extensions(); - - assert!(rx_a1.try_recv().is_err()); - assert!(rx_a2.try_recv().is_err()); - assert!(rx_a3.try_recv().is_ok()); - } - /// Ensure that if multiple loaders have like types but differing extensions, they can be found #[test] fn extension_resolution() { diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index c28121c029a11..762c3ea15e056 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -298,25 +298,6 @@ impl AssetServer { loader.get().await.map_err(|_| error()) } - /// Retrieves the default [`AssetLoader`] for the given [`Asset`] [`TypeId`], if one can be found. - pub async fn get_asset_loader_with_asset_type_id( - &self, - type_id: TypeId, - ) -> Result, MissingAssetLoaderForTypeIdError> { - let error = || MissingAssetLoaderForTypeIdError { type_id }; - - let loader = self.read_loaders().get_by_type(type_id).ok_or_else(error)?; - loader.get().await.map_err(|_| error()) - } - - /// Retrieves the default [`AssetLoader`] for the given [`Asset`] type, if one can be found. - pub async fn get_asset_loader_with_asset_type( - &self, - ) -> Result, MissingAssetLoaderForTypeIdError> { - self.get_asset_loader_with_asset_type_id(TypeId::of::()) - .await - } - /// Begins loading an [`Asset`] of type `A` stored at `path`. This will not block on the asset load. Instead, /// it returns a "strong" [`Handle`]. When the [`Asset`] is loaded (and enters [`LoadState::Loaded`]), it will be added to the /// associated [`Assets`] resource. From 81bd4c4d8a25b8002131ba8ced4b74a1b713c0f8 Mon Sep 17 00:00:00 2001 From: andriyDev Date: Sat, 22 Aug 2026 14:16:47 -0700 Subject: [PATCH 3/5] Add a migration guide. --- .../assets_not_loaded_by_type.md | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 _release-content/migration-guides/assets_not_loaded_by_type.md diff --git a/_release-content/migration-guides/assets_not_loaded_by_type.md b/_release-content/migration-guides/assets_not_loaded_by_type.md new file mode 100644 index 0000000000000..57b54dac2f8a8 --- /dev/null +++ b/_release-content/migration-guides/assets_not_loaded_by_type.md @@ -0,0 +1,53 @@ +--- +title: AssetLoaders are no longer chosen by the asset type. +pull_requests: [] +--- + +Previously, when picking which asset loader to use, the first step was looking up the asset loader +by the requested type. So if you loaded `asset_server.load::("blah.mp4")`, it would attempt +to load this `mp4` file with the `ImageLoader` (despite the fact that `mp4` is not a valid image +loader extension). This also lead to very complicated internal heuristics to deal with the fact that +a single asset file could be loaded as multiple different asset types at once. + +Now, the asset loader selection can only use the file extension. This means for any file path there +is an unambiguous default loader. + +This however breaks some uses. The most common is using a generic extension and then providing +asset loaders for those particular asset types. So for example, you may have files: + +``` +level1.ron +monster_snake.ron +``` + +Previously, if you had a `RonLoader` and `RonLoader`, these could be +loaded with `asset_server.load::("level1.ron")` and +`asset_server.load::("monster_snake.ron")` respectively, since the type of the `load` call +tells the asset system which loader to use. + +Now, these two would conflict (and we'd use whichever loader was registered last). To resolve this, +one thing to do is to give a unique extension. A good pattern is to add the type name as the +extension, for example: + +``` +level1.LevelDefinition.ron +monster_snake.Monster.ron +``` + +(don't forget to update the `extensions` method in your `AssetLoader`) + +Another approach is to use meta files. Meta files allow you to explicitly say which loader to use. +For example, we could write the following meta file at `level1.ron.meta`: + +``` +( + meta_format_version: "1.0", + asset: Load( + loader: "RonLoader", + settings: (), + ), +) +``` + +If you truly need to load one file with two loaders, come chat with us so we can better understand +your use-case! From 187e78a0f04aa553fa09e37d82e4badb6bb2b74f Mon Sep 17 00:00:00 2001 From: andriyDev Date: Wed, 2 Sep 2026 21:42:55 -0700 Subject: [PATCH 4/5] Remove type ID from any loader selection stuff. --- crates/bevy_asset/src/loader_builders.rs | 44 ++++-------------------- crates/bevy_asset/src/server/mod.rs | 22 +++--------- examples/asset/asset_decompression.rs | 2 +- 3 files changed, 12 insertions(+), 56 deletions(-) diff --git a/crates/bevy_asset/src/loader_builders.rs b/crates/bevy_asset/src/loader_builders.rs index 8b9d2dc784cb3..b0bf702758ee4 100644 --- a/crates/bevy_asset/src/loader_builders.rs +++ b/crates/bevy_asset/src/loader_builders.rs @@ -150,28 +150,14 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { .await } - /// Loads the provided path as the given type, returning the loaded data. + /// Loads the provided path, returning the loaded data. /// /// This load is async and therefore needs to be awaited before returning the loaded data. pub async fn load_erased_value<'a>( self, - type_id: TypeId, path: impl Into>, ) -> Result { - self.load_value_internal(Some(type_id), &path.into().into_owned(), None) - .await - .map(|(_, asset)| asset) - } - - /// Loads the provided path with an unknown type (which is guessed based on the path or meta - /// file), returning the loaded data. - /// - /// This load is async and therefore needs to be awaited before returning the loaded data. - pub async fn load_untyped_value<'a>( - self, - path: impl Into>, - ) -> Result { - self.load_value_internal(None, &path.into().into_owned(), None) + self.load_value_internal(&path.into().into_owned(), None) .await .map(|(_, asset)| asset) } @@ -190,34 +176,17 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { .await } - /// Loads the given type from the given `reader`, returning the loaded data. + /// Loads from the given `reader`, returning the loaded data. /// /// This load is async and therefore needs to be awaited before returning the loaded data. The /// provided path determines the path used for handles of subassets, as well as any relative /// paths of assets used by the nested loader. pub async fn load_erased_value_from_reader<'a>( - self, - type_id: TypeId, - path: impl Into>, - reader: &'builder mut dyn Reader, - ) -> Result { - self.load_value_internal(Some(type_id), &path.into().into_owned(), Some(reader)) - .await - .map(|(_, asset)| asset) - } - - /// Loads an asset from the given `reader` with an unknown type (which is guessed based on the - /// path or meta file), returning the loaded data. - /// - /// This load is async and therefore needs to be awaited before returning the loaded data. The - /// provided path determines the path used for handles of subassets, as well as any relative - /// paths of assets used by the nested loader. - pub async fn load_untyped_value_from_reader<'a>( self, path: impl Into>, reader: &'builder mut dyn Reader, ) -> Result { - self.load_value_internal(None, &path.into().into_owned(), Some(reader)) + self.load_value_internal(&path.into().into_owned(), Some(reader)) .await .map(|(_, asset)| asset) } @@ -265,7 +234,6 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { /// `path`. async fn load_value_internal( self, - type_id: Option, path: &AssetPath<'static>, reader: Option<&'builder mut dyn Reader>, ) -> Result<(Arc, ErasedLoadedAsset), LoadDirectError> { @@ -297,7 +265,7 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { let (meta, loader, reader) = self .load_context .asset_server - .get_meta_loader_and_reader(path, type_id) + .get_meta_loader_and_reader(path) .await .map_err(|error| LoadDirectError::LoadError { dependency: path.clone(), @@ -330,7 +298,7 @@ impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> { path: AssetPath<'static>, reader: Option<&'builder mut dyn Reader>, ) -> Result, LoadDirectError> { - self.load_value_internal(Some(TypeId::of::()), &path, reader) + self.load_value_internal(&path, reader) .await .and_then(move |(loader, untyped_asset)| { untyped_asset diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 762c3ea15e056..6d663dd5df571 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -525,12 +525,10 @@ impl AssetServer { force: bool, meta_transform: Option, ) -> Result, AssetLoadError> { - let input_handle_type_id = input_handle.as_ref().map(UntypedHandle::type_id); - let path = path.into_owned(); let path_clone = path.clone(); let (mut meta, loader, mut reader) = self - .get_meta_loader_and_reader(&path_clone, input_handle_type_id) + .get_meta_loader_and_reader(&path_clone) .await .inspect_err(|e| { // if there was an input handle, a "load" operation has already started, so we must produce a "failure" event, if @@ -1360,7 +1358,6 @@ impl AssetServer { pub(crate) async fn get_meta_loader_and_reader<'a>( &'a self, asset_path: &'a AssetPath<'_>, - asset_type_id: Option, ) -> Result< ( Box, @@ -1430,10 +1427,7 @@ impl AssetServer { // TODO: Handle error transformation let loader = { self.read_loaders().find(asset_path) }; - let error = || AssetLoadError::MissingAssetLoader { - asset_type_id, - asset_path: asset_path.to_string(), - }; + let error = || AssetLoadError::MissingAssetLoader(asset_path.to_string()); let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?; @@ -1445,10 +1439,7 @@ impl AssetServer { } else { let loader = { self.read_loaders().find(asset_path) }; - let error = || AssetLoadError::MissingAssetLoader { - asset_type_id, - asset_path: asset_path.to_string(), - }; + let error = || AssetLoadError::MissingAssetLoader(asset_path.to_string()); let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?; @@ -2223,11 +2214,8 @@ pub enum AssetLoadError { UnapprovedPath(AssetPath<'static>), #[error(transparent)] RequestedHandleTypeMismatch(#[from] Box), - #[error("Could not find an asset loader matching: Asset Type: {asset_type_id:?}; Path: {asset_path:?};")] - MissingAssetLoader { - asset_type_id: Option, - asset_path: String, - }, + #[error("Could not find an asset loader matching path: \"{0}\"")] + MissingAssetLoader(String), #[error(transparent)] MissingAssetLoaderForExtension(#[from] MissingAssetLoaderForExtensionError), #[error(transparent)] diff --git a/examples/asset/asset_decompression.rs b/examples/asset/asset_decompression.rs index 9f2e177c76225..3dffe6f70cd27 100644 --- a/examples/asset/asset_decompression.rs +++ b/examples/asset/asset_decompression.rs @@ -76,7 +76,7 @@ impl AssetLoader for GzAssetLoader { let uncompressed = load_context .load_builder() - .load_untyped_value_from_reader(contained_path, &mut reader) + .load_erased_value_from_reader(contained_path, &mut reader) .await?; Ok(GzAsset { uncompressed }) From f910d9fd7e9b70bea8d85b2ff131636f55008208 Mon Sep 17 00:00:00 2001 From: andriyDev Date: Wed, 2 Sep 2026 22:30:03 -0700 Subject: [PATCH 5/5] Fix bevy_scene tests. --- crates/bevy_scene/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index b7721bc9cccec..b421031c1997d 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -3540,5 +3540,9 @@ mod tests { ) -> Result { Ok(ScenePatch::load_with(load_context, (self.0)())) } + + fn extensions(&self) -> &[&str] { + &["bsn"] + } } }