diff options
Diffstat (limited to 'src-tauri/src/mod_management/mod.rs')
-rw-r--r-- | src-tauri/src/mod_management/mod.rs | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs index ecfd3835..96bb1a0f 100644 --- a/src-tauri/src/mod_management/mod.rs +++ b/src-tauri/src/mod_management/mod.rs @@ -1,4 +1,5 @@ // This file contains various mod management functions +use async_recursion::async_recursion; use anyhow::{anyhow, Result}; use app::NorthstarMod; @@ -130,3 +131,178 @@ pub fn get_installed_mods_and_properties( Ok(installed_mods) } + +async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<String, String> { + + // TODO: This will crash the thread if not internet connection exist. `match` should be used instead + let index = thermite::api::get_package_index().await.unwrap().to_vec(); + + // String replace works but more care should be taken in the future + let ts_mod_string_url = thunderstore_mod_string.replace("-", "/"); + + for ns_mod in index { + // Iterate over all versions of a given mod + for (_key, ns_mod) in &ns_mod.versions { + if ns_mod.url.contains(&ts_mod_string_url) { + dbg!(ns_mod.clone()); + return Ok(ns_mod.url.clone()); + } + } + } + + Err("Could not find mod on Thunderstore".to_string()) +} + +/// Adds given Thunderstore mod string to the given `mod.json` +/// This way we can later check whether a mod is outdated based on the TS mod string +fn add_thunderstore_mod_string( + path_to_mod_json: String, + thunderstore_mod_string: String, +) -> Result<(), anyhow::Error> { + + // Read file into string and parse it + let data = std::fs::read_to_string(path_to_mod_json.clone())?; + let parsed_json: serde_json::Value = json5::from_str(&data)?; + + // Insert the Thunderstore mod string + let mut parsed_json = parsed_json.as_object().unwrap().clone(); + parsed_json.insert( + "ThunderstoreModString".to_string(), + serde_json::Value::String(thunderstore_mod_string), + ); + + // And write back to disk + std::fs::write( + path_to_mod_json, + serde_json::to_string_pretty(&parsed_json)?, + )?; + + Ok(()) +} + +/// Returns a vector of modstrings containing the dependencies of a given mod +async fn get_mod_dependencies( + thunderstore_mod_string: String, +) -> Result<Vec<String>, anyhow::Error> { + dbg!(thunderstore_mod_string.clone()); + + // TODO: This will crash the thread if not internet connection exist. `match` should be used instead + let index = thermite::api::get_package_index().await.unwrap().to_vec(); + + // String replace works but more care should be taken in the future + let ts_mod_string_url = thunderstore_mod_string.replace("-", "/"); + + // Iterate over index + for ns_mod in index { + // Iterate over all versions of a given mod + for (_key, ns_mod) in &ns_mod.versions { + if ns_mod.url.contains(&ts_mod_string_url) { + dbg!(ns_mod.clone()); + return Ok(ns_mod.deps.clone()); + } + } + } + Ok(Vec::<String>::new()) +} + +// Copied from `libtermite` source code and modified +// Should be replaced with a library call to libthermite in the future +/// Download and install mod to the specified target. +#[async_recursion] +pub async fn fc_download_mod_and_install( + game_install: GameInstall, + thunderstore_mod_string: String, +) -> Result<(), String> { + // Get mods and download directories + let download_directory = format!( + "{}/___flightcore-temp-download-dir/", + game_install.game_path + ); + let mods_directory = format!("{}/R2Northstar/mods/", game_install.game_path); + + // Early return on empty string + if thunderstore_mod_string.len() == 0 { + return Err("Passed empty string".to_string()); + } + + let deps = match get_mod_dependencies(thunderstore_mod_string.clone()).await { + Ok(deps) => deps, + Err(err) => return Err(err.to_string()), + }; + dbg!(deps.clone()); + + // Recursively install dependencies + for dep in deps { + match fc_download_mod_and_install(game_install.clone(), dep).await { + Ok(()) => (), + Err(err) => { + if err.to_string() == "Cannot install Northstar as a mod!" { + continue; // For Northstar as a dependency, we just skip it + } + else { + return Err(err.to_string()) + } + } + }; + } + + // Prevent installing Northstar as a mod + // While it would fail during install anyway, having explicit error message is nicer + let blacklisted_mods = ["northstar-Northstar", "northstar-NorthstarReleaseCandidate", "ebkr-r2modman"]; + for blacklisted_mod in blacklisted_mods { + if thunderstore_mod_string.contains(blacklisted_mod) { + return Err("Cannot install Northstar as a mod!".to_string()); + } + } + + // Get download URL for the specified mod + let download_url = get_ns_mod_download_url(thunderstore_mod_string.clone()).await?; + + // Create download directory + match std::fs::create_dir_all(download_directory.clone()) { + Ok(()) => (), + Err(err) => return Err(err.to_string()), + }; + + let name = thunderstore_mod_string.clone(); + let path = format!( + "{}/___flightcore-temp-download-dir/{}.zip", + game_install.game_path, name + ); + + // Download the mod + let f = match thermite::core::actions::download_file(&download_url, path.clone()).await { + Ok(f) => f, + Err(e) => return Err(e.to_string()), + }; + + // Extract the mod to the mods directory + let pkg = match thermite::core::actions::install_mod(&f, std::path::Path::new(&mods_directory)) + { + Ok(pkg) => pkg, + Err(err) => return Err(err.to_string()), + }; + dbg!(pkg.clone()); + + // Add Thunderstore mod string to `mod.json` of installed NorthstarMods + for nsmod in pkg.mods { + let path_to_current_mod_json = format!( + "{}/{}/mod.json", + mods_directory, + nsmod.path.to_string_lossy() + ); + match add_thunderstore_mod_string(path_to_current_mod_json, thunderstore_mod_string.clone()) + { + Ok(()) => (), + Err(err) => { + println!("Failed setting modstring for {}", nsmod.name); + println!("{}", err); + } + } + } + + // Delete downloaded zip file + std::fs::remove_file(path).unwrap(); + + Ok(()) +} |