diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index e4678e4c25b28..60885920c9bcd 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -45,10 +45,12 @@ use bevy_app::{App, First, MainScheduleOrder, Plugin, PostUpdate}; use bevy_ecs::{ reflect::AppTypeRegistry, schedule::{IntoSystemConfigs, IntoSystemSetConfigs, ScheduleLabel, SystemSet}, + system::Resource, world::FromWorld, }; use bevy_log::error; use bevy_reflect::{FromReflect, GetTypeRegistration, Reflect, TypePath}; +use bevy_utils::HashSet; use std::{any::TypeId, sync::Arc}; /// Provides "asset" loading and processing functionality. An [`Asset`] is a "runtime value" that is loaded from an [`AssetSource`], @@ -97,6 +99,20 @@ pub enum AssetMode { Processed, } +/// Configures how / if meta files will be checked. If an asset's meta file is not checked, the default meta for the asset +/// will be used. +// TODO: To avoid breaking Bevy 0.12 users in 0.12.1, this is a Resource. In Bevy 0.13 this should be changed to a field on AssetPlugin (if it is still needed). +#[derive(Debug, Default, Clone, Resource)] +pub enum AssetMetaCheck { + /// Always check if assets have meta files. If the meta does not exist, the default meta will be used. + #[default] + Always, + /// Only look up meta files for the provided paths. The default meta will be used for any paths not contained in this set. + Paths(HashSet>), + /// Never check if assets have meta files and always use the default meta. If meta files exist, they will be ignored and the default meta will be used. + Never, +} + impl Default for AssetPlugin { fn default() -> Self { Self { @@ -139,9 +155,16 @@ impl Plugin for AssetPlugin { AssetMode::Unprocessed => { let mut builders = app.world.resource_mut::(); let sources = builders.build_sources(watch, false); - app.insert_resource(AssetServer::new( + let meta_check = app + .world + .get_resource::() + .cloned() + .unwrap_or_else(AssetMetaCheck::default); + + app.insert_resource(AssetServer::new_with_meta_check( sources, AssetServerMode::Unprocessed, + meta_check, watch, )); } @@ -157,6 +180,7 @@ impl Plugin for AssetPlugin { sources, processor.server().data.loaders.clone(), AssetServerMode::Processed, + AssetMetaCheck::Always, watch, )) .insert_resource(processor) @@ -166,9 +190,10 @@ impl Plugin for AssetPlugin { { let mut builders = app.world.resource_mut::(); let sources = builders.build_sources(false, watch); - app.insert_resource(AssetServer::new( + app.insert_resource(AssetServer::new_with_meta_check( sources, AssetServerMode::Processed, + AssetMetaCheck::Always, watch, )); } diff --git a/crates/bevy_asset/src/processor/mod.rs b/crates/bevy_asset/src/processor/mod.rs index e0fbcf9cc5ab5..284b7ab6a1bec 100644 --- a/crates/bevy_asset/src/processor/mod.rs +++ b/crates/bevy_asset/src/processor/mod.rs @@ -13,7 +13,7 @@ use crate::{ get_asset_hash, get_full_asset_hash, AssetAction, AssetActionMinimal, AssetHash, AssetMeta, AssetMetaDyn, AssetMetaMinimal, ProcessedInfo, ProcessedInfoMinimal, }, - AssetLoadError, AssetPath, AssetServer, AssetServerMode, DeserializeMetaError, + AssetLoadError, AssetMetaCheck, AssetPath, AssetServer, AssetServerMode, DeserializeMetaError, MissingAssetLoaderForExtensionError, }; use bevy_ecs::prelude::*; @@ -72,7 +72,12 @@ impl AssetProcessor { // The asset processor uses its own asset server with its own id space let mut sources = source.build_sources(false, false); sources.gate_on_processor(data.clone()); - let server = AssetServer::new(sources, AssetServerMode::Processed, false); + let server = AssetServer::new_with_meta_check( + sources, + AssetServerMode::Processed, + AssetMetaCheck::Always, + false, + ); Self { server, data } } diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 9ec46439e81e6..b8bac7f7b2b0f 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -12,7 +12,7 @@ use crate::{ MetaTransform, Settings, }, path::AssetPath, - Asset, AssetEvent, AssetHandleProvider, AssetId, Assets, DeserializeMetaError, + Asset, AssetEvent, AssetHandleProvider, AssetId, AssetMetaCheck, Assets, DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UntypedAssetId, UntypedHandle, }; use bevy_ecs::prelude::*; @@ -54,6 +54,7 @@ pub(crate) struct AssetServerData { asset_event_receiver: Receiver, sources: AssetSources, mode: AssetServerMode, + meta_check: AssetMetaCheck, } /// The "asset mode" the server is currently in. @@ -69,13 +70,37 @@ impl AssetServer { /// Create a new instance of [`AssetServer`]. If `watch_for_changes` is true, the [`AssetReader`] storage will watch for changes to /// asset sources and hot-reload them. pub fn new(sources: AssetSources, mode: AssetServerMode, watching_for_changes: bool) -> Self { - Self::new_with_loaders(sources, Default::default(), mode, watching_for_changes) + Self::new_with_loaders( + sources, + Default::default(), + mode, + AssetMetaCheck::Always, + watching_for_changes, + ) + } + + /// Create a new instance of [`AssetServer`]. If `watch_for_changes` is true, the [`AssetReader`] storage will watch for changes to + /// asset sources and hot-reload them. + pub fn new_with_meta_check( + sources: AssetSources, + mode: AssetServerMode, + meta_check: AssetMetaCheck, + watching_for_changes: bool, + ) -> Self { + Self::new_with_loaders( + sources, + Default::default(), + mode, + meta_check, + watching_for_changes, + ) } pub(crate) fn new_with_loaders( sources: AssetSources, loaders: Arc>, mode: AssetServerMode, + meta_check: AssetMetaCheck, watching_for_changes: bool, ) -> Self { let (asset_event_sender, asset_event_receiver) = crossbeam_channel::unbounded(); @@ -85,6 +110,7 @@ impl AssetServer { data: Arc::new(AssetServerData { sources, mode, + meta_check, asset_event_sender, asset_event_receiver, loaders, @@ -811,44 +837,57 @@ impl AssetServer { AssetServerMode::Processed { .. } => source.processed_reader()?, }; let reader = asset_reader.read(asset_path.path()).await?; - match asset_reader.read_meta_bytes(asset_path.path()).await { - Ok(meta_bytes) => { - // TODO: this isn't fully minimal yet. we only need the loader - let minimal: AssetMetaMinimal = ron::de::from_bytes(&meta_bytes).map_err(|e| { - AssetLoadError::DeserializeMeta { - path: asset_path.clone_owned(), - error: Box::new(DeserializeMetaError::DeserializeMinimal(e)), - } - })?; - let loader_name = match minimal.asset { - AssetActionMinimal::Load { loader } => loader, - AssetActionMinimal::Process { .. } => { - return Err(AssetLoadError::CannotLoadProcessedAsset { - path: asset_path.clone_owned(), - }) - } - AssetActionMinimal::Ignore => { - return Err(AssetLoadError::CannotLoadIgnoredAsset { + let read_meta = match &self.data.meta_check { + AssetMetaCheck::Always => true, + AssetMetaCheck::Paths(paths) => paths.contains(asset_path), + AssetMetaCheck::Never => false, + }; + + if read_meta { + match asset_reader.read_meta_bytes(asset_path.path()).await { + Ok(meta_bytes) => { + // TODO: this isn't fully minimal yet. we only need the loader + let minimal: AssetMetaMinimal = + ron::de::from_bytes(&meta_bytes).map_err(|e| { + AssetLoadError::DeserializeMeta { + path: asset_path.clone_owned(), + error: Box::new(DeserializeMetaError::DeserializeMinimal(e)), + } + })?; + let loader_name = match minimal.asset { + AssetActionMinimal::Load { loader } => loader, + AssetActionMinimal::Process { .. } => { + return Err(AssetLoadError::CannotLoadProcessedAsset { + path: asset_path.clone_owned(), + }) + } + AssetActionMinimal::Ignore => { + return Err(AssetLoadError::CannotLoadIgnoredAsset { + path: asset_path.clone_owned(), + }) + } + }; + let loader = self.get_asset_loader_with_type_name(&loader_name).await?; + let meta = loader.deserialize_meta(&meta_bytes).map_err(|e| { + AssetLoadError::DeserializeMeta { path: asset_path.clone_owned(), - }) - } - }; - let loader = self.get_asset_loader_with_type_name(&loader_name).await?; - let meta = loader.deserialize_meta(&meta_bytes).map_err(|e| { - AssetLoadError::DeserializeMeta { - path: asset_path.clone_owned(), - error: Box::new(e), - } - })?; + error: Box::new(e), + } + })?; - Ok((meta, loader, reader)) - } - Err(AssetReaderError::NotFound(_)) => { - let loader = self.get_path_asset_loader(asset_path).await?; - let meta = loader.default_meta(); - Ok((meta, loader, reader)) + Ok((meta, loader, reader)) + } + Err(AssetReaderError::NotFound(_)) => { + let loader = self.get_path_asset_loader(asset_path).await?; + let meta = loader.default_meta(); + Ok((meta, loader, reader)) + } + Err(err) => Err(err.into()), } - Err(err) => Err(err.into()), + } else { + let loader = self.get_path_asset_loader(asset_path).await?; + let meta = loader.default_meta(); + Ok((meta, loader, reader)) } }