feat(assets): implement virtual AssetManager with non-destructive mod overlays

This commit is contained in:
katR 2026-10-01 19:34:14 +05:30
parent f6e7bbd7a5
commit 1969f7aae9
3 changed files with 87 additions and 0 deletions

View file

@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use super::asset::AssetFile;
use super::dir::AssetDir;
use std::collections::HashMap;
pub struct AssetManager {
base_assets: HashMap<String, Vec<u8>>,
mod_overrides: HashMap<String, Vec<u8>>,
}
impl Default for AssetManager {
fn default() -> Self {
Self::new()
}
}
impl AssetManager {
pub fn new() -> Self {
Self {
base_assets: HashMap::new(),
mod_overrides: HashMap::new(),
}
}
pub fn add_asset(&mut self, path: &str, data: Vec<u8>) {
self.base_assets.insert(path.to_string(), data);
}
pub fn add_mod_override(&mut self, path: &str, data: Vec<u8>) {
self.mod_overrides.insert(path.to_string(), data);
}
pub fn open(&self, filename: &str) -> Option<AssetFile> {
// 1. Check user mod overrides first (non-destructive mod engine)
if let Some(data) = self.mod_overrides.get(filename) {
tracing::info!("AssetManager: redirected '{}' to active user mod", filename);
return Some(AssetFile::new(data.clone()));
}
// 2. Fallback to base asset
if let Some(data) = self.base_assets.get(filename) {
return Some(AssetFile::new(data.clone()));
}
tracing::warn!("AssetManager: asset '{}' not found", filename);
None
}
pub fn open_dir(&self, dir_name: &str) -> AssetDir {
let prefix = if dir_name.is_empty() || dir_name == "." {
"".to_string()
} else if !dir_name.ends_with('/') {
format!("{}/", dir_name)
} else {
dir_name.to_string()
};
let mut files = Vec::new();
for key in self.base_assets.keys().chain(self.mod_overrides.keys()) {
if key.starts_with(&prefix) {
let rest = &key[prefix.len()..];
if !rest.contains('/') {
files.push(rest.to_string());
}
}
}
files.sort();
files.dedup();
AssetDir::new(files)
}
}

View file

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
pub mod asset; pub mod asset;
pub mod dir; pub mod dir;
pub mod manager;
pub mod types; pub mod types;

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_core::assets::manager::AssetManager;
#[test]
fn test_asset_manager_open_with_mod_override() {
let mut mgr = AssetManager::new();
mgr.add_asset("content/sounds/ouch.ogg", b"BASE_SOUND".to_vec());
mgr.add_mod_override("content/sounds/ouch.ogg", b"CUSTOM_MOD_SOUND".to_vec());
let mut asset = mgr.open("content/sounds/ouch.ogg").expect("Should open asset");
let mut content = vec![0u8; asset.length() as usize];
asset.read(&mut content);
assert_eq!(&content, b"CUSTOM_MOD_SOUND", "Must redirect to user mod bytes");
}