From f73f52605f48baceeaf3a344781fc6b5e337e0ed Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Oct 2023 23:56:19 +0200 Subject: [PATCH] Support recipes and item images from plugins --- CHANGELOG.md | 1 + common/assets/src/lib.rs | 51 ++++++++++++++++++++++++++++++++++++ common/src/recipe.rs | 17 +++++++++--- voxygen/src/hud/item_imgs.rs | 7 +++-- 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d4304c542..be6d4a43b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New Frost Gigas attacks & AI - Allow plugins to add weapon and armor items - New voxelised LoD shader effect +- Allow plugins to add recipes and item images ### Changed diff --git a/common/assets/src/lib.rs b/common/assets/src/lib.rs index f2e7eb9ece..b949f66536 100644 --- a/common/assets/src/lib.rs +++ b/common/assets/src/lib.rs @@ -1,6 +1,7 @@ //#![warn(clippy::pedantic)] //! Load assets (images or voxel data) from files +use assets_manager::SharedBytes; use dot_vox::DotVoxData; use image::DynamicImage; use lazy_static::lazy_static; @@ -120,6 +121,30 @@ pub trait AssetExt: Sized + Send + Sync + 'static { fn get_or_insert(specifier: &str, default: Self) -> AssetHandle; } +/// Extension to AssetExt to combine Ron files from filesystem and plugins +pub trait AssetCombined: AssetExt { + fn load_and_combine(specifier: &str) -> Result, BoxedError>; + + #[track_caller] + fn load_expect_combined(specifier: &str) -> AssetHandle { + // Avoid using `unwrap_or_else` to avoid breaking `#[track_caller]` + match Self::load_and_combine(specifier) { + Ok(handle) => handle, + Err(err) => { + panic!("Failed loading essential combined asset: {specifier} (error={err:?})") + }, + } + } +} + +/// Extension to AnyCache to combine Ron files from filesystem and plugins +pub trait CacheCombined<'a> { + fn load_and_combine( + self, + id: &str, + ) -> Result, BoxedError>; +} + /// Loads directory and all files in it /// /// # Errors @@ -173,6 +198,32 @@ impl AssetExt for T { } } +impl<'a> CacheCombined<'a> for AnyCache<'a> { + fn load_and_combine( + self, + specifier: &str, + ) -> Result, BoxedError> { + self.get_cached(specifier).map_or_else( + || { + // only create this combined object if is not yet cached + let id_bytes = SharedBytes::from_slice(specifier.as_bytes()); + // as it was created from UTF8 it needs to be valid UTF8 + let id = SharedString::from_utf8(id_bytes).unwrap(); + tracing::info!("combine {specifier}"); + let data: Result = ASSETS.combine(|cache: AnyCache| A::load(cache, &id)); + data.map(|data| self.get_or_insert(specifier, data)) + }, + |obj| Ok(obj), + ) + } +} + +impl AssetCombined for T { + fn load_and_combine(specifier: &str) -> Result, BoxedError> { + ASSETS.as_any_cache().load_and_combine(specifier) + } +} + pub struct Image(pub Arc); impl Image { diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 77ec90459e..f0e33136de 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1,5 +1,5 @@ use crate::{ - assets::{self, AssetExt, AssetHandle}, + assets::{self, AssetExt, AssetHandle, CacheCombined, Concatenate}, comp::{ inventory::slot::{InvSlotId, Slot}, item::{ @@ -504,6 +504,9 @@ impl assets::Asset for RawRecipeBook { const EXTENSION: &'static str = "ron"; } +impl Concatenate for RawRecipeBook { + fn concatenate(self, b: Self) -> Self { RawRecipeBook(self.0.concatenate(b.0)) } +} #[derive(Deserialize, Clone)] struct ItemList(Vec); @@ -513,6 +516,9 @@ impl assets::Asset for ItemList { const EXTENSION: &'static str = "ron"; } +impl Concatenate for ItemList { + fn concatenate(self, b: Self) -> Self { ItemList(self.0.concatenate(b.0)) } +} impl assets::Compound for RecipeBook { fn load( @@ -531,7 +537,7 @@ impl assets::Compound for RecipeBook { Ok((def, *amount, *is_mod_comp)) } - let raw = cache.load::(specifier)?.cloned(); + let raw = cache.load_and_combine::(specifier)?.cloned(); let recipes = raw .0 @@ -596,6 +602,9 @@ impl assets::Asset for RawComponentRecipeBook { const EXTENSION: &'static str = "ron"; } +impl Concatenate for RawComponentRecipeBook { + fn concatenate(self, b: Self) -> Self { RawComponentRecipeBook(self.0.concatenate(b.0)) } +} #[derive(Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)] pub struct ComponentKey { @@ -884,7 +893,9 @@ impl assets::Compound for ComponentRecipeBook { }) } - let raw = cache.load::(specifier)?.cloned(); + let raw = cache + .load_and_combine::(specifier)? + .cloned(); let recipes = raw .0 diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index ef5ab60de5..a8bd1db563 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -1,6 +1,6 @@ use crate::ui::{Graphic, SampleStrat, Transform, Ui}; use common::{ - assets::{self, AssetExt, AssetHandle, DotVoxAsset, ReloadWatcher}, + assets::{self, AssetCombined, AssetExt, AssetHandle, Concatenate, DotVoxAsset, ReloadWatcher}, comp::item::item_key::ItemKey, figure::Segment, }; @@ -62,6 +62,9 @@ impl assets::Asset for ItemImagesSpec { const EXTENSION: &'static str = "ron"; } +impl Concatenate for ItemImagesSpec { + fn concatenate(self, b: Self) -> Self { ItemImagesSpec(self.0.concatenate(b.0)) } +} // TODO: when there are more images don't load them all into memory pub struct ItemImgs { @@ -73,7 +76,7 @@ pub struct ItemImgs { impl ItemImgs { pub fn new(ui: &mut Ui, not_found: Id) -> Self { - let manifest = ItemImagesSpec::load_expect("voxygen.item_image_manifest"); + let manifest = ItemImagesSpec::load_expect_combined("voxygen.item_image_manifest"); let map = manifest .read() .0