aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/mod_management/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src-tauri/src/mod_management/mod.rs')
-rw-r--r--src-tauri/src/mod_management/mod.rs176
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(())
+}