diff --git a/CHANGELOG.md b/CHANGELOG.md index a56c9ac58a..763d648f8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New arena building in desert cities, suitable for PVP, also NPCs like to watch the fights too - The loading screen now displays status updates for singleplayer server and client initialization progress - New Frost Gigas attacks & AI -- Weapons and armor load from plugins +- Allow plugins to add weapon and armor items ### Changed diff --git a/common/assets/Cargo.toml b/common/assets/Cargo.toml index 1730ad25e4..2ab9f976b0 100644 --- a/common/assets/Cargo.toml +++ b/common/assets/Cargo.toml @@ -25,5 +25,4 @@ walkdir = "2.3.2" [features] hot-reloading = ["assets_manager/hot-reloading"] asset_tweak = ["dep:serde", "hot-reloading"] -hashbrown = ["dep:hashbrown"] -plugins = ["dep:serde", "dep:tar"] +plugins = ["dep:serde", "dep:tar", "dep:hashbrown"] diff --git a/common/assets/src/lib.rs b/common/assets/src/lib.rs index f19ecf36a8..3b9442c83a 100644 --- a/common/assets/src/lib.rs +++ b/common/assets/src/lib.rs @@ -245,7 +245,7 @@ impl Concatenate for Vec { } } -#[cfg(feature = "hashbrown")] +#[cfg(feature = "plugins")] impl Concatenate for hashbrown::HashMap { fn concatenate(mut self, b: Self) -> Self { self.extend(b); diff --git a/common/assets/src/plugin_cache.rs b/common/assets/src/plugin_cache.rs index 261d17e981..0c4ee9eb01 100644 --- a/common/assets/src/plugin_cache.rs +++ b/common/assets/src/plugin_cache.rs @@ -14,8 +14,18 @@ struct PluginEntry { cache: AssetCache, } -/// The source combining filesystem and plugins (typically used via -/// CombinedCache) +/// The location of this asset +enum AssetSource { + FileSystem, + Plugin { index: usize }, +} + +struct SourceAndContents<'a>(AssetSource, FileContent<'a>); + +/// This source combines assets loaded from the filesystem and from plugins. +/// It is typically used via the CombinedCache type. +/// +/// A load will search through all sources and warn about unhandled duplicates. pub struct CombinedSource { fs: AssetCache, plugin_list: RwLock>, @@ -31,34 +41,40 @@ impl CombinedSource { } impl CombinedSource { - fn read_multiple(&self, id: &str, ext: &str) -> Vec<(Option, FileContent<'_>)> { + /// Look for an asset in all known sources + fn read_multiple(&self, id: &str, ext: &str) -> Vec> { let mut result = Vec::new(); if let Ok(file_entry) = self.fs.raw_source().read(id, ext) { - result.push((None, file_entry)); + result.push(SourceAndContents(AssetSource::FileSystem, file_entry)); } for (n, p) in self.plugin_list.read().unwrap().iter().enumerate() { if let Ok(entry) = p.cache.raw_source().read(id, ext) { // the data is behind an RwLockReadGuard, so own it for returning - result.push((Some(n), match entry { - FileContent::Slice(s) => FileContent::Buffer(Vec::from(s)), - FileContent::Buffer(b) => FileContent::Buffer(b), - FileContent::Owned(s) => FileContent::Buffer(Vec::from(s.as_ref().as_ref())), - })); + result.push(SourceAndContents( + AssetSource::Plugin { index: n }, + match entry { + FileContent::Slice(s) => FileContent::Buffer(Vec::from(s)), + FileContent::Buffer(b) => FileContent::Buffer(b), + FileContent::Owned(s) => { + FileContent::Buffer(Vec::from(s.as_ref().as_ref())) + }, + }, + )); } } result } - // We don't want to keep the lock, so we clone - fn plugin_path(&self, index: Option) -> Option { - if let Some(index) = index { - self.plugin_list + /// Return the path of a source + fn plugin_path(&self, index: &AssetSource) -> Option { + match index { + AssetSource::FileSystem => Some(ASSETS_PATH.clone()), + AssetSource::Plugin { index } => self.plugin_list .read() .unwrap() - .get(index) - .map(|plugin| plugin.path.clone()) - } else { - None + .get(*index) + // We don't want to keep the lock, so we clone + .map(|plugin| plugin.path.clone()), } } } @@ -71,12 +87,11 @@ impl Source for CombinedSource { Err(std::io::ErrorKind::NotFound.into()) } else { if entries.len() > 1 { - let plugina = self.plugin_path(entries[0].0); - let pluginb = self.plugin_path(entries[1].0); - let patha = plugina.as_ref().unwrap_or(&ASSETS_PATH); - let pathb = pluginb.as_ref().unwrap_or(&ASSETS_PATH); + let patha = self.plugin_path(&entries[0].0); + let pathb = self.plugin_path(&entries[1].0); tracing::error!("Duplicate asset {id} in {patha:?} and {pathb:?}"); } + // unconditionally return the first asset found Ok(entries.swap_remove(0).1) } } @@ -119,7 +134,7 @@ impl CombinedCache { /// Combine objects from filesystem and plugins pub fn combine( &self, - load_from: impl Fn(AnyCache) -> Result, + mut load_from: impl FnMut(AnyCache) -> Result, ) -> Result { let mut result = load_from(self.0.raw_source().fs.as_any_cache()); // Report a severe error from the filesystem asset even if later overwritten by @@ -162,6 +177,8 @@ impl CombinedCache { result } + /// Add a tar archive (a plugin) to the system. + /// All files in that tar file become potential assets. pub fn register_tar(&self, path: PathBuf) -> std::io::Result<()> { let tar_source = Tar::from_path(&path)?; let cache = AssetCache::with_source(tar_source); @@ -175,6 +192,7 @@ impl CombinedCache { } } +// Delegate all cache operations directly to the contained cache object impl std::ops::Deref for CombinedCache { type Target = AssetCache; diff --git a/common/assets/src/tar_source.rs b/common/assets/src/tar_source.rs index cf44e342eb..2cf0e313f7 100644 --- a/common/assets/src/tar_source.rs +++ b/common/assets/src/tar_source.rs @@ -119,7 +119,7 @@ impl Backend { fn read(&self, pos: u64, len: usize) -> std::io::Result> { File::open(self.0.clone()).and_then(|file| { let mut result = vec![0; len]; - file.read_at(result.as_mut_slice(), pos) + file.read_exact_at(result.as_mut_slice(), pos) .map(move |_num_bytes| result) }) } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index ef7c7a4bde..883d1d7adf 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -27,7 +27,7 @@ singleplayer = ["server"] simd = ["vek/platform_intrinsics"] tracy = ["common-frontend/tracy", "client/tracy"] tracy-memory = ["tracy"] # enables heap profiling with tracy -plugins = ["client/plugins", "common-assets/plugins", "common-assets/hashbrown"] +plugins = ["client/plugins", "common-assets/plugins"] egui-ui = ["voxygen-egui", "egui", "egui_wgpu_backend", "egui_winit_platform"] shaderc-from-source = ["shaderc/build-from-source"] discord = ["discord-sdk"] diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index f17d166515..7ff430d1b2 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -178,7 +178,7 @@ macro_rules! make_vox_spec { } } } -macro_rules! concatenate_tuple { +macro_rules! impl_concatenate_for_wrapper { ($name:ty) => { impl Concatenate for $name { fn concatenate(self, b: Self) -> Self { Self(self.0.concatenate(b.0)) } @@ -375,7 +375,7 @@ impl HumHeadSpec { ) } } -concatenate_tuple!(HumHeadSpec); +impl_concatenate_for_wrapper!(HumHeadSpec); // Armor aspects should be in the same order, top to bottom. // These seem overly split up, but wanted to keep the armor seperated @@ -398,43 +398,43 @@ impl Concatenate for ArmorVoxSpecMap { } #[derive(Deserialize)] struct HumArmorShoulderSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorShoulderSpec); +impl_concatenate_for_wrapper!(HumArmorShoulderSpec); #[derive(Deserialize)] struct HumArmorChestSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorChestSpec); +impl_concatenate_for_wrapper!(HumArmorChestSpec); #[derive(Deserialize)] struct HumArmorHandSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorHandSpec); +impl_concatenate_for_wrapper!(HumArmorHandSpec); #[derive(Deserialize)] struct HumArmorBeltSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorBeltSpec); +impl_concatenate_for_wrapper!(HumArmorBeltSpec); #[derive(Deserialize)] struct HumArmorBackSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorBackSpec); +impl_concatenate_for_wrapper!(HumArmorBackSpec); #[derive(Deserialize)] struct HumArmorPantsSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorPantsSpec); +impl_concatenate_for_wrapper!(HumArmorPantsSpec); #[derive(Deserialize)] struct HumArmorFootSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorFootSpec); +impl_concatenate_for_wrapper!(HumArmorFootSpec); #[derive(Deserialize)] struct HumMainWeaponSpec(HashMap); -concatenate_tuple!(HumMainWeaponSpec); +impl_concatenate_for_wrapper!(HumMainWeaponSpec); #[derive(Deserialize)] struct HumModularComponentSpec(HashMap); -concatenate_tuple!(HumModularComponentSpec); +impl_concatenate_for_wrapper!(HumModularComponentSpec); #[derive(Deserialize)] struct HumArmorLanternSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorLanternSpec); +impl_concatenate_for_wrapper!(HumArmorLanternSpec); #[derive(Deserialize)] struct HumArmorGliderSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorGliderSpec); +impl_concatenate_for_wrapper!(HumArmorGliderSpec); #[derive(Deserialize)] struct HumArmorHeadSpec(ArmorVoxSpecMap<(Species, BodyType, String), ArmorVoxSpec>); -concatenate_tuple!(HumArmorHeadSpec); +impl_concatenate_for_wrapper!(HumArmorHeadSpec); #[derive(Deserialize)] struct HumArmorTabardSpec(ArmorVoxSpecMap); -concatenate_tuple!(HumArmorTabardSpec); +impl_concatenate_for_wrapper!(HumArmorTabardSpec); make_vox_spec!( Body, @@ -1105,7 +1105,7 @@ fn mesh_hold() -> BoneMeshes { ////// #[derive(Deserialize)] struct QuadrupedSmallCentralSpec(HashMap<(QSSpecies, QSBodyType), SidedQSCentralVoxSpec>); -concatenate_tuple!(QuadrupedSmallCentralSpec); +impl_concatenate_for_wrapper!(QuadrupedSmallCentralSpec); #[derive(Deserialize)] struct SidedQSCentralVoxSpec { @@ -1123,7 +1123,7 @@ struct QuadrupedSmallCentralSubSpec { #[derive(Deserialize)] struct QuadrupedSmallLateralSpec(HashMap<(QSSpecies, QSBodyType), SidedQSLateralVoxSpec>); -concatenate_tuple!(QuadrupedSmallLateralSpec); +impl_concatenate_for_wrapper!(QuadrupedSmallLateralSpec); #[derive(Deserialize)] struct SidedQSLateralVoxSpec { @@ -1322,7 +1322,7 @@ impl QuadrupedSmallLateralSpec { ////// #[derive(Deserialize)] struct QuadrupedMediumCentralSpec(HashMap<(QMSpecies, QMBodyType), SidedQMCentralVoxSpec>); -concatenate_tuple!(QuadrupedMediumCentralSpec); +impl_concatenate_for_wrapper!(QuadrupedMediumCentralSpec); #[derive(Deserialize)] struct SidedQMCentralVoxSpec { @@ -1344,7 +1344,7 @@ struct QuadrupedMediumCentralSubSpec { #[derive(Deserialize)] struct QuadrupedMediumLateralSpec(HashMap<(QMSpecies, QMBodyType), SidedQMLateralVoxSpec>); -concatenate_tuple!(QuadrupedMediumLateralSpec); +impl_concatenate_for_wrapper!(QuadrupedMediumLateralSpec); #[derive(Deserialize)] struct SidedQMLateralVoxSpec { leg_fl: QuadrupedMediumLateralSubSpec, @@ -1700,7 +1700,7 @@ impl QuadrupedMediumLateralSpec { ////// #[derive(Deserialize)] struct BirdMediumCentralSpec(HashMap<(BMSpecies, BMBodyType), SidedBMCentralVoxSpec>); -concatenate_tuple!(BirdMediumCentralSpec); +impl_concatenate_for_wrapper!(BirdMediumCentralSpec); #[derive(Deserialize)] struct SidedBMCentralVoxSpec { @@ -1718,7 +1718,7 @@ struct BirdMediumCentralSubSpec { #[derive(Deserialize)] struct BirdMediumLateralSpec(HashMap<(BMSpecies, BMBodyType), SidedBMLateralVoxSpec>); -concatenate_tuple!(BirdMediumLateralSpec); +impl_concatenate_for_wrapper!(BirdMediumLateralSpec); #[derive(Deserialize)] struct SidedBMLateralVoxSpec { @@ -1956,7 +1956,7 @@ impl BirdMediumLateralSpec { ////// #[derive(Deserialize)] struct TheropodCentralSpec(HashMap<(TSpecies, TBodyType), SidedTCentralVoxSpec>); -concatenate_tuple!(TheropodCentralSpec); +impl_concatenate_for_wrapper!(TheropodCentralSpec); #[derive(Deserialize)] struct SidedTCentralVoxSpec { @@ -1977,7 +1977,7 @@ struct TheropodCentralSubSpec { } #[derive(Deserialize)] struct TheropodLateralSpec(HashMap<(TSpecies, TBodyType), SidedTLateralVoxSpec>); -concatenate_tuple!(TheropodLateralSpec); +impl_concatenate_for_wrapper!(TheropodLateralSpec); #[derive(Deserialize)] struct SidedTLateralVoxSpec { @@ -2288,7 +2288,7 @@ impl TheropodLateralSpec { ////// #[derive(Deserialize)] struct ArthropodCentralSpec(HashMap<(ASpecies, ABodyType), SidedACentralVoxSpec>); -concatenate_tuple!(ArthropodCentralSpec); +impl_concatenate_for_wrapper!(ArthropodCentralSpec); #[derive(Deserialize)] struct SidedACentralVoxSpec { @@ -2304,7 +2304,7 @@ struct ArthropodCentralSubSpec { } #[derive(Deserialize)] struct ArthropodLateralSpec(HashMap<(ASpecies, ABodyType), SidedALateralVoxSpec>); -concatenate_tuple!(ArthropodLateralSpec); +impl_concatenate_for_wrapper!(ArthropodLateralSpec); #[derive(Deserialize)] struct SidedALateralVoxSpec { @@ -2690,7 +2690,7 @@ impl ArthropodLateralSpec { ////// #[derive(Deserialize)] struct FishMediumCentralSpec(HashMap<(FMSpecies, FMBodyType), SidedFMCentralVoxSpec>); -concatenate_tuple!(FishMediumCentralSpec); +impl_concatenate_for_wrapper!(FishMediumCentralSpec); #[derive(Deserialize)] struct SidedFMCentralVoxSpec { @@ -2709,7 +2709,7 @@ struct FishMediumCentralSubSpec { } #[derive(Deserialize)] struct FishMediumLateralSpec(HashMap<(FMSpecies, FMBodyType), SidedFMLateralVoxSpec>); -concatenate_tuple!(FishMediumLateralSpec); +impl_concatenate_for_wrapper!(FishMediumLateralSpec); #[derive(Deserialize)] struct SidedFMLateralVoxSpec { fin_l: FishMediumLateralSubSpec, @@ -2898,7 +2898,7 @@ impl FishMediumLateralSpec { ////// #[derive(Deserialize)] struct FishSmallCentralSpec(HashMap<(FSSpecies, FSBodyType), SidedFSCentralVoxSpec>); -concatenate_tuple!(FishSmallCentralSpec); +impl_concatenate_for_wrapper!(FishSmallCentralSpec); #[derive(Deserialize)] struct SidedFSCentralVoxSpec { @@ -2914,7 +2914,7 @@ struct FishSmallCentralSubSpec { } #[derive(Deserialize)] struct FishSmallLateralSpec(HashMap<(FSSpecies, FSBodyType), SidedFSLateralVoxSpec>); -concatenate_tuple!(FishSmallLateralSpec); +impl_concatenate_for_wrapper!(FishSmallLateralSpec); #[derive(Deserialize)] struct SidedFSLateralVoxSpec { fin_l: FishSmallLateralSubSpec, @@ -3045,25 +3045,25 @@ impl FishSmallLateralSpec { #[derive(Deserialize)] struct BipedSmallWeaponSpec(HashMap); -concatenate_tuple!(BipedSmallWeaponSpec); +impl_concatenate_for_wrapper!(BipedSmallWeaponSpec); #[derive(Deserialize)] struct BipedSmallArmorHeadSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorHeadSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorHeadSpec); #[derive(Deserialize)] struct BipedSmallArmorHandSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorHandSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorHandSpec); #[derive(Deserialize)] struct BipedSmallArmorFootSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorFootSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorFootSpec); #[derive(Deserialize)] struct BipedSmallArmorChestSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorChestSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorChestSpec); #[derive(Deserialize)] struct BipedSmallArmorPantsSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorPantsSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorPantsSpec); #[derive(Deserialize)] struct BipedSmallArmorTailSpec(ArmorVoxSpecMap); -concatenate_tuple!(BipedSmallArmorTailSpec); +impl_concatenate_for_wrapper!(BipedSmallArmorTailSpec); make_vox_spec!( biped_small::Body, struct BipedSmallSpec { @@ -3327,7 +3327,7 @@ impl BipedSmallWeaponSpec { ////// #[derive(Deserialize)] struct DragonCentralSpec(HashMap<(DSpecies, DBodyType), SidedDCentralVoxSpec>); -concatenate_tuple!(DragonCentralSpec); +impl_concatenate_for_wrapper!(DragonCentralSpec); #[derive(Deserialize)] struct SidedDCentralVoxSpec { @@ -3349,7 +3349,7 @@ struct DragonCentralSubSpec { #[derive(Deserialize)] struct DragonLateralSpec(HashMap<(DSpecies, DBodyType), SidedDLateralVoxSpec>); -concatenate_tuple!(DragonLateralSpec); +impl_concatenate_for_wrapper!(DragonLateralSpec); #[derive(Deserialize)] struct SidedDLateralVoxSpec { @@ -3700,7 +3700,7 @@ impl DragonLateralSpec { ////// #[derive(Deserialize)] struct BirdLargeCentralSpec(HashMap<(BLASpecies, BLABodyType), SidedBLACentralVoxSpec>); -concatenate_tuple!(BirdLargeCentralSpec); +impl_concatenate_for_wrapper!(BirdLargeCentralSpec); #[derive(Deserialize)] struct SidedBLACentralVoxSpec { @@ -3721,7 +3721,7 @@ struct BirdLargeCentralSubSpec { #[derive(Deserialize)] struct BirdLargeLateralSpec(HashMap<(BLASpecies, BLABodyType), SidedBLALateralVoxSpec>); -concatenate_tuple!(BirdLargeLateralSpec); +impl_concatenate_for_wrapper!(BirdLargeLateralSpec); #[derive(Deserialize)] struct SidedBLALateralVoxSpec { @@ -4105,7 +4105,7 @@ impl BirdLargeLateralSpec { ////// #[derive(Deserialize)] struct BipedLargeCentralSpec(HashMap<(BLSpecies, BLBodyType), SidedBLCentralVoxSpec>); -concatenate_tuple!(BipedLargeCentralSpec); +impl_concatenate_for_wrapper!(BipedLargeCentralSpec); #[derive(Deserialize)] struct SidedBLCentralVoxSpec { @@ -4125,7 +4125,7 @@ struct BipedLargeCentralSubSpec { #[derive(Deserialize)] struct BipedLargeLateralSpec(HashMap<(BLSpecies, BLBodyType), SidedBLLateralVoxSpec>); -concatenate_tuple!(BipedLargeLateralSpec); +impl_concatenate_for_wrapper!(BipedLargeLateralSpec); #[derive(Deserialize)] struct SidedBLLateralVoxSpec { @@ -4147,10 +4147,10 @@ struct BipedLargeLateralSubSpec { } #[derive(Deserialize)] struct BipedLargeMainSpec(HashMap); -concatenate_tuple!(BipedLargeMainSpec); +impl_concatenate_for_wrapper!(BipedLargeMainSpec); #[derive(Deserialize)] struct BipedLargeSecondSpec(HashMap); -concatenate_tuple!(BipedLargeSecondSpec); +impl_concatenate_for_wrapper!(BipedLargeSecondSpec); make_vox_spec!( biped_large::Body, struct BipedLargeSpec { @@ -4528,7 +4528,7 @@ impl BipedLargeSecondSpec { ////// #[derive(Deserialize)] struct GolemCentralSpec(HashMap<(GSpecies, GBodyType), SidedGCentralVoxSpec>); -concatenate_tuple!(GolemCentralSpec); +impl_concatenate_for_wrapper!(GolemCentralSpec); #[derive(Deserialize)] struct SidedGCentralVoxSpec { @@ -4547,7 +4547,7 @@ struct GolemCentralSubSpec { #[derive(Deserialize)] struct GolemLateralSpec(HashMap<(GSpecies, GBodyType), SidedGLateralVoxSpec>); -concatenate_tuple!(GolemLateralSpec); +impl_concatenate_for_wrapper!(GolemLateralSpec); #[derive(Deserialize)] struct SidedGLateralVoxSpec { @@ -4840,7 +4840,7 @@ impl GolemLateralSpec { ////// #[derive(Deserialize)] struct QuadrupedLowCentralSpec(HashMap<(QLSpecies, QLBodyType), SidedQLCentralVoxSpec>); -concatenate_tuple!(QuadrupedLowCentralSpec); +impl_concatenate_for_wrapper!(QuadrupedLowCentralSpec); #[derive(Deserialize)] struct SidedQLCentralVoxSpec { @@ -4861,7 +4861,7 @@ struct QuadrupedLowCentralSubSpec { #[derive(Deserialize)] struct QuadrupedLowLateralSpec(HashMap<(QLSpecies, QLBodyType), SidedQLLateralVoxSpec>); -concatenate_tuple!(QuadrupedLowLateralSpec); +impl_concatenate_for_wrapper!(QuadrupedLowLateralSpec); #[derive(Deserialize)] struct SidedQLLateralVoxSpec { front_left: QuadrupedLowLateralSubSpec, @@ -5191,7 +5191,7 @@ impl ObjectCentralSpec { (central, Vec3::from(spec.bone1.offset)) } } -concatenate_tuple!(ObjectCentralSpec); +impl_concatenate_for_wrapper!(ObjectCentralSpec); struct ModelWithOptionalIndex(String, u32); @@ -5229,7 +5229,7 @@ impl<'de> Deserialize<'de> for ModelWithOptionalIndex { #[derive(Deserialize)] struct ItemDropCentralSpec(HashMap); -concatenate_tuple!(ItemDropCentralSpec); +impl_concatenate_for_wrapper!(ItemDropCentralSpec); make_vox_spec!( item_drop::Body,