Error info has been added to LoadState::Failed (#12709)
# Objective Fixes #12667. ## Solution - Stored [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html) inside of [LoadState::Failed](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) as a Box<AssetLoadError> to avoid bloating the size of all variants of LoadState. - Fixed dependent code ## Migration guide Added [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html) to [LoadState::Failed](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) option Removed `Copy`, `Ord` and `PartialOrd` implementations for [LoadState](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html) enum Added `Eq` and `PartialEq` implementations for [MissingAssetSourceError](https://docs.rs/bevy/latest/bevy/asset/io/struct.MissingAssetSourceError.html), [MissingProcessedAssetReaderError](https://docs.rs/bevy/latest/bevy/asset/io/struct.MissingProcessedAssetReaderError.html), [DeserializeMetaError](https://docs.rs/bevy/latest/bevy/asset/enum.DeserializeMetaError.html), [LoadState](https://docs.rs/bevy/latest/bevy/asset/enum.LoadState.html), [AssetLoadError](https://docs.rs/bevy/latest/bevy/asset/enum.AssetLoadError.html), [MissingAssetLoaderForTypeNameError](https://docs.rs/bevy/latest/bevy/asset/struct.MissingAssetLoaderForTypeNameError.html) and [MissingAssetLoaderForTypeIdError](https://docs.rs/bevy/latest/bevy/asset/struct.MissingAssetLoaderForTypeIdError.html)
This commit is contained in:
		
							parent
							
								
									4ca8cf5d66
								
							
						
					
					
						commit
						4da4493449
					
				| @ -51,6 +51,21 @@ pub enum AssetReaderError { | ||||
|     HttpError(u16), | ||||
| } | ||||
| 
 | ||||
| impl PartialEq for AssetReaderError { | ||||
|     /// Equality comparison for `AssetReaderError::Io` is not full (only through `ErrorKind` of inner error)
 | ||||
|     #[inline] | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         match (self, other) { | ||||
|             (Self::NotFound(path), Self::NotFound(other_path)) => path == other_path, | ||||
|             (Self::Io(error), Self::Io(other_error)) => error.kind() == other_error.kind(), | ||||
|             (Self::HttpError(code), Self::HttpError(other_code)) => code == other_code, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Eq for AssetReaderError {} | ||||
| 
 | ||||
| impl From<std::io::Error> for AssetReaderError { | ||||
|     fn from(value: std::io::Error) -> Self { | ||||
|         Self::Io(Arc::new(value)) | ||||
|  | ||||
| @ -584,7 +584,7 @@ impl AssetSources { | ||||
| } | ||||
| 
 | ||||
| /// An error returned when an [`AssetSource`] does not exist for a given id.
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| #[error("Asset Source '{0}' does not exist")] | ||||
| pub struct MissingAssetSourceError(AssetSourceId<'static>); | ||||
| 
 | ||||
| @ -594,7 +594,7 @@ pub struct MissingAssetSourceError(AssetSourceId<'static>); | ||||
| pub struct MissingAssetWriterError(AssetSourceId<'static>); | ||||
| 
 | ||||
| /// An error returned when a processed [`AssetReader`] does not exist for a given id.
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| #[error("Asset Source '{0}' does not have a processed AssetReader.")] | ||||
| pub struct MissingProcessedAssetReaderError(AssetSourceId<'static>); | ||||
| 
 | ||||
|  | ||||
| @ -1071,13 +1071,13 @@ mod tests { | ||||
|             let d_id = c_text.dependencies[0].id(); | ||||
|             let d_text = get::<CoolText>(world, d_id); | ||||
|             let (d_load, d_deps, d_rec_deps) = asset_server.get_load_states(d_id).unwrap(); | ||||
|             if d_load != LoadState::Failed { | ||||
|             if !matches!(d_load, LoadState::Failed(_)) { | ||||
|                 // wait until d has exited the loading state
 | ||||
|                 return None; | ||||
|             } | ||||
| 
 | ||||
|             assert!(d_text.is_none()); | ||||
|             assert_eq!(d_load, LoadState::Failed); | ||||
|             assert!(matches!(d_load, LoadState::Failed(_))); | ||||
|             assert_eq!(d_deps, DependencyLoadState::Failed); | ||||
|             assert_eq!(d_rec_deps, RecursiveDependencyLoadState::Failed); | ||||
| 
 | ||||
| @ -1384,7 +1384,7 @@ mod tests { | ||||
|             // Check what just failed
 | ||||
|             for error in errors.read() { | ||||
|                 let (load_state, _, _) = server.get_load_states(error.id).unwrap(); | ||||
|                 assert_eq!(load_state, LoadState::Failed); | ||||
|                 assert!(matches!(load_state, LoadState::Failed(_))); | ||||
|                 assert_eq!(*error.path.source(), AssetSourceId::Name("unstable".into())); | ||||
|                 match &error.error { | ||||
|                     AssetLoadError::AssetReaderError(read_error) => match read_error { | ||||
|  | ||||
| @ -257,7 +257,7 @@ pub struct LoadDirectError { | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs while deserializing [`AssetMeta`].
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| pub enum DeserializeMetaError { | ||||
|     #[error("Failed to deserialize asset meta: {0:?}")] | ||||
|     DeserializeSettings(#[from] SpannedError), | ||||
|  | ||||
| @ -1203,10 +1203,8 @@ impl ProcessorAssetInfos { | ||||
|             Err(err) => { | ||||
|                 error!("Failed to process asset {asset_path}: {err}"); | ||||
|                 // if this failed because a dependency could not be loaded, make sure it is reprocessed if that dependency is reprocessed
 | ||||
|                 if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError { | ||||
|                     path: dependency, | ||||
|                     .. | ||||
|                 }) = err | ||||
|                 if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError(dependency)) = | ||||
|                     err | ||||
|                 { | ||||
|                     let info = self.get_mut(&asset_path).expect("info should exist"); | ||||
|                     info.processed_info = Some(ProcessedInfo { | ||||
| @ -1214,7 +1212,7 @@ impl ProcessorAssetInfos { | ||||
|                         full_hash: AssetHash::default(), | ||||
|                         process_dependencies: vec![], | ||||
|                     }); | ||||
|                     self.add_dependant(&dependency, asset_path.to_owned()); | ||||
|                     self.add_dependant(dependency.path(), asset_path.to_owned()); | ||||
|                 } | ||||
| 
 | ||||
|                 let info = self.get_mut(&asset_path).expect("info should exist"); | ||||
|  | ||||
| @ -212,8 +212,7 @@ impl AssetInfos { | ||||
|                 let mut should_load = false; | ||||
|                 if loading_mode == HandleLoadingMode::Force | ||||
|                     || (loading_mode == HandleLoadingMode::Request | ||||
|                         && (info.load_state == LoadState::NotLoaded | ||||
|                             || info.load_state == LoadState::Failed)) | ||||
|                         && matches!(info.load_state, LoadState::NotLoaded | LoadState::Failed(_))) | ||||
|                 { | ||||
|                     info.load_state = LoadState::Loading; | ||||
|                     info.dep_load_state = DependencyLoadState::Loading; | ||||
| @ -412,7 +411,7 @@ impl AssetInfos { | ||||
|                         // If dependency is loaded, reduce our count by one
 | ||||
|                         false | ||||
|                     } | ||||
|                     LoadState::Failed => { | ||||
|                     LoadState::Failed(_) => { | ||||
|                         failed_deps.insert(*dep_id); | ||||
|                         false | ||||
|                     } | ||||
| @ -582,12 +581,12 @@ impl AssetInfos { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) fn process_asset_fail(&mut self, failed_id: UntypedAssetId) { | ||||
|     pub(crate) fn process_asset_fail(&mut self, failed_id: UntypedAssetId, error: AssetLoadError) { | ||||
|         let (dependants_waiting_on_load, dependants_waiting_on_rec_load) = { | ||||
|             let info = self | ||||
|                 .get_mut(failed_id) | ||||
|                 .expect("Asset info should always exist at this point"); | ||||
|             info.load_state = LoadState::Failed; | ||||
|             info.load_state = LoadState::Failed(Box::new(error)); | ||||
|             info.dep_load_state = DependencyLoadState::Failed; | ||||
|             info.rec_dep_load_state = RecursiveDependencyLoadState::Failed; | ||||
|             ( | ||||
|  | ||||
| @ -26,7 +26,7 @@ use futures_lite::StreamExt; | ||||
| use info::*; | ||||
| use loaders::*; | ||||
| use parking_lot::RwLock; | ||||
| use std::path::PathBuf; | ||||
| use std::{any::Any, path::PathBuf}; | ||||
| use std::{any::TypeId, path::Path, sync::Arc}; | ||||
| use thiserror::Error; | ||||
| 
 | ||||
| @ -754,7 +754,7 @@ impl AssetServer { | ||||
|             .infos | ||||
|             .read() | ||||
|             .get(id.into()) | ||||
|             .map(|i| (i.load_state, i.dep_load_state, i.rec_dep_load_state)) | ||||
|             .map(|i| (i.load_state.clone(), i.dep_load_state, i.rec_dep_load_state)) | ||||
|     } | ||||
| 
 | ||||
|     /// Retrieves the main [`LoadState`] of a given asset `id`.
 | ||||
| @ -762,7 +762,11 @@ impl AssetServer { | ||||
|     /// Note that this is "just" the root asset load state. To check if an asset _and_ its recursive
 | ||||
|     /// dependencies have loaded, see [`AssetServer::is_loaded_with_dependencies`].
 | ||||
|     pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState> { | ||||
|         self.data.infos.read().get(id.into()).map(|i| i.load_state) | ||||
|         self.data | ||||
|             .infos | ||||
|             .read() | ||||
|             .get(id.into()) | ||||
|             .map(|i| i.load_state.clone()) | ||||
|     } | ||||
| 
 | ||||
|     /// Retrieves the [`RecursiveDependencyLoadState`] of a given asset `id`.
 | ||||
| @ -1041,11 +1045,11 @@ impl AssetServer { | ||||
|         let load_context = | ||||
|             LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes); | ||||
|         loader.load(reader, meta, load_context).await.map_err(|e| { | ||||
|             AssetLoadError::AssetLoaderError { | ||||
|             AssetLoadError::AssetLoaderError(AssetLoaderError { | ||||
|                 path: asset_path.clone_owned(), | ||||
|                 loader_name: loader.type_name(), | ||||
|                 error: e.into(), | ||||
|             } | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @ -1073,7 +1077,7 @@ pub fn handle_internal_asset_events(world: &mut World) { | ||||
|                     sender(world, id); | ||||
|                 } | ||||
|                 InternalAssetEvent::Failed { id, path, error } => { | ||||
|                     infos.process_asset_fail(id); | ||||
|                     infos.process_asset_fail(id, error.clone()); | ||||
| 
 | ||||
|                     // Send untyped failure event
 | ||||
|                     untyped_failures.push(UntypedAssetLoadFailedEvent { | ||||
| @ -1190,7 +1194,7 @@ pub(crate) enum InternalAssetEvent { | ||||
| } | ||||
| 
 | ||||
| /// The load state of an asset.
 | ||||
| #[derive(Component, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] | ||||
| #[derive(Component, Clone, Debug, PartialEq, Eq)] | ||||
| pub enum LoadState { | ||||
|     /// The asset has not started loading yet
 | ||||
|     NotLoaded, | ||||
| @ -1199,7 +1203,7 @@ pub enum LoadState { | ||||
|     /// The asset has been loaded and has been added to the [`World`]
 | ||||
|     Loaded, | ||||
|     /// The asset failed to load.
 | ||||
|     Failed, | ||||
|     Failed(Box<AssetLoadError>), | ||||
| } | ||||
| 
 | ||||
| /// The load state of an asset's dependencies.
 | ||||
| @ -1229,7 +1233,7 @@ pub enum RecursiveDependencyLoadState { | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs during an [`Asset`] load.
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| pub enum AssetLoadError { | ||||
|     #[error("Requested handle of type {requested:?} for asset '{path}' does not match actual asset type '{actual_asset_name}', which used loader '{loader_name}'")] | ||||
|     RequestedHandleTypeMismatch { | ||||
| @ -1268,12 +1272,8 @@ pub enum AssetLoadError { | ||||
|     CannotLoadProcessedAsset { path: AssetPath<'static> }, | ||||
|     #[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")] | ||||
|     CannotLoadIgnoredAsset { path: AssetPath<'static> }, | ||||
|     #[error("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")] | ||||
|     AssetLoaderError { | ||||
|         path: AssetPath<'static>, | ||||
|         loader_name: &'static str, | ||||
|         error: Arc<dyn std::error::Error + Send + Sync + 'static>, | ||||
|     }, | ||||
|     #[error(transparent)] | ||||
|     AssetLoaderError(#[from] AssetLoaderError), | ||||
|     #[error("The file at '{}' does not contain the labeled asset '{}'; it contains the following {} assets: {}",
 | ||||
|             base_path, | ||||
|             label, | ||||
| @ -1286,22 +1286,48 @@ pub enum AssetLoadError { | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[error("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")] | ||||
| pub struct AssetLoaderError { | ||||
|     path: AssetPath<'static>, | ||||
|     loader_name: &'static str, | ||||
|     error: Arc<dyn std::error::Error + Send + Sync + 'static>, | ||||
| } | ||||
| 
 | ||||
| impl PartialEq for AssetLoaderError { | ||||
|     /// Equality comparison for `AssetLoaderError::error` is not full (only through `TypeId`)
 | ||||
|     #[inline] | ||||
|     fn eq(&self, other: &Self) -> bool { | ||||
|         self.path == other.path | ||||
|             && self.loader_name == other.loader_name | ||||
|             && self.error.type_id() == other.error.type_id() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Eq for AssetLoaderError {} | ||||
| 
 | ||||
| impl AssetLoaderError { | ||||
|     pub fn path(&self) -> &AssetPath<'static> { | ||||
|         &self.path | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
 | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| #[error("no `AssetLoader` found{}", format_missing_asset_ext(.extensions))] | ||||
| pub struct MissingAssetLoaderForExtensionError { | ||||
|     extensions: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given [`std::any::type_name`].
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| #[error("no `AssetLoader` found with the name '{type_name}'")] | ||||
| pub struct MissingAssetLoaderForTypeNameError { | ||||
|     type_name: String, | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs when an [`AssetLoader`] is not registered for a given [`Asset`] [`TypeId`].
 | ||||
| #[derive(Error, Debug, Clone)] | ||||
| #[derive(Error, Debug, Clone, PartialEq, Eq)] | ||||
| #[error("no `AssetLoader` found with the ID '{type_id:?}'")] | ||||
| pub struct MissingAssetLoaderForTypeIdError { | ||||
|     pub type_id: TypeId, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Vitaliy Sapronenko
						Vitaliy Sapronenko