diff --git a/Cargo.lock b/Cargo.lock index f1775a8d45..c3cf499305 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5514,7 +5514,6 @@ name = "veloren-common" version = "0.9.0" dependencies = [ "approx 0.4.0", - "assets_manager", "bitflags", "criterion", "crossbeam-channel", @@ -5523,7 +5522,6 @@ dependencies = [ "dot_vox", "enum-iterator", "hashbrown", - "image", "indexmap", "lazy_static", "num-derive", @@ -5531,10 +5529,8 @@ dependencies = [ "ordered-float 2.1.1", "rand 0.8.3", "rayon", - "ron", "roots", "serde", - "serde_json", "serde_repr", "slab", "slotmap", @@ -5548,9 +5544,22 @@ dependencies = [ "tracing-subscriber", "uuid", "vek", + "veloren-common-assets", "veloren-common-base", ] +[[package]] +name = "veloren-common-assets" +version = "0.9.0" +dependencies = [ + "assets_manager", + "dot_vox", + "image", + "lazy_static", + "ron", + "tracing", +] + [[package]] name = "veloren-common-base" version = "0.9.0" @@ -5647,14 +5656,13 @@ dependencies = [ name = "veloren-i18n" version = "0.9.0" dependencies = [ - "assets_manager", "deunicode", "git2", "hashbrown", - "lazy_static", "ron", "serde", "tracing", + "veloren-common-assets", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9e4152af59..90fc87e346 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ cargo-features = ["named-profiles","profile-overrides"] [workspace] members = [ "common", + "common/assets", "common/base", "common/ecs", "common/net", diff --git a/common/Cargo.toml b/common/Cargo.toml index 2d8f2ed24e..f537a79eeb 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -39,14 +39,10 @@ uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] rand = "0.8" # Assets +assets = {package = "veloren-common-assets", path = "assets"} dot_vox = "4.0" -image = { version = "0.23.12", default-features = false, features = ["png"] } # Assets -assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]} -# Serde -ron = { version = "0.6", default-features = false } -serde_json = "1.0.50" serde_repr = "0.1.6" # csv export diff --git a/common/assets/Cargo.toml b/common/assets/Cargo.toml new file mode 100644 index 0000000000..226b982955 --- /dev/null +++ b/common/assets/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["juliancoffee "] +edition = "2018" +name = "veloren-common-assets" +description = "Crate for game loading assets for veloren." +version = "0.9.0" + +[dependencies] +lazy_static = "1.4.0" +assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]} +ron = { version = "0.6", default-features = false } +dot_vox = "4.0" +image = { version = "0.23.12", default-features = false, features = ["png"] } +tracing = "0.1" diff --git a/voxygen/i18n/src/assets.rs b/common/assets/src/lib.rs similarity index 63% rename from voxygen/i18n/src/assets.rs rename to common/assets/src/lib.rs index 5b8e4b3bf4..9ce38b13ad 100644 --- a/voxygen/i18n/src/assets.rs +++ b/common/assets/src/lib.rs @@ -1,12 +1,21 @@ //! Load assets (images or voxel data) from files +use dot_vox::DotVoxData; +use image::DynamicImage; use lazy_static::lazy_static; -use std::path::PathBuf; +use std::{ + borrow::Cow, + fs, io, + path::{Path, PathBuf}, + sync::Arc, +}; pub use assets_manager::{ asset::Ron, - loader::{self, RonLoader}, - source, Asset, AssetCache, Compound, Error, + loader::{ + self, BincodeLoader, BytesLoader, JsonLoader, LoadFrom, Loader, RonLoader, StringLoader, + }, + source, Asset, AssetCache, BoxedError, Compound, Error, }; lazy_static! { @@ -14,13 +23,22 @@ lazy_static! { static ref ASSETS: AssetCache = AssetCache::new(&*ASSETS_PATH).unwrap(); } +pub fn start_hot_reloading() { ASSETS.enhance_hot_reloading(); } + pub type AssetHandle = assets_manager::Handle<'static, T>; pub type AssetGuard = assets_manager::AssetGuard<'static, T>; +pub type AssetDir = assets_manager::DirReader<'static, T, source::FileSystem>; /// The Asset trait, which is implemented by all structures that have their data /// stored in the filesystem. pub trait AssetExt: Sized + Send + Sync + 'static { /// Function used to load assets from the filesystem or the cache. + /// Example usage: + /// ```no_run + /// use veloren_common::assets::{self, AssetExt}; + /// + /// let my_image = assets::Image::load("core.ui.backgrounds.city").unwrap(); + /// ``` fn load(specifier: &str) -> Result, Error>; /// Function used to load assets from the filesystem or the cache and return @@ -33,7 +51,12 @@ pub trait AssetExt: Sized + Send + Sync + 'static { } /// Function used to load essential assets from the filesystem or the cache. - /// It will panic if the asset is not found. + /// It will panic if the asset is not found. Example usage: + /// ```no_run + /// use veloren_common::assets::{self, AssetExt}; + /// + /// let my_image = assets::Image::load_expect("core.ui.backgrounds.city"); + /// ``` #[track_caller] fn load_expect(specifier: &str) -> AssetHandle { Self::load(specifier).unwrap_or_else(|err| { @@ -57,12 +80,52 @@ pub trait AssetExt: Sized + Send + Sync + 'static { fn load_owned(specifier: &str) -> Result; } +pub fn load_dir(specifier: &str) -> Result, Error> { + Ok(ASSETS.load_dir(specifier)?) +} + impl AssetExt for T { fn load(specifier: &str) -> Result, Error> { ASSETS.load(specifier) } fn load_owned(specifier: &str) -> Result { ASSETS.load_owned(specifier) } } +pub struct Image(pub Arc); + +impl Image { + pub fn to_image(&self) -> Arc { Arc::clone(&self.0) } +} + +pub struct ImageLoader; +impl Loader for ImageLoader { + fn load(content: Cow<[u8]>, _: &str) -> Result { + let image = image::load_from_memory(&content)?; + Ok(Image(Arc::new(image))) + } +} + +impl Asset for Image { + type Loader = ImageLoader; + + const EXTENSIONS: &'static [&'static str] = &["png", "jpg"]; +} + +pub struct DotVoxAsset(pub DotVoxData); + +pub struct DotVoxLoader; +impl Loader for DotVoxLoader { + fn load(content: std::borrow::Cow<[u8]>, _: &str) -> Result { + let data = dot_vox::load_bytes(&content).map_err(|err| err.to_owned())?; + Ok(DotVoxAsset(data)) + } +} + +impl Asset for DotVoxAsset { + type Loader = DotVoxLoader; + + const EXTENSION: &'static str = "vox"; +} + lazy_static! { /// Lazy static to find and cache where the asset directory is. /// Cases we need to account for: @@ -147,3 +210,40 @@ lazy_static! { /// /// For directories, give `""` as extension. pub fn path_of(specifier: &str, ext: &str) -> PathBuf { ASSETS.source().path_of(specifier, ext) } + +fn get_dir_files(files: &mut Vec, path: &Path, specifier: &str) -> io::Result<()> { + for entry in (fs::read_dir(path)?).flatten() { + let path = entry.path(); + let maybe_stem = path.file_stem().and_then(|stem| stem.to_str()); + + if let Some(stem) = maybe_stem { + let specifier = format!("{}.{}", specifier, stem); + + if path.is_dir() { + get_dir_files(files, &path, &specifier)?; + } else { + files.push(specifier); + } + } + } + + Ok(()) +} + +pub struct Directory(Vec); + +impl Directory { + pub fn iter(&self) -> impl Iterator { self.0.iter() } +} + +impl Compound for Directory { + fn load(_: &AssetCache, specifier: &str) -> Result { + let specifier = specifier.strip_suffix(".*").unwrap_or(specifier); + let root = ASSETS.source().path_of(specifier, ""); + let mut files = Vec::new(); + + get_dir_files(&mut files, &root, specifier)?; + + Ok(Directory(files)) + } +} diff --git a/common/src/assets.rs b/common/src/assets.rs deleted file mode 100644 index 737810da46..0000000000 --- a/common/src/assets.rs +++ /dev/null @@ -1,358 +0,0 @@ -//! Load assets (images or voxel data) from files - -use dot_vox::DotVoxData; -use image::DynamicImage; -use lazy_static::lazy_static; -use std::{ - borrow::Cow, - fs, io, - path::{Path, PathBuf}, - sync::Arc, -}; - -pub use assets_manager::{ - asset::Ron, - loader::{ - self, BincodeLoader, BytesLoader, JsonLoader, LoadFrom, Loader, RonLoader, StringLoader, - }, - source, Asset, AssetCache, BoxedError, Compound, Error, -}; - -lazy_static! { - /// The HashMap where all loaded assets are stored in. - static ref ASSETS: AssetCache = AssetCache::new(&*ASSETS_PATH).unwrap(); -} - -pub fn start_hot_reloading() { ASSETS.enhance_hot_reloading(); } - -pub type AssetHandle = assets_manager::Handle<'static, T>; -pub type AssetGuard = assets_manager::AssetGuard<'static, T>; -pub type AssetDir = assets_manager::DirReader<'static, T, source::FileSystem>; - -/// The Asset trait, which is implemented by all structures that have their data -/// stored in the filesystem. -pub trait AssetExt: Sized + Send + Sync + 'static { - /// Function used to load assets from the filesystem or the cache. - /// Example usage: - /// ```no_run - /// use veloren_common::assets::{self, AssetExt}; - /// - /// let my_image = assets::Image::load("core.ui.backgrounds.city").unwrap(); - /// ``` - fn load(specifier: &str) -> Result, Error>; - - /// Function used to load assets from the filesystem or the cache and return - /// a clone. - fn load_cloned(specifier: &str) -> Result - where - Self: Clone, - { - Self::load(specifier).map(AssetHandle::cloned) - } - - /// Function used to load essential assets from the filesystem or the cache. - /// It will panic if the asset is not found. Example usage: - /// ```no_run - /// use veloren_common::assets::{self, AssetExt}; - /// - /// let my_image = assets::Image::load_expect("core.ui.backgrounds.city"); - /// ``` - #[track_caller] - fn load_expect(specifier: &str) -> AssetHandle { - Self::load(specifier).unwrap_or_else(|err| { - panic!( - "Failed loading essential asset: {} (error={:?})", - specifier, err - ) - }) - } - - /// Function used to load essential assets from the filesystem or the cache - /// and return a clone. It will panic if the asset is not found. - #[track_caller] - fn load_expect_cloned(specifier: &str) -> Self - where - Self: Clone, - { - Self::load_expect(specifier).cloned() - } - - fn load_owned(specifier: &str) -> Result; -} - -pub fn load_dir(specifier: &str) -> Result, Error> { - Ok(ASSETS.load_dir(specifier)?) -} - -impl AssetExt for T { - fn load(specifier: &str) -> Result, Error> { ASSETS.load(specifier) } - - fn load_owned(specifier: &str) -> Result { ASSETS.load_owned(specifier) } -} - -pub struct Image(pub Arc); - -impl Image { - pub fn to_image(&self) -> Arc { Arc::clone(&self.0) } -} - -pub struct ImageLoader; -impl Loader for ImageLoader { - fn load(content: Cow<[u8]>, _: &str) -> Result { - let image = image::load_from_memory(&content)?; - Ok(Image(Arc::new(image))) - } -} - -impl Asset for Image { - type Loader = ImageLoader; - - const EXTENSIONS: &'static [&'static str] = &["png", "jpg"]; -} - -pub struct DotVoxAsset(pub DotVoxData); - -pub struct DotVoxLoader; -impl Loader for DotVoxLoader { - fn load(content: std::borrow::Cow<[u8]>, _: &str) -> Result { - let data = dot_vox::load_bytes(&content).map_err(|err| err.to_owned())?; - Ok(DotVoxAsset(data)) - } -} - -impl Asset for DotVoxAsset { - type Loader = DotVoxLoader; - - const EXTENSION: &'static str = "vox"; -} - -lazy_static! { - /// Lazy static to find and cache where the asset directory is. - /// Cases we need to account for: - /// 1. Running through airshipper (`assets` next to binary) - /// 2. Install with package manager and run (assets probably in `/usr/share/veloren/assets` while binary in `/usr/bin/`) - /// 3. Download & hopefully extract zip (`assets` next to binary) - /// 4. Running through cargo (`assets` in workspace root but not always in cwd incase you `cd voxygen && cargo r`) - /// 5. Running executable in the target dir (`assets` in workspace) - pub static ref ASSETS_PATH: PathBuf = { - let mut paths = Vec::new(); - - // Note: Ordering matters here! - - // 1. VELOREN_ASSETS environment variable - if let Ok(var) = std::env::var("VELOREN_ASSETS") { - paths.push(var.into()); - } - - // 2. Executable path - if let Ok(mut path) = std::env::current_exe() { - path.pop(); - paths.push(path); - } - - // 3. Working path - if let Ok(path) = std::env::current_dir() { - paths.push(path); - } - - // 4. Cargo Workspace (e.g. local development) - // https://github.com/rust-lang/cargo/issues/3946#issuecomment-359619839 - if let Ok(Ok(path)) = std::env::var("CARGO_MANIFEST_DIR").map(|s| s.parse::()) { - paths.push(path.parent().unwrap().to_path_buf()); - paths.push(path); - } - - // 5. System paths - #[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))] - { - if let Ok(result) = std::env::var("XDG_DATA_HOME") { - paths.push(format!("{}/veloren/", result).into()); - } else if let Ok(result) = std::env::var("HOME") { - paths.push(format!("{}/.local/share/veloren/", result).into()); - } - - if let Ok(result) = std::env::var("XDG_DATA_DIRS") { - result.split(':').for_each(|x| paths.push(format!("{}/veloren/", x).into())); - } else { - // Fallback - let fallback_paths = vec!["/usr/local/share", "/usr/share"]; - for fallback_path in fallback_paths { - paths.push(format!("{}/veloren/", fallback_path).into()); - } - } - } - - tracing::trace!("Possible asset locations paths={:?}", paths); - - for mut path in paths.clone() { - if !path.ends_with("assets") { - path = path.join("assets"); - } - - if path.is_dir() { - tracing::info!("Assets found path={}", path.display()); - return path; - } - } - - panic!( - "Asset directory not found. In attempting to find it, we searched:\n{})", - paths.iter().fold(String::new(), |mut a, path| { - a += &path.to_string_lossy(); - a += "\n"; - a - }), - ); - }; -} - -/// Returns the actual path of the specifier with the extension. -/// -/// For directories, give `""` as extension. -pub fn path_of(specifier: &str, ext: &str) -> PathBuf { ASSETS.source().path_of(specifier, ext) } - -fn get_dir_files(files: &mut Vec, path: &Path, specifier: &str) -> io::Result<()> { - for entry in (fs::read_dir(path)?).flatten() { - let path = entry.path(); - let maybe_stem = path.file_stem().and_then(|stem| stem.to_str()); - - if let Some(stem) = maybe_stem { - let specifier = format!("{}.{}", specifier, stem); - - if path.is_dir() { - get_dir_files(files, &path, &specifier)?; - } else { - files.push(specifier); - } - } - } - - Ok(()) -} - -pub struct Directory(Vec); - -impl Directory { - pub fn iter(&self) -> impl Iterator { self.0.iter() } -} - -impl Compound for Directory { - fn load(_: &AssetCache, specifier: &str) -> Result { - let specifier = specifier.strip_suffix(".*").unwrap_or(specifier); - let root = ASSETS.source().path_of(specifier, ""); - let mut files = Vec::new(); - - get_dir_files(&mut files, &root, specifier)?; - - Ok(Directory(files)) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test_assets_items() { - // TODO: Figure out how to get file name in error so only a single glob is - // needed - - // Separated out into subsections so that error more descriptive - crate::comp::item::Item::new_from_asset_glob("common.items.armor.*") - .expect("Failed to iterate over armors."); - - crate::comp::item::Item::new_from_asset_glob("common.items.boss_drops.*") - .expect("Failed to iterate over boss drops."); - - crate::comp::item::Item::new_from_asset_glob("common.items.consumable.*") - .expect("Failed to iterate over consumables."); - - crate::comp::item::Item::new_from_asset_glob("common.items.crafting_ing.*") - .expect("Failed to iterate over crafting ingredients."); - - crate::comp::item::Item::new_from_asset_glob("common.items.crafting_tools.*") - .expect("Failed to iterate over crafting tools."); - - crate::comp::item::Item::new_from_asset_glob("common.items.debug.*") - .expect("Failed to iterate over debug items."); - - crate::comp::item::Item::new_from_asset_glob("common.items.flowers.*") - .expect("Failed to iterate over flower items."); - - crate::comp::item::Item::new_from_asset_glob("common.items.food.*") - .expect("Failed to iterate over food items."); - - crate::comp::item::Item::new_from_asset_glob("common.items.glider.*") - .expect("Failed to iterate over gliders."); - - crate::comp::item::Item::new_from_asset_glob("common.items.grasses.*") - .expect("Failed to iterate over grasses."); - - crate::comp::item::Item::new_from_asset_glob("common.items.lantern.*") - .expect("Failed to iterate over lanterns."); - - crate::comp::item::Item::new_from_asset_glob("common.items.npc_armor.*") - .expect("Failed to iterate over npc armors."); - - crate::comp::item::Item::new_from_asset_glob("common.items.npc_weapons.*") - .expect("Failed to iterate over npc weapons."); - - crate::comp::item::Item::new_from_asset_glob("common.items.ore.*") - .expect("Failed to iterate over ores."); - - crate::comp::item::Item::new_from_asset_glob("common.items.tag_examples.*") - .expect("Failed to iterate over tag examples."); - - crate::comp::item::Item::new_from_asset_glob("common.items.testing.*") - .expect("Failed to iterate over testing items."); - - crate::comp::item::Item::new_from_asset_glob("common.items.utility.*") - .expect("Failed to iterate over utility items."); - - // Checks each weapon type to allow errors to be located more easily - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.axe.*") - .expect("Failed to iterate over axes."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.axe_1h.*") - .expect("Failed to iterate over 1h axes."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.bow.*") - .expect("Failed to iterate over bows."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.dagger.*") - .expect("Failed to iterate over daggers."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.empty.*") - .expect("Failed to iterate over empty."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.hammer.*") - .expect("Failed to iterate over hammers."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.hammer_1h.*") - .expect("Failed to iterate over 1h hammers."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.sceptre.*") - .expect("Failed to iterate over sceptres."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.shield.*") - .expect("Failed to iterate over shields."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.staff.*") - .expect("Failed to iterate over staffs."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.sword.*") - .expect("Failed to iterate over swords."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.sword_1h.*") - .expect("Failed to iterate over 1h swords."); - - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.tool.*") - .expect("Failed to iterate over tools."); - - // Checks all weapons should more weapons be added later - crate::comp::item::Item::new_from_asset_glob("common.items.weapons.*") - .expect("Failed to iterate over weapons."); - - // Final at the end to account for a new folder being added - crate::comp::item::Item::new_from_asset_glob("common.items.*") - .expect("Failed to iterate over item folders."); - } -} diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index c7d5d4f841..83b07c9e0b 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -407,8 +407,8 @@ impl Deref for Item { } impl assets::Compound for ItemDef { - fn load( - cache: &assets_manager::AssetCache, + fn load( + cache: &assets::AssetCache, specifier: &str, ) -> Result { // load from the filesystem first, but if the file doesn't exist, see if it's a @@ -853,3 +853,111 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn tags(&self) -> &[ItemTag] { (*self).tags() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_assets_items() { + // TODO: Figure out how to get file name in error so only a single glob is + // needed + + // Separated out into subsections so that error more descriptive + Item::new_from_asset_glob("common.items.armor.*").expect("Failed to iterate over armors."); + + Item::new_from_asset_glob("common.items.boss_drops.*") + .expect("Failed to iterate over boss drops."); + + Item::new_from_asset_glob("common.items.consumable.*") + .expect("Failed to iterate over consumables."); + + Item::new_from_asset_glob("common.items.crafting_ing.*") + .expect("Failed to iterate over crafting ingredients."); + + Item::new_from_asset_glob("common.items.crafting_tools.*") + .expect("Failed to iterate over crafting tools."); + + Item::new_from_asset_glob("common.items.debug.*") + .expect("Failed to iterate over debug items."); + + Item::new_from_asset_glob("common.items.flowers.*") + .expect("Failed to iterate over flower items."); + + Item::new_from_asset_glob("common.items.food.*") + .expect("Failed to iterate over food items."); + + Item::new_from_asset_glob("common.items.glider.*") + .expect("Failed to iterate over gliders."); + + Item::new_from_asset_glob("common.items.grasses.*") + .expect("Failed to iterate over grasses."); + + Item::new_from_asset_glob("common.items.lantern.*") + .expect("Failed to iterate over lanterns."); + + Item::new_from_asset_glob("common.items.npc_armor.*") + .expect("Failed to iterate over npc armors."); + + Item::new_from_asset_glob("common.items.npc_weapons.*") + .expect("Failed to iterate over npc weapons."); + + Item::new_from_asset_glob("common.items.ore.*").expect("Failed to iterate over ores."); + + Item::new_from_asset_glob("common.items.tag_examples.*") + .expect("Failed to iterate over tag examples."); + + Item::new_from_asset_glob("common.items.testing.*") + .expect("Failed to iterate over testing items."); + + Item::new_from_asset_glob("common.items.utility.*") + .expect("Failed to iterate over utility items."); + + // Checks each weapon type to allow errors to be located more easily + Item::new_from_asset_glob("common.items.weapons.axe.*") + .expect("Failed to iterate over axes."); + + Item::new_from_asset_glob("common.items.weapons.axe_1h.*") + .expect("Failed to iterate over 1h axes."); + + Item::new_from_asset_glob("common.items.weapons.bow.*") + .expect("Failed to iterate over bows."); + + Item::new_from_asset_glob("common.items.weapons.dagger.*") + .expect("Failed to iterate over daggers."); + + Item::new_from_asset_glob("common.items.weapons.empty.*") + .expect("Failed to iterate over empty."); + + Item::new_from_asset_glob("common.items.weapons.hammer.*") + .expect("Failed to iterate over hammers."); + + Item::new_from_asset_glob("common.items.weapons.hammer_1h.*") + .expect("Failed to iterate over 1h hammers."); + + Item::new_from_asset_glob("common.items.weapons.sceptre.*") + .expect("Failed to iterate over sceptres."); + + Item::new_from_asset_glob("common.items.weapons.shield.*") + .expect("Failed to iterate over shields."); + + Item::new_from_asset_glob("common.items.weapons.staff.*") + .expect("Failed to iterate over staffs."); + + Item::new_from_asset_glob("common.items.weapons.sword.*") + .expect("Failed to iterate over swords."); + + Item::new_from_asset_glob("common.items.weapons.sword_1h.*") + .expect("Failed to iterate over 1h swords."); + + Item::new_from_asset_glob("common.items.weapons.tool.*") + .expect("Failed to iterate over tools."); + + // Checks all weapons should more weapons be added later + Item::new_from_asset_glob("common.items.weapons.*") + .expect("Failed to iterate over weapons."); + + // Final at the end to account for a new folder being added + Item::new_from_asset_glob("common.items.*").expect("Failed to iterate over item folders."); + } +} diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 3f4053b9ad..50f62365d5 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -392,8 +392,8 @@ impl Asset for AbilityMap { } impl assets::Compound for AbilityMap { - fn load( - cache: &assets_manager::AssetCache, + fn load( + cache: &assets::AssetCache, specifier: &str, ) -> Result { let manifest = cache.load::>(specifier)?.read(); diff --git a/common/src/comp/inventory/trade_pricing.rs b/common/src/comp/inventory/trade_pricing.rs index e1ba75bd74..78b9fad9b6 100644 --- a/common/src/comp/inventory/trade_pricing.rs +++ b/common/src/comp/inventory/trade_pricing.rs @@ -4,7 +4,7 @@ use crate::{ recipe::{default_recipe_book, RecipeInput}, trade::Good, }; -use assets_manager::AssetGuard; +use assets::AssetGuard; use hashbrown::HashMap; use lazy_static::lazy_static; use serde::Deserialize; diff --git a/common/src/lib.rs b/common/src/lib.rs index a3792863a7..c4a89e1738 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -23,7 +23,7 @@ pub use uuid; // modules #[cfg(not(target_arch = "wasm32"))] -pub mod assets; +pub use assets; #[cfg(not(target_arch = "wasm32"))] pub mod astar; #[cfg(not(target_arch = "wasm32"))] mod cached_spatial_grid; diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 68af4728b0..31bf2cfbfd 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -108,10 +108,10 @@ impl assets::Asset for RawRecipeBook { } impl assets::Compound for RecipeBook { - fn load( - cache: &assets_manager::AssetCache, + fn load( + cache: &assets::AssetCache, specifier: &str, - ) -> Result { + ) -> Result { #[inline] fn load_item_def(spec: &(String, u32)) -> Result<(Arc, u32), assets::Error> { let def = Arc::::load_cloned(&spec.0)?; diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index c9b12d8d0c..14a658b43a 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -60,8 +60,8 @@ impl std::ops::Deref for StructuresGroup { } impl assets::Compound for StructuresGroup { - fn load( - cache: &assets_manager::AssetCache, + fn load( + cache: &assets::AssetCache, specifier: &str, ) -> Result { let specs = cache.load::(specifier)?.read(); @@ -118,8 +118,8 @@ impl ReadVol for Structure { } impl assets::Compound for BaseStructure { - fn load( - cache: &assets_manager::AssetCache, + fn load( + cache: &assets::AssetCache, specifier: &str, ) -> Result { let dot_vox_data = cache.load::(specifier)?.read(); diff --git a/voxygen/i18n/Cargo.toml b/voxygen/i18n/Cargo.toml index eb97136baa..ab71564d1c 100644 --- a/voxygen/i18n/Cargo.toml +++ b/voxygen/i18n/Cargo.toml @@ -9,12 +9,13 @@ version = "0.9.0" name = "i18n-check" [dependencies] -lazy_static = "1.4.0" +# Assets hashbrown = { version = "0.9", features = ["serde", "nightly"] } -assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]} +common-assets = {package = "veloren-common-assets", path = "../../common/assets"} deunicode = "1.0" -ron = "0.6" serde = { version = "1.0", features = ["derive"] } -tracing = "0.1" # Diagnostic git2 = "0.13" +ron = "0.6" + +tracing = "0.1" diff --git a/voxygen/i18n/src/i18n.rs b/voxygen/i18n/src/i18n.rs index 71f388a0e3..fe1dcfdfb4 100644 --- a/voxygen/i18n/src/i18n.rs +++ b/voxygen/i18n/src/i18n.rs @@ -172,7 +172,9 @@ impl assets::Compound for Language { asset_key: &str, ) -> Result { let raw = cache - .load::(&[asset_key, ".", LANG_MANIFEST_FILE].concat())? + .load::( + &["voxygen.i18n.", asset_key, ".", LANG_MANIFEST_FILE].concat(), + )? .cloned(); let mut localization = Language::from(raw); @@ -333,7 +335,7 @@ impl LocalizationHandle { } pub fn load(specifier: &str) -> Result { - let default_key = i18n_asset_key(REFERENCE_LANG); + let default_key = REFERENCE_LANG; let is_default = specifier == default_key; Ok(Self { active: Language::load(specifier)?, @@ -381,12 +383,10 @@ impl assets::Compound for LocalizationList { } /// Load all the available languages located in the voxygen asset directory -pub fn list_localizations() -> Vec { - LocalizationList::load_expect_cloned("voxygen.i18n").0 -} +pub fn list_localizations() -> Vec { LocalizationList::load_expect_cloned("").0 } -/// Return the asset associated with the language_id -pub fn i18n_asset_key(language_id: &str) -> String { ["voxygen.i18n.", language_id].concat() } +/// Start hot reloading of i18n assets +pub fn start_hot_reloading() { assets::start_hot_reloading(); } #[cfg(test)] mod tests { diff --git a/voxygen/i18n/src/lib.rs b/voxygen/i18n/src/lib.rs index fbd2b1e1ed..9b0d48cfea 100644 --- a/voxygen/i18n/src/lib.rs +++ b/voxygen/i18n/src/lib.rs @@ -1,5 +1,5 @@ pub mod analysis; -mod assets; mod i18n; +use common_assets as assets; pub use i18n::*; diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 63ba0d7c48..89ad6e7b9d 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -5,7 +5,7 @@ use veloren_voxygen::{ audio::AudioFrontend, - i18n::{self, i18n_asset_key, LocalizationHandle}, + i18n::{self, LocalizationHandle}, profile::Profile, run, scene::terrain::SpriteRenderContext, @@ -144,6 +144,7 @@ fn main() { })); assets::start_hot_reloading(); + i18n::start_hot_reloading(); // Initialise watcher for animation hotreloading #[cfg(feature = "hot-anim")] @@ -163,8 +164,8 @@ fn main() { // Load the profile. let profile = Profile::load(); - let mut i18n = LocalizationHandle::load(&i18n_asset_key(&settings.language.selected_language)) - .unwrap_or_else(|error| { + let mut i18n = + LocalizationHandle::load(&settings.language.selected_language).unwrap_or_else(|error| { let selected_language = &settings.language.selected_language; warn!( ?error, @@ -172,7 +173,7 @@ fn main() { "Impossible to load language: change to the default language (English) instead.", ); settings.language.selected_language = i18n::REFERENCE_LANG.to_owned(); - LocalizationHandle::load_expect(&i18n_asset_key(&settings.language.selected_language)) + LocalizationHandle::load_expect(&settings.language.selected_language) }); i18n.read().log_missing_entries(); i18n.set_english_fallback(settings.language.use_english_fallback); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 642d902871..dac729c725 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -5,7 +5,7 @@ use super::char_selection::CharSelectionState; #[cfg(feature = "singleplayer")] use crate::singleplayer::Singleplayer; use crate::{ - i18n::{i18n_asset_key, Localization, LocalizationHandle}, + i18n::{Localization, LocalizationHandle}, render::Renderer, settings::Settings, window::Event, @@ -295,9 +295,9 @@ impl PlayState for MainMenuState { MainMenuEvent::ChangeLanguage(new_language) => { global_state.settings.language.selected_language = new_language.language_identifier; - global_state.i18n = LocalizationHandle::load_expect(&i18n_asset_key( + global_state.i18n = LocalizationHandle::load_expect( &global_state.settings.language.selected_language, - )); + ); global_state.i18n.read().log_missing_entries(); global_state .i18n diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index c1e674bbcd..9426e798ae 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -5,7 +5,7 @@ use crate::{ BarNumbers, BuffPosition, CrosshairType, Intro, PressBehavior, ScaleChange, ShortcutNumbers, XpBar, }, - i18n::{i18n_asset_key, LanguageMetadata, LocalizationHandle}, + i18n::{LanguageMetadata, LocalizationHandle}, render::RenderMode, settings::{ AudioSettings, ControlSettings, Fps, GamepadSettings, GameplaySettings, GraphicsSettings, @@ -482,9 +482,8 @@ impl SettingsChange { SettingsChange::Language(language_change) => match language_change { Language::ChangeLanguage(new_language) => { settings.language.selected_language = new_language.language_identifier; - global_state.i18n = LocalizationHandle::load_expect(&i18n_asset_key( - &settings.language.selected_language, - )); + global_state.i18n = + LocalizationHandle::load_expect(&settings.language.selected_language); global_state.i18n.read().log_missing_entries(); global_state .i18n