feat(bootstrap): implement non-destructive mod overlay manager
This commit is contained in:
parent
72dcee25a8
commit
f209c7ad64
3 changed files with 97 additions and 0 deletions
|
|
@ -3,3 +3,4 @@ pub mod apk;
|
|||
pub mod cdn;
|
||||
pub mod error;
|
||||
pub mod fflags;
|
||||
pub mod mods;
|
||||
|
|
|
|||
65
crates/molotov-bootstrap/src/mods.rs
Normal file
65
crates/molotov-bootstrap/src/mods.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::error::MolotovError;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ModType {
|
||||
DeathSound,
|
||||
Cursor,
|
||||
Font,
|
||||
}
|
||||
|
||||
pub struct ModManager {
|
||||
mods_root: PathBuf,
|
||||
active_overlays: HashMap<String, PathBuf>,
|
||||
}
|
||||
|
||||
impl ModManager {
|
||||
pub fn new(mods_root: PathBuf) -> Self {
|
||||
Self {
|
||||
mods_root,
|
||||
active_overlays: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_installed_mods(&mut self) -> Result<(), MolotovError> {
|
||||
if !self.mods_root.exists() {
|
||||
std::fs::create_dir_all(&self.mods_root)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Custom death sound check
|
||||
let death_sound = self.mods_root.join("ouch.ogg");
|
||||
if death_sound.exists() {
|
||||
self.active_overlays
|
||||
.insert("content/sounds/ouch.ogg".to_string(), death_sound);
|
||||
}
|
||||
|
||||
// Custom cursor check
|
||||
let cursor = self.mods_root.join("ArrowCursor.png");
|
||||
if cursor.exists() {
|
||||
self.active_overlays.insert(
|
||||
"content/textures/Cursors/KeyboardMouse/ArrowCursor.png".to_string(),
|
||||
cursor,
|
||||
);
|
||||
}
|
||||
|
||||
// Custom font check
|
||||
let font = self.mods_root.join("custom_font.ttf");
|
||||
if font.exists() {
|
||||
self.active_overlays
|
||||
.insert("content/fonts/arial.ttf".to_string(), font);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resolve_asset_path<'a>(&'a self, relative_asset_path: &'a str) -> PathBuf {
|
||||
if let Some(custom_path) = self.active_overlays.get(relative_asset_path) {
|
||||
custom_path.clone()
|
||||
} else {
|
||||
PathBuf::from(relative_asset_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
31
crates/molotov-bootstrap/tests/mods_test.rs
Normal file
31
crates/molotov-bootstrap/tests/mods_test.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use molotov_bootstrap::mods::ModManager;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn test_mod_redirection_for_custom_death_sound() {
|
||||
let temp = tempdir().unwrap();
|
||||
let mod_dir = temp.path().join("mods");
|
||||
std::fs::create_dir_all(&mod_dir).unwrap();
|
||||
|
||||
let custom_sound = mod_dir.join("ouch.ogg");
|
||||
std::fs::write(&custom_sound, b"OGG_CUSTOM_DATA").unwrap();
|
||||
|
||||
let mut manager = ModManager::new(mod_dir);
|
||||
manager.scan_installed_mods().unwrap();
|
||||
|
||||
let default_asset = "content/sounds/ouch.ogg";
|
||||
let resolved = manager.resolve_asset_path(default_asset);
|
||||
|
||||
assert_eq!(resolved, custom_sound, "Death sound must be redirected to user mod");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unmodded_asset_passes_through() {
|
||||
let temp = tempdir().unwrap();
|
||||
let manager = ModManager::new(temp.path().to_path_buf());
|
||||
let default_asset = "content/textures/unmodified_texture.png";
|
||||
let resolved = manager.resolve_asset_path(default_asset);
|
||||
|
||||
assert_eq!(resolved.to_str().unwrap(), default_asset);
|
||||
}
|
||||
Reference in a new issue