Remove mod flag

This commit is contained in:
Ethan O'Brien
2026-07-24 21:16:21 -05:00
parent 6eec06e164
commit 59ac1f55c3
4 changed files with 6 additions and 101 deletions

View File

@@ -4,7 +4,6 @@ fn main() -> std::io::Result<()> {
let args = ew::get_args(); let args = ew::get_args();
ew::runtime::update_data_path(&args.path); ew::runtime::update_data_path(&args.path);
ew::runtime::update_masterdata_path(&args.masterdata); ew::runtime::update_masterdata_path(&args.masterdata);
ew::runtime::update_mod_paths(&args.mods);
ew::run_server(false) ew::run_server(false)
} }

View File

@@ -72,10 +72,7 @@ pub struct Args {
pub image_asset_path: String, pub image_asset_path: String,
#[arg(long, default_value = "", help = "Optional directory to load asset lists and master data CSVs from at runtime. Layout mirrors the bundled assets (asset_lists/, csv/, csv-en/). Missing files fall back to the internal copies.")] #[arg(long, default_value = "", help = "Optional directory to load asset lists and master data CSVs from at runtime. Layout mirrors the bundled assets (asset_lists/, csv/, csv-en/). Missing files fall back to the internal copies.")]
pub masterdata: String, pub masterdata: String
#[arg(long = "mod", value_name = "DIR", action = clap::ArgAction::Append, help = "Path to a mod directory layered on top of --masterdata + the bundled defaults. May be passed multiple times. Each mod dir mirrors the masterdata layout (asset_lists/, csv/, csv-en/, userdata/) but only needs to include the files it adds rows to. CSV rows merge by primary key (first column), asset_lists entries merge by m_identifier, new_user.json top-level arrays union. Later --mod wins on collisions.")]
pub mods: Vec<String>
} }
pub fn get_args() -> Args { pub fn get_args() -> Args {

View File

@@ -7,7 +7,7 @@ use std::sync::Mutex;
use crate::include_file; use crate::include_file;
lazy_static! { lazy_static! {
static ref MERGED_CACHE: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new()); static ref LIST_CACHE: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
} }
pub fn routes(cfg: &mut web::ServiceConfig) { pub fn routes(cfg: &mut web::ServiceConfig) {
@@ -32,11 +32,11 @@ async fn get(_req: HttpRequest) -> impl Responder {
} }
fn load_list(name: &str) -> String { fn load_list(name: &str) -> String {
if let Some(cached) = MERGED_CACHE.lock().unwrap().get(name) { if let Some(cached) = LIST_CACHE.lock().unwrap().get(name) {
return cached.clone(); return cached.clone();
} }
let rel = format!("asset_lists/{}.json", name); let rel = format!("asset_lists/{}.json", name);
let base = crate::runtime::read_masterdata_file(&rel) let list = crate::runtime::read_masterdata_file(&rel)
.and_then(|b| String::from_utf8(b).ok()) .and_then(|b| String::from_utf8(b).ok())
.unwrap_or_else(|| match name { .unwrap_or_else(|| match name {
"Bundle" => include_file!("src/router/asset_lists/Bundle.json"), "Bundle" => include_file!("src/router/asset_lists/Bundle.json"),
@@ -45,62 +45,8 @@ fn load_list(name: &str) -> String {
_ => unreachable!(), _ => unreachable!(),
}); });
let mod_files = crate::runtime::read_mod_files(&rel); LIST_CACHE.lock().unwrap().insert(name.to_string(), list.clone());
let merged = if mod_files.is_empty() { list
base
} else {
merge_asset_list(name, base, mod_files)
};
MERGED_CACHE.lock().unwrap().insert(name.to_string(), merged.clone());
merged
}
fn merge_asset_list(name: &str, base: String, mod_files: Vec<(String, Vec<u8>)>) -> String {
let mut root = match jzon::parse(&base) {
Ok(v) => v,
Err(_) => return base,
};
let mut by_ident: HashMap<String, usize> = HashMap::new();
if let jzon::JsonValue::Array(ref arr) = root["m_manifestCollection"] {
for (i, e) in arr.iter().enumerate() {
let id = e["m_identifier"].to_string();
by_ident.insert(id.to_string(), i);
}
}
for (mod_dir, bytes) in mod_files {
let Ok(s) = String::from_utf8(bytes) else { continue };
let Ok(mod_root) = jzon::parse(&s) else { continue };
let mod_entries = match mod_root["m_manifestCollection"] {
jzon::JsonValue::Array(ref arr) => arr.clone(),
_ => continue,
};
let mut added = 0usize;
let mut replaced = 0usize;
for entry in mod_entries {
let ident = entry["m_identifier"].as_str().unwrap_or("").to_string();
if ident.is_empty() {
continue;
}
if let Some(&idx) = by_ident.get(&ident) {
root["m_manifestCollection"][idx] = entry;
replaced += 1;
} else {
let idx = root["m_manifestCollection"].len();
let _ = root["m_manifestCollection"].push(entry);
by_ident.insert(ident, idx);
added += 1;
}
}
if added > 0 || replaced > 0 {
println!(
"[mod {}] {}.json: +{} new entries, {} replaced",
mod_dir, name, added, replaced
);
}
}
jzon::stringify(root)
} }
async fn supported() -> impl Responder { async fn supported() -> impl Responder {

View File

@@ -9,7 +9,6 @@ lazy_static! {
static ref DATAPATH: RwLock<String> = RwLock::new(String::new()); static ref DATAPATH: RwLock<String> = RwLock::new(String::new());
static ref MASTERDATA_PATH: RwLock<String> = RwLock::new(String::new()); static ref MASTERDATA_PATH: RwLock<String> = RwLock::new(String::new());
static ref MASTERDATA_WARNED: Mutex<HashSet<String>> = Mutex::new(HashSet::new()); static ref MASTERDATA_WARNED: Mutex<HashSet<String>> = Mutex::new(HashSet::new());
static ref MOD_PATHS: RwLock<Vec<String>> = RwLock::new(Vec::new());
static ref EASTER: RwLock<bool> = RwLock::new(false); static ref EASTER: RwLock<bool> = RwLock::new(false);
static ref HOST_CONFIG: RwLock<HostConfig> = RwLock::new(HostConfig::default()); static ref HOST_CONFIG: RwLock<HostConfig> = RwLock::new(HostConfig::default());
} }
@@ -100,42 +99,6 @@ pub fn get_easter_mode() -> bool {
*EASTER.read().unwrap() *EASTER.read().unwrap()
} }
pub fn update_mod_paths(paths: &[String]) {
let cleaned: Vec<String> = paths.iter()
.map(|p| p.trim_end_matches('/').to_string())
.filter(|p| !p.is_empty())
.filter_map(|p| {
if Path::new(&p).is_dir() {
Some(p)
} else {
println!("Couldn't find mod directory {} — skipping", p);
None
}
})
.collect();
if !cleaned.is_empty() {
println!("Loaded {} mod overlay{}", cleaned.len(),
if cleaned.len() == 1 { "" } else { "s" });
for p in &cleaned {
println!(" mod: {}", p);
}
}
let mut w = MOD_PATHS.write().unwrap();
*w = cleaned;
}
pub fn read_mod_files(rel_path: &str) -> Vec<(String, Vec<u8>)> {
let paths = MOD_PATHS.read().unwrap().clone();
let mut out = Vec::new();
for p in paths {
let full = format!("{}/{}", p, rel_path);
if let Ok(bytes) = fs::read(&full) {
out.push((p, bytes));
}
}
out
}
pub fn apply_config_json(json: &str) { pub fn apply_config_json(json: &str) {
let parsed = match jzon::parse(json) { let parsed = match jzon::parse(json) {
Ok(p) => p, Ok(p) => p,