Update assets_manager to 0.7

This commit is contained in:
Benoît du Garreau 2021-12-12 20:01:52 +01:00
parent 182d5cf971
commit aca088388b
27 changed files with 223 additions and 227 deletions

8
Cargo.lock generated
View File

@ -214,10 +214,11 @@ dependencies = [
[[package]] [[package]]
name = "assets_manager" name = "assets_manager"
version = "0.6.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35c55040a3eee1353bfa4bee2c03a695395d64176e8063223aa62d11b4ef669c" checksum = "912acc83cc5bf31b0d9685e8d11a9d7c36b949e850ca2db1a3203b3b656a5e4a"
dependencies = [ dependencies = [
"ab_glyph",
"ahash 0.7.6", "ahash 0.7.6",
"bincode", "bincode",
"crossbeam-channel", "crossbeam-channel",
@ -6081,7 +6082,7 @@ dependencies = [
"petgraph 0.5.1", "petgraph 0.5.1",
"rand 0.8.4", "rand 0.8.4",
"rayon", "rayon",
"ron 0.6.6", "ron 0.7.0",
"roots", "roots",
"serde", "serde",
"serde_repr", "serde_repr",
@ -6358,6 +6359,7 @@ dependencies = [
name = "veloren-voxygen" name = "veloren-voxygen"
version = "0.11.0" version = "0.11.0"
dependencies = [ dependencies = [
"assets_manager",
"backtrace", "backtrace",
"bincode", "bincode",
"bytemuck", "bytemuck",

View File

@ -58,7 +58,7 @@ dot_vox = "4.0"
serde_repr = "0.1.6" serde_repr = "0.1.6"
# csv import # csv import
ron = { version = "0.6", default-features = false, optional = true } ron = { version = "0.7", default-features = false, optional = true }
# csv export # csv export
csv = { version = "1.1.3", optional = true } csv = { version = "1.1.3", optional = true }
structopt = { version = "0.3.13", optional = true } structopt = { version = "0.3.13", optional = true }

View File

@ -7,7 +7,7 @@ version = "0.10.0"
[dependencies] [dependencies]
lazy_static = "1.4.0" lazy_static = "1.4.0"
assets_manager = {version = "0.6.0", features = ["bincode", "ron", "json"]} assets_manager = {version = "0.7", features = ["bincode", "ron", "json"]}
ron = { version = "0.7", default-features = false } ron = { version = "0.7", default-features = false }
dot_vox = "4.0" dot_vox = "4.0"
image = { version = "0.23.12", default-features = false, features = ["png"] } image = { version = "0.23.12", default-features = false, features = ["png"] }

91
common/assets/src/fs.rs Normal file
View File

@ -0,0 +1,91 @@
use std::{borrow::Cow, io, path::PathBuf};
use assets_manager::{
hot_reloading::{DynUpdateSender, EventSender, FsWatcherBuilder},
source::{DirEntry, FileSystem as RawFs, Source},
BoxedError,
};
/// Loads assets from the default path or `VELOREN_ASSETS_OVERRIDE` env if it is
/// set.
#[derive(Debug, Clone)]
pub struct FileSystem {
default: RawFs,
override_dir: Option<RawFs>,
}
impl FileSystem {
pub fn new() -> io::Result<Self> {
let default = RawFs::new(&*super::ASSETS_PATH)?;
let override_dir = std::env::var_os("VELOREN_ASSETS_OVERRIDE").and_then(|path| {
RawFs::new(path)
.map_err(|err| tracing::error!("Error setting override assets directory: {}", err))
.ok()
});
Ok(Self {
default,
override_dir,
})
}
pub fn path_of(&self, specifier: &str, ext: &str) -> PathBuf {
self.default.path_of(DirEntry::File(specifier, ext))
}
}
impl Source for FileSystem {
fn read(&self, id: &str, ext: &str) -> io::Result<Cow<[u8]>> {
if let Some(dir) = &self.override_dir {
match dir.read(id, ext) {
Ok(content) => return Ok(content),
Err(err) => {
if err.kind() != io::ErrorKind::NotFound {
let path = dir.path_of(DirEntry::File(id, ext));
tracing::warn!("Error reading \"{}\": {}", path.display(), err);
}
},
}
}
// If not found in override path, try load from main asset path
self.default.read(id, ext)
}
fn read_dir(&self, id: &str, f: &mut dyn FnMut(DirEntry)) -> io::Result<()> {
if let Some(dir) = &self.override_dir {
match dir.read_dir(id, f) {
Ok(()) => return Ok(()),
Err(err) => {
if err.kind() != io::ErrorKind::NotFound {
let path = dir.path_of(DirEntry::Directory(id));
tracing::warn!("Error reading \"{}\": {}", path.display(), err);
}
},
}
}
// If not found in override path, try load from main asset path
self.default.read_dir(id, f)
}
fn exists(&self, entry: DirEntry) -> bool {
self.override_dir
.as_ref()
.map_or(false, |dir| dir.exists(entry))
|| self.default.exists(entry)
}
fn make_source(&self) -> Option<Box<dyn Source + Send>> { Some(Box::new(self.clone())) }
fn configure_hot_reloading(&self, events: EventSender) -> Result<DynUpdateSender, BoxedError> {
let mut builder = FsWatcherBuilder::new()?;
if let Some(dir) = &self.override_dir {
builder.watch(dir.root().to_owned())?;
}
builder.watch(self.default.root().to_owned())?;
Ok(builder.build(events))
}
}

View File

@ -4,7 +4,7 @@
use dot_vox::DotVoxData; use dot_vox::DotVoxData;
use image::DynamicImage; use image::DynamicImage;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{borrow::Cow, fmt, path::PathBuf, sync::Arc}; use std::{borrow::Cow, path::PathBuf, sync::Arc};
pub use assets_manager::{ pub use assets_manager::{
asset::{DirLoadable, Ron}, asset::{DirLoadable, Ron},
@ -15,29 +15,21 @@ pub use assets_manager::{
Asset, AssetCache, BoxedError, Compound, Error, SharedString, Asset, AssetCache, BoxedError, Compound, Error, SharedString,
}; };
mod fs;
lazy_static! { lazy_static! {
/// The HashMap where all loaded assets are stored in. /// The HashMap where all loaded assets are stored in.
static ref ASSETS: AssetCache = static ref ASSETS: AssetCache<fs::FileSystem> =
AssetCache::new(&*ASSETS_PATH).unwrap(); AssetCache::with_source(fs::FileSystem::new().unwrap());
/// Asset cache for overrides
static ref ASSETS_OVERRIDE: Option<AssetCache> = {
std::env::var("VELOREN_ASSETS_OVERRIDE").ok().map(|path| {
AssetCache::new(path).unwrap()
})
};
} }
#[cfg(feature = "hot-reloading")] #[cfg(feature = "hot-reloading")]
pub fn start_hot_reloading() { pub fn start_hot_reloading() { ASSETS.enhance_hot_reloading(); }
ASSETS.enhance_hot_reloading();
if let Some(cache) = &*ASSETS_OVERRIDE {
cache.enhance_hot_reloading();
}
}
pub type AssetHandle<T> = assets_manager::Handle<'static, T>; pub type AssetHandle<T> = assets_manager::Handle<'static, T>;
pub type AssetGuard<T> = assets_manager::AssetGuard<'static, T>; pub type AssetGuard<T> = assets_manager::AssetGuard<'static, T>;
pub type AssetDirHandle<T> = assets_manager::DirHandle<'static, T, source::FileSystem>; pub type AssetDirHandle<T> = assets_manager::DirHandle<'static, T, fs::FileSystem>;
pub type ReloadWatcher = assets_manager::ReloadWatcher<'static>;
/// The Asset trait, which is implemented by all structures that have their data /// The Asset trait, which is implemented by all structures that have their data
/// stored in the filesystem. /// stored in the filesystem.
@ -76,12 +68,21 @@ pub trait AssetExt: Sized + Send + Sync + 'static {
/// ``` /// ```
#[track_caller] #[track_caller]
fn load_expect(specifier: &str) -> AssetHandle<Self> { fn load_expect(specifier: &str) -> AssetHandle<Self> {
Self::load(specifier).unwrap_or_else(|err| { #[track_caller]
#[cold]
fn expect_failed(err: Error) -> ! {
panic!( panic!(
"Failed loading essential asset: {} (error={:?})", "Failed loading essential asset: {} (error={:?})",
specifier, err err.id(),
err.reason()
) )
}) }
// Avoid using `unwrap_or_else` to avoid breaking `#[track_caller]`
match Self::load(specifier) {
Ok(handle) => handle,
Err(err) => expect_failed(err),
}
} }
/// Function used to load essential assets from the filesystem or the cache /// Function used to load essential assets from the filesystem or the cache
@ -111,21 +112,8 @@ pub fn load_dir<T: DirLoadable>(
specifier: &str, specifier: &str,
recursive: bool, recursive: bool,
) -> Result<AssetDirHandle<T>, Error> { ) -> Result<AssetDirHandle<T>, Error> {
use std::io;
let specifier = specifier.strip_suffix(".*").unwrap_or(specifier); let specifier = specifier.strip_suffix(".*").unwrap_or(specifier);
// Try override path first ASSETS.load_dir(specifier, recursive)
let from_override = match &*ASSETS_OVERRIDE {
Some(cache) => cache.load_dir(specifier, recursive),
None => return ASSETS.load_dir(specifier, recursive),
};
// If not found in override path, try load from main asset path
match from_override {
Err(Error::Io(e)) if e.kind() == io::ErrorKind::NotFound => {
ASSETS.load_dir(specifier, recursive)
},
_ => from_override,
}
} }
/// Loads directory and all files in it /// Loads directory and all files in it
@ -138,88 +126,30 @@ pub fn read_expect_dir<T: DirLoadable>(
specifier: &str, specifier: &str,
recursive: bool, recursive: bool,
) -> impl Iterator<Item = AssetGuard<T>> { ) -> impl Iterator<Item = AssetGuard<T>> {
load_dir::<T>(specifier, recursive) #[track_caller]
.unwrap_or_else(|e| panic!("Failed loading directory {}. error={:?}", e, specifier)) #[cold]
.ids() fn expect_failed(err: Error) -> ! {
.map(|entry| T::load_expect(entry).read()) panic!(
} "Failed loading directory: {} (error={:?})",
err.id(),
err.reason()
)
}
#[derive(Debug)] // Avoid using `unwrap_or_else` to avoid breaking `#[track_caller]`
pub struct AssetError(String, Error); match load_dir::<T>(specifier, recursive) {
Ok(dir) => dir.ids().map(|entry| T::load_expect(entry).read()),
impl std::error::Error for AssetError {} Err(err) => expect_failed(err),
impl fmt::Display for AssetError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Failed to load '{}': {}", self.0, self.1)
} }
} }
impl<T: Compound> AssetExt for T { impl<T: Compound> AssetExt for T {
fn load(specifier: &str) -> Result<AssetHandle<Self>, Error> { fn load(specifier: &str) -> Result<AssetHandle<Self>, Error> { ASSETS.load(specifier) }
use std::io;
// Try override path first
let from_override = match &*ASSETS_OVERRIDE {
Some(cache) => cache.load(specifier),
None => return ASSETS.load(specifier),
};
// If not found in override path, try load from main asset path
//
// NOTE: this won't work if asset catches error with
// Asset::default_value during Asset::load.
//
// We don't use it, and hopefully won't because there is
// `AssetExt::get_or_insert` or `AssetExt::load_or_insert_with`
// that allows you to do the same.
//
// If accidentaly we end up using Asset::default_value,
// there is possibility of this code trying to load
// from override cache and end there returning default value
// for `cache.load(specifier)` above.
match from_override {
Err(Error::Io(e)) if e.kind() == io::ErrorKind::NotFound => ASSETS.load(specifier),
Err(e) => Err(Error::Conversion(Box::new(AssetError(
specifier.to_string(),
e,
)))),
_ => from_override,
}
}
fn load_owned(specifier: &str) -> Result<Self, Error> { fn load_owned(specifier: &str) -> Result<Self, Error> { ASSETS.load_owned(specifier) }
use std::io;
// Try override path first
let from_override = match &*ASSETS_OVERRIDE {
Some(cache) => cache.load_owned(specifier),
None => return ASSETS.load_owned(specifier),
};
// If not found in override path, try load from main asset path
match from_override {
Err(Error::Io(e)) if e.kind() == io::ErrorKind::NotFound => {
ASSETS.load_owned(specifier)
},
_ => from_override,
}
}
fn get_or_insert(specifier: &str, default: Self) -> AssetHandle<Self> { fn get_or_insert(specifier: &str, default: Self) -> AssetHandle<Self> {
// 1) Check if we have ASSETS_OVERRIDE, if not - use main ASSETS ASSETS.get_or_insert(specifier, default)
// 2) Check if we have this asset in ASSETS_OVERRIDE, if not -
// use main ASSETS
// 3) If we have this asset in ASSETS_OVERRIDE, use ASSETS_OVERRIDE.
use std::io;
let override_cache = match &*ASSETS_OVERRIDE {
Some(cache) => cache,
None => return ASSETS.get_or_insert(specifier, default),
};
let from_override = override_cache.load::<T>(specifier);
// If not found in override path, try load from main asset path
match from_override {
Err(Error::Io(e)) if e.kind() == io::ErrorKind::NotFound => {
ASSETS.get_or_insert(specifier, default)
},
_ => override_cache.get_or_insert(specifier, default),
}
} }
} }
@ -355,11 +285,7 @@ lazy_static! {
/// Returns the actual path of the specifier with the extension. /// Returns the actual path of the specifier with the extension.
/// ///
/// For directories, give `""` as extension. /// For directories, give `""` as extension.
pub fn path_of(specifier: &str, ext: &str) -> PathBuf { pub fn path_of(specifier: &str, ext: &str) -> PathBuf { ASSETS.source().path_of(specifier, ext) }
ASSETS
.source()
.path_of(source::DirEntry::File(specifier, ext))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -481,7 +407,7 @@ pub mod asset_tweak {
Specifier::Asset(path) => path.join("."), Specifier::Asset(path) => path.join("."),
}; };
let handle = <AssetTweakWrapper<T> as AssetExt>::load_expect(&asset_specifier); let handle = <AssetTweakWrapper<T> as AssetExt>::load_expect(&asset_specifier);
let AssetTweakWrapper(value) = handle.read().clone(); let AssetTweakWrapper(value) = handle.cloned();
value value
} }

View File

@ -226,10 +226,10 @@ fn armor_stats() -> Result<(), Box<dyn Error>> {
); );
let pretty_config = PrettyConfig::new() let pretty_config = PrettyConfig::new()
.with_depth_limit(4) .depth_limit(4)
.with_separate_tuple_members(true) .separate_tuple_members(true)
.with_decimal_floats(true) .decimal_floats(true)
.with_enumerate_arrays(true); .enumerate_arrays(true);
let mut path = ASSETS_PATH.clone(); let mut path = ASSETS_PATH.clone();
for part in item.item_definition_id().split('.') { for part in item.item_definition_id().split('.') {
@ -418,10 +418,10 @@ fn weapon_stats() -> Result<(), Box<dyn Error>> {
); );
let pretty_config = PrettyConfig::new() let pretty_config = PrettyConfig::new()
.with_depth_limit(4) .depth_limit(4)
.with_separate_tuple_members(true) .separate_tuple_members(true)
.with_decimal_floats(true) .decimal_floats(true)
.with_enumerate_arrays(true); .enumerate_arrays(true);
let mut path = ASSETS_PATH.clone(); let mut path = ASSETS_PATH.clone();
for part in item.item_definition_id().split('.') { for part in item.item_definition_id().split('.') {
@ -494,9 +494,7 @@ fn loot_table(loot_table: &str) -> Result<(), Box<dyn Error>> {
items.push((chance, item)); items.push((chance, item));
} }
let pretty_config = PrettyConfig::new() let pretty_config = PrettyConfig::new().depth_limit(4).decimal_floats(true);
.with_depth_limit(4)
.with_decimal_floats(true);
let mut path = ASSETS_PATH.clone(); let mut path = ASSETS_PATH.clone();
path.push("common"); path.push("common");

View File

@ -197,10 +197,10 @@ pub mod figuredata {
} }
impl assets::Compound for ShipSpec { impl assets::Compound for ShipSpec {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
_: &str, _: &str,
) -> Result<Self, assets::Error> { ) -> Result<Self, assets::BoxedError> {
let manifest: AssetHandle<Ron<ShipCentralSpec>> = let manifest: AssetHandle<Ron<ShipCentralSpec>> =
AssetExt::load("common.manifests.ship_manifest")?; AssetExt::load("common.manifests.ship_manifest")?;
let mut colliders = HashMap::new(); let mut colliders = HashMap::new();

View File

@ -7,7 +7,7 @@ pub use modular::{ModularComponent, ModularComponentKind, ModularComponentTag};
pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind}; pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind};
use crate::{ use crate::{
assets::{self, AssetExt, Error}, assets::{self, AssetExt, BoxedError, Error},
comp::inventory::{item::tool::AbilityMap, InvSlot}, comp::inventory::{item::tool::AbilityMap, InvSlot},
effect::Effect, effect::Effect,
recipe::RecipeInput, recipe::RecipeInput,
@ -550,10 +550,10 @@ impl Deref for Item {
} }
impl assets::Compound for ItemDef { impl assets::Compound for ItemDef {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, Error> { ) -> Result<Self, BoxedError> {
// load from the filesystem first, but if the file doesn't exist, see if it's a // load from the filesystem first, but if the file doesn't exist, see if it's a
// programmaticly-generated asset // programmaticly-generated asset
let raw = match cache.load::<RawItemDef>(specifier) { let raw = match cache.load::<RawItemDef>(specifier) {

View File

@ -432,10 +432,10 @@ impl Asset for AbilityMap<String> {
} }
impl assets::Compound for AbilityMap { impl assets::Compound for AbilityMap {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, assets::Error> { ) -> Result<Self, assets::BoxedError> {
let manifest = cache.load::<AbilityMap<String>>(specifier)?.read(); let manifest = cache.load::<AbilityMap<String>>(specifier)?.read();
Ok(AbilityMap( Ok(AbilityMap(

View File

@ -502,7 +502,7 @@ impl LoadoutBuilder {
/// 3) Will panic while runs in tests and asset doesn't have "correct" form /// 3) Will panic while runs in tests and asset doesn't have "correct" form
#[must_use = "Method consumes builder and returns updated builder."] #[must_use = "Method consumes builder and returns updated builder."]
pub fn with_asset_expect(mut self, asset_specifier: &str, rng: &mut impl Rng) -> Self { pub fn with_asset_expect(mut self, asset_specifier: &str, rng: &mut impl Rng) -> Self {
let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone(); let spec = LoadoutSpec::load_expect_cloned(asset_specifier).0;
for (key, entry) in spec { for (key, entry) in spec {
let item = match entry.try_to_item(asset_specifier, rng) { let item = match entry.try_to_item(asset_specifier, rng) {
Some(item) => item, Some(item) => item,

View File

@ -151,10 +151,10 @@ impl EqualitySet {
} }
impl assets::Compound for EqualitySet { impl assets::Compound for EqualitySet {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
id: &str, id: &str,
) -> Result<Self, assets::Error> { ) -> Result<Self, assets::BoxedError> {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
enum EqualitySpec { enum EqualitySpec {
LootTable(String), LootTable(String),

View File

@ -202,7 +202,7 @@ impl EntityInfo {
/// Helper function for applying config from asset /// Helper function for applying config from asset
pub fn with_asset_expect(self, asset_specifier: &str) -> Self { pub fn with_asset_expect(self, asset_specifier: &str) -> Self {
let config = EntityConfig::load_expect(asset_specifier).read().clone(); let config = EntityConfig::load_expect_cloned(asset_specifier);
self.with_entity_config(config, Some(asset_specifier)) self.with_entity_config(config, Some(asset_specifier))
} }

View File

@ -234,10 +234,10 @@ impl assets::Asset for RawRecipeBook {
} }
impl assets::Compound for RecipeBook { impl assets::Compound for RecipeBook {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, assets::Error> { ) -> Result<Self, assets::BoxedError> {
#[inline] #[inline]
fn load_item_def(spec: &(String, u32)) -> Result<(Arc<ItemDef>, u32), assets::Error> { fn load_item_def(spec: &(String, u32)) -> Result<(Arc<ItemDef>, u32), assets::Error> {
let def = Arc::<ItemDef>::load_cloned(&spec.0)?; let def = Arc::<ItemDef>::load_cloned(&spec.0)?;
@ -255,7 +255,7 @@ impl assets::Compound for RecipeBook {
Ok((def, spec.1)) Ok((def, spec.1))
} }
let mut raw = cache.load::<RawRecipeBook>(specifier)?.read().clone(); let mut raw = cache.load::<RawRecipeBook>(specifier)?.cloned();
// Avoid showing purple-question-box recipes until the assets are added // Avoid showing purple-question-box recipes until the assets are added
// (the `if false` is needed because commenting out the call will add a warning // (the `if false` is needed because commenting out the call will add a warning

View File

@ -1,6 +1,6 @@
use super::{BlockKind, SpriteKind}; use super::{BlockKind, SpriteKind};
use crate::{ use crate::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Error}, assets::{self, AssetExt, AssetHandle, BoxedError, DotVoxAsset},
make_case_elim, make_case_elim,
vol::{BaseVol, ReadVol, SizedVol, WriteVol}, vol::{BaseVol, ReadVol, SizedVol, WriteVol},
volumes::dyna::{Dyna, DynaError}, volumes::dyna::{Dyna, DynaError},
@ -67,10 +67,10 @@ impl std::ops::Deref for StructuresGroup {
} }
impl assets::Compound for StructuresGroup { impl assets::Compound for StructuresGroup {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, Error> { ) -> Result<Self, BoxedError> {
let specs = cache.load::<StructuresGroupSpec>(specifier)?.read(); let specs = cache.load::<StructuresGroupSpec>(specifier)?.read();
Ok(StructuresGroup( Ok(StructuresGroup(
@ -94,7 +94,7 @@ impl assets::Compound for StructuresGroup {
}, },
}) })
}) })
.collect::<Result<_, Error>>()?, .collect::<Result<_, BoxedError>>()?,
)) ))
} }
} }
@ -137,10 +137,10 @@ impl ReadVol for Structure {
} }
impl assets::Compound for BaseStructure { impl assets::Compound for BaseStructure {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
cache: &assets::AssetCache<S>, cache: &assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, Error> { ) -> Result<Self, BoxedError> {
let dot_vox_data = cache.load::<DotVoxAsset>(specifier)?.read(); let dot_vox_data = cache.load::<DotVoxAsset>(specifier)?.read();
let dot_vox_data = &dot_vox_data.0; let dot_vox_data = &dot_vox_data.0;

View File

@ -89,6 +89,7 @@ gilrs = {version = "0.8.0", features = ["serde-serialize"]}
server = { package = "veloren-server", path = "../server", optional = true, default-features = false, features = ["worldgen"] } server = { package = "veloren-server", path = "../server", optional = true, default-features = false, features = ["worldgen"] }
# Utility # Utility
assets_manager = {version = "0.7", features = ["ab_glyph"]}
backtrace = "0.3.40" backtrace = "0.3.40"
bincode = "1.3.1" bincode = "1.3.1"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }

View File

@ -11,7 +11,7 @@ pub mod verification;
pub use path::BasePath; pub use path::BasePath;
use crate::path::{LANG_EXTENSION, LANG_MANIFEST_FILE}; use crate::path::{LANG_EXTENSION, LANG_MANIFEST_FILE};
use common_assets::{self, source::DirEntry, AssetExt, AssetGuard, AssetHandle}; use common_assets::{self, source::DirEntry, AssetExt, AssetGuard, AssetHandle, ReloadWatcher};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use raw::{RawFragment, RawLanguage, RawManifest}; use raw::{RawFragment, RawLanguage, RawManifest};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -102,10 +102,10 @@ impl Language {
} }
impl common_assets::Compound for Language { impl common_assets::Compound for Language {
fn load<S: common_assets::source::Source>( fn load<S: common_assets::source::Source + ?Sized>(
cache: &common_assets::AssetCache<S>, cache: &common_assets::AssetCache<S>,
asset_key: &str, asset_key: &str,
) -> Result<Self, common_assets::Error> { ) -> Result<Self, common_assets::BoxedError> {
let manifest = cache let manifest = cache
.load::<RawManifest>(&[asset_key, ".", LANG_MANIFEST_FILE].concat())? .load::<RawManifest>(&[asset_key, ".", LANG_MANIFEST_FILE].concat())?
.cloned(); .cloned();
@ -145,9 +145,10 @@ impl common_assets::Compound for Language {
/// the central data structure to handle localization in veloren /// the central data structure to handle localization in veloren
// inherit Copy+Clone from AssetHandle // inherit Copy+Clone from AssetHandle
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct LocalizationHandle { pub struct LocalizationHandle {
active: AssetHandle<Language>, active: AssetHandle<Language>,
watcher: ReloadWatcher,
fallback: Option<AssetHandle<Language>>, fallback: Option<AssetHandle<Language>>,
pub use_english_fallback: bool, pub use_english_fallback: bool,
} }
@ -259,8 +260,10 @@ impl LocalizationHandle {
let default_key = ["voxygen.i18n.", REFERENCE_LANG].concat(); let default_key = ["voxygen.i18n.", REFERENCE_LANG].concat();
let language_key = ["voxygen.i18n.", specifier].concat(); let language_key = ["voxygen.i18n.", specifier].concat();
let is_default = language_key == default_key; let is_default = language_key == default_key;
let active = Language::load(&language_key)?;
Ok(Self { Ok(Self {
active: Language::load(&language_key)?, active,
watcher: active.reload_watcher(),
fallback: if is_default { fallback: if is_default {
None None
} else { } else {
@ -274,16 +277,16 @@ impl LocalizationHandle {
Self::load(specifier).expect("Can't load language files") Self::load(specifier).expect("Can't load language files")
} }
pub fn reloaded(&mut self) -> bool { self.active.reloaded() } pub fn reloaded(&mut self) -> bool { self.watcher.reloaded() }
} }
struct FindManifests; struct FindManifests;
impl common_assets::Compound for FindManifests { impl common_assets::Compound for FindManifests {
fn load<S: common_assets::Source>( fn load<S: common_assets::Source + ?Sized>(
_: &common_assets::AssetCache<S>, _: &common_assets::AssetCache<S>,
_: &str, _: &str,
) -> Result<Self, common_assets::Error> { ) -> Result<Self, common_assets::BoxedError> {
Ok(Self) Ok(Self)
} }
} }
@ -312,10 +315,10 @@ impl common_assets::DirLoadable for FindManifests {
struct LocalizationList(Vec<LanguageMetadata>); struct LocalizationList(Vec<LanguageMetadata>);
impl common_assets::Compound for LocalizationList { impl common_assets::Compound for LocalizationList {
fn load<S: common_assets::Source>( fn load<S: common_assets::Source + ?Sized>(
cache: &common_assets::AssetCache<S>, cache: &common_assets::AssetCache<S>,
specifier: &str, specifier: &str,
) -> Result<Self, common_assets::Error> { ) -> Result<Self, common_assets::BoxedError> {
// List language directories // List language directories
let languages = common_assets::load_dir::<FindManifests>(specifier, false) let languages = common_assets::load_dir::<FindManifests>(specifier, false)
.unwrap_or_else(|e| panic!("Failed to get manifests from {}: {:?}", specifier, e)) .unwrap_or_else(|e| panic!("Failed to get manifests from {}: {:?}", specifier, e))

View File

@ -31,16 +31,11 @@ pub(crate) struct RawLanguage<T> {
pub(crate) fragments: HashMap</* relative to i18n_path */ PathBuf, RawFragment<T>>, pub(crate) fragments: HashMap</* relative to i18n_path */ PathBuf, RawFragment<T>>,
} }
#[derive(Debug)] pub(crate) fn load_manifest(path: &LangPath) -> Result<RawManifest, common_assets::BoxedError> {
pub(crate) enum RawError {
RonError(ron::Error),
}
pub(crate) fn load_manifest(path: &LangPath) -> Result<RawManifest, common_assets::Error> {
let manifest_file = path.file(LANG_MANIFEST_FILE); let manifest_file = path.file(LANG_MANIFEST_FILE);
tracing::debug!(?manifest_file, "manifest loading"); tracing::debug!(?manifest_file, "manifest loading");
let f = fs::File::open(&manifest_file)?; let f = fs::File::open(&manifest_file)?;
let manifest: RawManifest = from_reader(f).map_err(RawError::RonError)?; let manifest: RawManifest = from_reader(f)?;
// verify that the folder name `de_DE` matches the value inside the metadata! // verify that the folder name `de_DE` matches the value inside the metadata!
assert_eq!( assert_eq!(
manifest.metadata.language_identifier, manifest.metadata.language_identifier,
@ -52,7 +47,7 @@ pub(crate) fn load_manifest(path: &LangPath) -> Result<RawManifest, common_asset
pub(crate) fn load_raw_language( pub(crate) fn load_raw_language(
path: &LangPath, path: &LangPath,
manifest: RawManifest, manifest: RawManifest,
) -> Result<RawLanguage<String>, common_assets::Error> { ) -> Result<RawLanguage<String>, common_assets::BoxedError> {
//get List of files //get List of files
let files = path.fragments()?; let files = path.fragments()?;
@ -60,7 +55,7 @@ pub(crate) fn load_raw_language(
let mut fragments = HashMap::new(); let mut fragments = HashMap::new();
for sub_path in files { for sub_path in files {
let f = fs::File::open(path.sub_path(&sub_path))?; let f = fs::File::open(path.sub_path(&sub_path))?;
let fragment = from_reader(f).map_err(RawError::RonError)?; let fragment = from_reader(f)?;
fragments.insert(sub_path, fragment); fragments.insert(sub_path, fragment);
} }
@ -105,20 +100,6 @@ impl From<RawLanguage<String>> for Language {
} }
} }
impl core::fmt::Display for RawError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RawError::RonError(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for RawError {}
impl From<RawError> for common_assets::Error {
fn from(e: RawError) -> Self { Self::Conversion(Box::new(e)) }
}
impl common_assets::Asset for RawManifest { impl common_assets::Asset for RawManifest {
type Loader = common_assets::RonLoader; type Loader = common_assets::RonLoader;

View File

@ -420,10 +420,10 @@ impl assets::Asset for SoundtrackCollection<RawSoundtrackItem> {
} }
impl assets::Compound for SoundtrackCollection<SoundtrackItem> { impl assets::Compound for SoundtrackCollection<SoundtrackItem> {
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
_: &assets::AssetCache<S>, _: &assets::AssetCache<S>,
id: &str, id: &str,
) -> Result<Self, assets::Error> { ) -> Result<Self, assets::BoxedError> {
let inner = || -> Result<_, assets::Error> { let inner = || -> Result<_, assets::Error> {
let manifest: AssetHandle<SoundtrackCollection<RawSoundtrackItem>> = let manifest: AssetHandle<SoundtrackCollection<RawSoundtrackItem>> =
AssetExt::load(id)?; AssetExt::load(id)?;

View File

@ -1,6 +1,6 @@
use crate::ui::{Graphic, SampleStrat, Transform, Ui}; use crate::ui::{Graphic, SampleStrat, Transform, Ui};
use common::{ use common::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset}, assets::{self, AssetExt, AssetHandle, DotVoxAsset, ReloadWatcher},
comp::item::{ comp::item::{
armor::{Armor, ArmorKind}, armor::{Armor, ArmorKind},
Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility,
@ -109,6 +109,7 @@ impl assets::Asset for ItemImagesSpec {
pub struct ItemImgs { pub struct ItemImgs {
map: HashMap<ItemKey, Id>, map: HashMap<ItemKey, Id>,
manifest: AssetHandle<ItemImagesSpec>, manifest: AssetHandle<ItemImagesSpec>,
watcher: ReloadWatcher,
not_found: Id, not_found: Id,
} }
@ -128,6 +129,7 @@ impl ItemImgs {
Self { Self {
map, map,
manifest, manifest,
watcher: manifest.reload_watcher(),
not_found, not_found,
} }
} }
@ -135,7 +137,7 @@ impl ItemImgs {
/// Checks if the manifest has been changed and reloads the images if so /// Checks if the manifest has been changed and reloads the images if so
/// Reuses img ids /// Reuses img ids
pub fn reload_if_changed(&mut self, ui: &mut Ui) { pub fn reload_if_changed(&mut self, ui: &mut Ui) {
if self.manifest.reloaded() { if self.watcher.reloaded() {
for (kind, spec) in self.manifest.read().0.iter() { for (kind, spec) in self.manifest.read().0.iter() {
// Load new graphic // Load new graphic
let graphic = spec.create_graphic(); let graphic = spec.create_graphic();

View File

@ -28,7 +28,7 @@ use super::{
AaMode, AddressMode, FilterMode, OtherModes, PipelineModes, RenderError, RenderMode, AaMode, AddressMode, FilterMode, OtherModes, PipelineModes, RenderError, RenderMode,
ShadowMapMode, ShadowMode, Vertex, ShadowMapMode, ShadowMode, Vertex,
}; };
use common::assets::{self, AssetExt, AssetHandle}; use common::assets::{self, AssetExt, AssetHandle, ReloadWatcher};
use common_base::span; use common_base::span;
use core::convert::TryFrom; use core::convert::TryFrom;
#[cfg(feature = "egui-ui")] #[cfg(feature = "egui-ui")]
@ -157,6 +157,7 @@ pub struct Renderer {
quad_index_buffer_u32: Buffer<u32>, quad_index_buffer_u32: Buffer<u32>,
shaders: AssetHandle<Shaders>, shaders: AssetHandle<Shaders>,
shaders_watcher: ReloadWatcher,
pipeline_modes: PipelineModes, pipeline_modes: PipelineModes,
other_modes: OtherModes, other_modes: OtherModes,
@ -360,6 +361,7 @@ impl Renderer {
.ok(); .ok();
let shaders = Shaders::load_expect(""); let shaders = Shaders::load_expect("");
let shaders_watcher = shaders.reload_watcher();
let layouts = { let layouts = {
let global = GlobalsLayouts::new(&device); let global = GlobalsLayouts::new(&device);
@ -407,7 +409,7 @@ impl Renderer {
immutable: Arc::clone(&layouts.immutable), immutable: Arc::clone(&layouts.immutable),
postprocess: Arc::clone(&layouts.postprocess), postprocess: Arc::clone(&layouts.postprocess),
}, },
shaders.read().clone(), shaders.cloned(),
pipeline_modes.clone(), pipeline_modes.clone(),
sc_desc.clone(), // Note: cheap clone sc_desc.clone(), // Note: cheap clone
shadow_views.is_some(), shadow_views.is_some(),
@ -510,6 +512,7 @@ impl Renderer {
quad_index_buffer_u32, quad_index_buffer_u32,
shaders, shaders,
shaders_watcher,
pipeline_modes, pipeline_modes,
other_modes, other_modes,
@ -1053,7 +1056,7 @@ impl Renderer {
} }
// If the shaders files were changed attempt to recreate the shaders // If the shaders files were changed attempt to recreate the shaders
if self.shaders.reloaded() { if self.shaders_watcher.reloaded() {
self.recreate_pipelines(self.pipeline_modes.clone()); self.recreate_pipelines(self.pipeline_modes.clone());
} }
@ -1113,7 +1116,7 @@ impl Renderer {
pipeline_creation::recreate_pipelines( pipeline_creation::recreate_pipelines(
Arc::clone(&self.device), Arc::clone(&self.device),
Arc::clone(&self.layouts.immutable), Arc::clone(&self.layouts.immutable),
self.shaders.read().clone(), self.shaders.cloned(),
pipeline_modes, pipeline_modes,
// NOTE: if present_mode starts to be used to configure pipelines then it // NOTE: if present_mode starts to be used to configure pipelines then it
// needs to become a part of the pipeline modes // needs to become a part of the pipeline modes

View File

@ -24,10 +24,10 @@ pub struct Shaders {
impl assets::Compound for Shaders { impl assets::Compound for Shaders {
// TODO: Taking the specifier argument as a base for shaders specifiers // TODO: Taking the specifier argument as a base for shaders specifiers
// would allow to use several shaders groups easily // would allow to use several shaders groups easily
fn load<S: assets::source::Source>( fn load<S: assets::source::Source + ?Sized>(
_: &assets::AssetCache<S>, _: &assets::AssetCache<S>,
_: &str, _: &str,
) -> Result<Shaders, assets::Error> { ) -> Result<Shaders, assets::BoxedError> {
let shaders = [ let shaders = [
"include.constants", "include.constants",
"include.globals", "include.globals",

View File

@ -6,6 +6,7 @@ use crate::{
}; };
use anim::Skeleton; use anim::Skeleton;
use common::{ use common::{
assets::ReloadWatcher,
comp::{ comp::{
inventory::{ inventory::{
slot::{ArmorSlot, EquipSlot}, slot::{ArmorSlot, EquipSlot},
@ -287,6 +288,7 @@ where
{ {
models: HashMap<FigureKey<Skel::Body>, ((FigureModelEntryFuture<LOD_COUNT>, Skel::Attr), u64)>, models: HashMap<FigureKey<Skel::Body>, ((FigureModelEntryFuture<LOD_COUNT>, Skel::Attr), u64)>,
manifests: <Skel::Body as BodySpec>::Manifests, manifests: <Skel::Body as BodySpec>::Manifests,
watcher: ReloadWatcher,
} }
impl<Skel: Skeleton> FigureModelCache<Skel> impl<Skel: Skeleton> FigureModelCache<Skel>
@ -295,10 +297,14 @@ where
{ {
#[allow(clippy::new_without_default)] // TODO: Pending review in #587 #[allow(clippy::new_without_default)] // TODO: Pending review in #587
pub fn new() -> Self { pub fn new() -> Self {
// NOTE: It might be better to bubble this error up rather than panicking.
let manifests = <Skel::Body as BodySpec>::load_spec().unwrap();
let watcher = <Skel::Body as BodySpec>::reload_watcher(&manifests);
Self { Self {
models: HashMap::new(), models: HashMap::new(),
// NOTE: It might be better to bubble this error up rather than panicking. manifests,
manifests: <Skel::Body as BodySpec>::load_spec().unwrap(), watcher,
} }
} }
@ -561,7 +567,7 @@ where
{ {
// Check for reloaded manifests // Check for reloaded manifests
// TODO: maybe do this in a different function, maintain? // TODO: maybe do this in a different function, maintain?
if <Skel::Body as BodySpec>::is_reloaded(&mut self.manifests) { if self.watcher.reloaded() {
col_lights.atlas.clear(); col_lights.atlas.clear();
self.models.clear(); self.models.clear();
} }

View File

@ -1,6 +1,6 @@
use super::cache::{FigureKey, ToolKey}; use super::cache::{FigureKey, ToolKey};
use common::{ use common::{
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron}, assets::{self, AssetExt, AssetHandle, DotVoxAsset, ReloadWatcher, Ron},
comp::{ comp::{
biped_large::{self, BodyType as BLBodyType, Species as BLSpecies}, biped_large::{self, BodyType as BLBodyType, Species as BLSpecies},
biped_small, biped_small,
@ -96,7 +96,7 @@ pub trait BodySpec: Sized {
fn load_spec() -> Result<Self::Manifests, assets::Error>; fn load_spec() -> Result<Self::Manifests, assets::Error>;
/// Determine whether the cache's manifest was reloaded /// Determine whether the cache's manifest was reloaded
fn is_reloaded(manifests: &mut Self::Manifests) -> bool; fn reload_watcher(manifests: &Self::Manifests) -> ReloadWatcher;
/// Mesh bones using the given spec, character state, and mesh generation /// Mesh bones using the given spec, character state, and mesh generation
/// function. /// function.
@ -124,7 +124,7 @@ macro_rules! make_vox_spec {
} }
impl assets::Compound for $Spec { impl assets::Compound for $Spec {
fn load<S: assets::source::Source>(_: &assets::AssetCache<S>, _: &str) -> Result<Self, assets::Error> { fn load<S: assets::source::Source + ?Sized>(_: &assets::AssetCache<S>, _: &str) -> Result<Self, assets::BoxedError> {
Ok($Spec { Ok($Spec {
$( $field: AssetExt::load($asset_path)?, )* $( $field: AssetExt::load($asset_path)?, )*
}) })
@ -141,7 +141,7 @@ macro_rules! make_vox_spec {
Self::Spec::load("") Self::Spec::load("")
} }
fn is_reloaded(manifests: &mut Self::Manifests) -> bool { manifests.reloaded() } fn reload_watcher(manifests: &Self::Manifests) -> ReloadWatcher { manifests.reload_watcher() }
fn bone_meshes( fn bone_meshes(
$self_pat: &FigureKey<Self>, $self_pat: &FigureKey<Self>,
@ -4549,7 +4549,7 @@ impl BodySpec for ship::Body {
#[allow(unused_variables)] #[allow(unused_variables)]
fn load_spec() -> Result<Self::Manifests, assets::Error> { Self::Spec::load("") } fn load_spec() -> Result<Self::Manifests, assets::Error> { Self::Spec::load("") }
fn is_reloaded(manifests: &mut Self::Manifests) -> bool { manifests.reloaded() } fn reload_watcher(manifests: &Self::Manifests) -> ReloadWatcher { manifests.reload_watcher() }
fn bone_meshes( fn bone_meshes(
FigureKey { body, .. }: &FigureKey<Self>, FigureKey { body, .. }: &FigureKey<Self>,

View File

@ -61,7 +61,9 @@ impl BodySpec for VolumeKey {
fn load_spec() -> Result<Self::Manifests, assets::Error> { Ok(()) } fn load_spec() -> Result<Self::Manifests, assets::Error> { Ok(()) }
fn is_reloaded(_: &mut Self::Manifests) -> bool { false } fn reload_watcher(_: &Self::Manifests) -> assets::ReloadWatcher {
assets::ReloadWatcher::default()
}
fn bone_meshes( fn bone_meshes(
_: &FigureKey<Self>, _: &FigureKey<Self>,

View File

@ -5,10 +5,7 @@ use crate::{
}; };
use common::assets::{self, AssetExt}; use common::assets::{self, AssetExt};
use glyph_brush::GlyphBrushBuilder; use glyph_brush::GlyphBrushBuilder;
use std::{ use std::cell::{RefCell, RefMut};
borrow::Cow,
cell::{RefCell, RefMut},
};
use vek::*; use vek::*;
// Multiplied by current window size // Multiplied by current window size
@ -24,22 +21,7 @@ type GlyphBrush = glyph_brush::GlyphBrush<(Aabr<f32>, Aabr<f32>), ()>;
// TODO: might not need pub // TODO: might not need pub
pub type Font = glyph_brush::ab_glyph::FontArc; pub type Font = glyph_brush::ab_glyph::FontArc;
struct FontAsset(Font); pub fn load_font(specifier: &str) -> Font { Font::load_expect(specifier).cloned() }
struct FontLoader;
impl assets::Loader<FontAsset> for FontLoader {
fn load(data: Cow<[u8]>, _: &str) -> Result<FontAsset, assets::BoxedError> {
let font = Font::try_from_vec(data.into_owned())?;
Ok(FontAsset(font))
}
}
impl assets::Asset for FontAsset {
type Loader = FontLoader;
const EXTENSION: &'static str = "ttf";
}
pub fn load_font(specifier: &str) -> Font { FontAsset::load_expect(specifier).read().0.clone() }
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
pub struct FontId(pub(super) glyph_brush::FontId); pub struct FontId(pub(super) glyph_brush::FontId);

View File

@ -38,7 +38,7 @@ impl assets::Asset for SpawnEntry {
} }
impl SpawnEntry { impl SpawnEntry {
pub fn from(asset_specifier: &str) -> Self { Self::load_expect(asset_specifier).read().clone() } pub fn from(asset_specifier: &str) -> Self { Self::load_expect_cloned(asset_specifier) }
pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<Pack> { pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<Pack> {
self.rules self.rules

View File

@ -442,18 +442,17 @@ impl WorldSim {
FileOpts::LoadAsset(ref specifier) => match WorldFile::load_owned(specifier) { FileOpts::LoadAsset(ref specifier) => match WorldFile::load_owned(specifier) {
Ok(map) => map.into_modern(), Ok(map) => map.into_modern(),
Err(err) => { Err(err) => {
match err { match err.reason().downcast_ref::<std::io::Error>() {
assets::Error::Io(e) => { Some(e) => {
warn!(?e, ?specifier, "Couldn't read asset specifier for maps"); warn!(?e, ?specifier, "Couldn't read asset specifier for maps");
}, },
assets::Error::Conversion(e) => { None => {
warn!( warn!(
?e, ?err,
"Couldn't parse modern map. Maybe you meant to try a legacy \ "Couldn't parse modern map. Maybe you meant to try a legacy \
load?" load?"
); );
}, },
assets::Error::NoDefaultValue => unreachable!(),
} }
return None; return None;
}, },