aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src
diff options
context:
space:
mode:
Diffstat (limited to 'src-tauri/src')
-rw-r--r--src-tauri/src/constants.rs16
-rw-r--r--src-tauri/src/github/mod.rs181
-rw-r--r--src-tauri/src/github/pull_requests.rs25
-rw-r--r--src-tauri/src/github/release_notes.rs10
-rw-r--r--src-tauri/src/lib.rs276
-rw-r--r--src-tauri/src/main.rs87
-rw-r--r--src-tauri/src/mod_management/mod.rs88
-rw-r--r--src-tauri/src/northstar/mod.rs18
-rw-r--r--src-tauri/src/platform_specific/linux.rs4
-rw-r--r--src-tauri/src/platform_specific/windows.rs6
-rw-r--r--src-tauri/src/repair_and_verify/mod.rs10
-rw-r--r--src-tauri/src/thunderstore/mod.rs4
12 files changed, 551 insertions, 174 deletions
diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs
index 57a10e02..8c88df1e 100644
--- a/src-tauri/src/constants.rs
+++ b/src-tauri/src/constants.rs
@@ -1,6 +1,6 @@
// This file stores various global constants values
-
use const_format::concatcp;
+use std::time::Duration;
// FlightCore user agent for web requests
pub const APP_USER_AGENT: &str = concatcp!("FlightCore/", env!("CARGO_PKG_VERSION"));
@@ -29,8 +29,22 @@ pub const BLACKLISTED_MODS: [&str; 3] = [
// Titanfall2 game IDs on Origin/EA-App
pub const TITANFALL2_ORIGIN_IDS: [&str; 2] = ["Origin.OFR.50.0001452", "Origin.OFR.50.0001456"];
+// Titanfall2 Steam App ID
+pub const TITANFALL2_STEAM_ID: &str = "1237970";
+
+// Order in which the sections for release notes should be displayed
+pub const SECTION_ORDER: [&str; 9] = [
+ "feat", "fix", "docs", "style", "refactor", "build", "test", "chore", "other",
+];
+
// GitHub API endpoints for launcher/mods PRs
pub const PULLS_API_ENDPOINT_LAUNCHER: &str =
"https://api.github.com/repos/R2Northstar/NorthstarLauncher/pulls";
pub const PULLS_API_ENDPOINT_MODS: &str =
"https://api.github.com/repos/R2Northstar/NorthstarMods/pulls";
+
+// Statistics (players and servers counts) refresh delay
+pub const REFRESH_DELAY: Duration = Duration::from_secs(5 * 60);
+
+// Flightcore repo name and org name on GitHub
+pub const FLIGHTCORE_REPO_NAME: &str = "R2NorthstarTools/FlightCore";
diff --git a/src-tauri/src/github/mod.rs b/src-tauri/src/github/mod.rs
index 942f0db2..b336ab5c 100644
--- a/src-tauri/src/github/mod.rs
+++ b/src-tauri/src/github/mod.rs
@@ -1,2 +1,183 @@
pub mod pull_requests;
pub mod release_notes;
+
+use app::constants::{APP_USER_AGENT, FLIGHTCORE_REPO_NAME, SECTION_ORDER};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use ts_rs::TS;
+
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub struct Tag {
+ name: String,
+}
+
+/// Wrapper type needed for frontend
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub struct TagWrapper {
+ label: String,
+ value: Tag,
+}
+
+#[derive(Debug, Deserialize)]
+struct CommitInfo {
+ sha: String,
+ commit: Commit,
+}
+
+#[derive(Debug, Deserialize)]
+struct Commit {
+ message: String,
+}
+
+#[derive(Debug, Deserialize)]
+struct Comparison {
+ commits: Vec<CommitInfo>,
+}
+
+/// Get a list of tags on the FlightCore repo
+#[tauri::command]
+pub fn get_list_of_tags() -> Result<Vec<TagWrapper>, String> {
+ // Set the repository name.
+
+ // Create a `reqwest` client with a user agent.
+ let client = reqwest::blocking::Client::builder()
+ .user_agent(APP_USER_AGENT)
+ .build()
+ .unwrap();
+
+ // Fetch the list of tags for the repository as a `Vec<Tag>`.
+ let tags_url = format!("https://api.github.com/repos/{}/tags", FLIGHTCORE_REPO_NAME);
+ let tags: Vec<Tag> = client.get(tags_url).send().unwrap().json().unwrap();
+
+ // Map each `Tag` element to a `TagWrapper` element with the desired label and `Tag` value.
+ let tag_wrappers: Vec<TagWrapper> = tags
+ .into_iter()
+ .map(|tag| TagWrapper {
+ label: tag.name.clone(),
+ value: tag,
+ })
+ .collect();
+
+ Ok(tag_wrappers)
+}
+
+/// Use GitHub API to compare two tags of the same repo against each other and get the resulting changes
+#[tauri::command]
+pub fn compare_tags(first_tag: Tag, second_tag: Tag) -> Result<String, String> {
+ // Fetch the list of commits between the two tags.
+
+ // Create a `reqwest` client with a user agent.
+ let client = reqwest::blocking::Client::builder()
+ .user_agent(APP_USER_AGENT)
+ .build()
+ .unwrap();
+
+ let repo = "R2NorthstarTools/FlightCore";
+
+ let mut full_patch_notes = "".to_string();
+
+ let mut patch_notes: Vec<String> = [].to_vec();
+ println!("{}", repo);
+ // let repo = "R2Northstar/NorthstarLauncher";
+ let comparison_url = format!(
+ "https://api.github.com/repos/{}/compare/{}...{}",
+ repo, first_tag.name, second_tag.name
+ );
+
+ let comparison: Comparison = client.get(comparison_url).send().unwrap().json().unwrap();
+ let commits = comparison.commits;
+
+ // Display the list of commits.
+ println!(
+ "Commits between {} and {}:",
+ first_tag.name, second_tag.name
+ );
+
+ // Iterate over all commits in the diff
+ for commit in commits {
+ println!(
+ " * {} : {}",
+ commit.sha,
+ commit.commit.message.split('\n').next().unwrap()
+ );
+ patch_notes.push(
+ commit
+ .commit
+ .message
+ .split('\n')
+ .next()
+ .unwrap()
+ .to_string(),
+ );
+ }
+
+ full_patch_notes += &generate_flightcore_release_notes(patch_notes);
+
+ Ok(full_patch_notes.to_string())
+}
+
+/// Generate release notes in the format used for FlightCore
+fn generate_flightcore_release_notes(commits: Vec<String>) -> String {
+ let grouped_commits = group_commits_by_type(commits);
+ let mut release_notes = String::new();
+
+ // Go over commit types and generate notes
+ for commit_type in SECTION_ORDER {
+ if let Some(commit_list) = grouped_commits.get(commit_type) {
+ if !commit_list.is_empty() {
+ let section_title = match commit_type {
+ "feat" => "**Features:**",
+ "fix" => "**Bug Fixes:**",
+ "docs" => "**Documentation:**",
+ "style" => "**Styles:**",
+ "refactor" => "**Code Refactoring:**",
+ "build" => "**Build:**",
+ "test" => "**Tests:**",
+ "chore" => "**Chores:**",
+ _ => "**Other:**",
+ };
+
+ release_notes.push_str(&format!("{}\n", section_title));
+
+ for commit_message in commit_list {
+ release_notes.push_str(&format!("- {}\n", commit_message));
+ }
+
+ release_notes.push('\n');
+ }
+ }
+ }
+
+ release_notes
+}
+
+/// Group semantic commit messages by type
+/// Commmit messages that are not formatted accordingly are marked as "other"
+fn group_commits_by_type(commits: Vec<String>) -> HashMap<String, Vec<String>> {
+ let mut grouped_commits: HashMap<String, Vec<String>> = HashMap::new();
+ let mut other_commits: Vec<String> = vec![];
+
+ for commit in commits {
+ let commit_parts: Vec<&str> = commit.splitn(2, ':').collect();
+ if commit_parts.len() == 2 {
+ let commit_type = commit_parts[0].to_lowercase();
+ let commit_description = commit_parts[1].trim().to_string();
+
+ // Check if known commit type
+ if SECTION_ORDER.contains(&commit_type.as_str()) {
+ let commit_list = grouped_commits.entry(commit_type.to_string()).or_default();
+ commit_list.push(commit_description);
+ } else {
+ // otherwise add to list of "other"
+ other_commits.push(commit.to_string());
+ }
+ } else {
+ other_commits.push(commit.to_string());
+ }
+ }
+ grouped_commits.insert("other".to_string(), other_commits);
+
+ grouped_commits
+}
diff --git a/src-tauri/src/github/pull_requests.rs b/src-tauri/src/github/pull_requests.rs
index 586a4fb3..96ac623f 100644
--- a/src-tauri/src/github/pull_requests.rs
+++ b/src-tauri/src/github/pull_requests.rs
@@ -60,15 +60,15 @@ struct ArtifactsResponse {
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub enum PullRequestType {
- MODS,
- LAUNCHER,
+ Mods,
+ Launcher,
}
/// Parse pull requests from specified URL
pub async fn get_pull_requests(url: String) -> Result<Vec<PullsApiResponseElement>, String> {
let json_response = match fetch_github_releases_api(&url).await {
Ok(result) => result,
- Err(err) => return Err(err.to_string()),
+ Err(err) => return Err(err),
};
let pulls_response: Vec<PullsApiResponseElement> = match serde_json::from_str(&json_response) {
@@ -85,8 +85,8 @@ pub async fn get_pull_requests_wrapper(
install_type: PullRequestType,
) -> Result<Vec<PullsApiResponseElement>, String> {
let api_pr_url = match install_type {
- PullRequestType::MODS => PULLS_API_ENDPOINT_MODS,
- PullRequestType::LAUNCHER => PULLS_API_ENDPOINT_LAUNCHER,
+ PullRequestType::Mods => PULLS_API_ENDPOINT_MODS,
+ PullRequestType::Launcher => PULLS_API_ENDPOINT_LAUNCHER,
};
get_pull_requests(api_pr_url.to_string()).await
@@ -142,7 +142,8 @@ fn get_mods_download_link(pull_request: PullsApiResponseElement) -> Result<Strin
}
/// Gets `nightly.link` artifact download link of a launcher PR
-async fn get_launcher_download_link(
+#[tauri::command]
+pub async fn get_launcher_download_link(
pull_request: PullsApiResponseElement,
) -> Result<String, String> {
// Iterate over the first 10 pages of
@@ -207,7 +208,7 @@ fn add_batch_file(game_install_path: &str) {
match file.write_all(batch_file_content.as_bytes()) {
Err(why) => panic!("couldn't write to {}: {}", display, why),
- Ok(_) => println!("successfully wrote to {}", display),
+ Ok(_) => log::info!("successfully wrote to {}", display),
}
}
@@ -258,7 +259,7 @@ pub async fn apply_launcher_pr(
for file_name in files_to_copy {
let source_file_path = format!("{}/{}", extract_directory, file_name);
let destination_file_path = format!("{}/{}", game_install_path, file_name);
- match std::fs::copy(&source_file_path, &destination_file_path) {
+ match std::fs::copy(source_file_path, destination_file_path) {
Ok(_result) => (),
Err(err) => {
return Err(format!(
@@ -280,7 +281,7 @@ pub async fn apply_launcher_pr(
}
}
- println!("All done with installing launcher PR");
+ log::info!("All done with installing launcher PR");
Ok(())
}
@@ -308,9 +309,9 @@ pub async fn apply_mods_pr(
// Delete previously managed folder
if std::fs::remove_dir_all(profile_folder.clone()).is_err() {
if std::path::Path::new(&profile_folder).exists() {
- println!("Failed removing previous dir");
+ log::error!("Failed removing previous dir");
} else {
- println!("Failed removing folder that doesn't exist. Probably cause first run");
+ log::warn!("Failed removing folder that doesn't exist. Probably cause first run");
}
};
@@ -330,6 +331,6 @@ pub async fn apply_mods_pr(
// Add batch file to launch right profile
add_batch_file(game_install_path);
- println!("All done with installing mods PR");
+ log::info!("All done with installing mods PR");
Ok(())
}
diff --git a/src-tauri/src/github/release_notes.rs b/src-tauri/src/github/release_notes.rs
index b59358ec..b45442e0 100644
--- a/src-tauri/src/github/release_notes.rs
+++ b/src-tauri/src/github/release_notes.rs
@@ -20,7 +20,7 @@ pub struct FlightCoreVersion {
// Fetches repo release API and returns response as string
pub async fn fetch_github_releases_api(url: &str) -> Result<String, String> {
- println!("Fetching releases notes from GitHub API");
+ log::info!("Fetching releases notes from GitHub API");
let client = reqwest::Client::new();
let res = client
@@ -40,13 +40,13 @@ pub async fn fetch_github_releases_api(url: &str) -> Result<String, String> {
#[tauri::command]
pub async fn get_newest_flightcore_version() -> Result<FlightCoreVersion, String> {
// Get newest version number from GitHub API
- println!("Checking GitHub API");
+ log::info!("Checking GitHub API");
let url = "https://api.github.com/repos/R2NorthstarTools/FlightCore/releases/latest";
let res = fetch_github_releases_api(url).await?;
let flightcore_version: FlightCoreVersion =
serde_json::from_str(&res).expect("JSON was not well-formatted");
- println!("Done checking GitHub API");
+ log::info!("Done checking GitHub API");
Ok(flightcore_version)
}
@@ -98,7 +98,7 @@ pub async fn get_northstar_release_notes() -> Result<Vec<ReleaseInfo>, String> {
let release_info_vector: Vec<ReleaseInfo> =
serde_json::from_str(&res).expect("JSON was not well-formatted");
- println!("Done checking GitHub API");
+ log::info!("Done checking GitHub API");
- return Ok(release_info_vector);
+ Ok(release_info_vector)
}
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 12c839fe..8ec7e4b1 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,7 +1,6 @@
-use std::env;
+use std::{cell::RefCell, env, fs, path::Path, time::Duration, time::Instant};
use anyhow::{anyhow, Context, Result};
-use sentry::{add_breadcrumb, Breadcrumb, Level};
mod northstar;
@@ -15,11 +14,14 @@ use platform_specific::linux;
use serde::{Deserialize, Serialize};
use sysinfo::SystemExt;
+use tokio::time::sleep;
use ts_rs::TS;
use zip::ZipArchive;
use northstar::get_northstar_version_number;
+use crate::constants::TITANFALL2_STEAM_ID;
+
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum InstallType {
STEAM,
@@ -50,10 +52,26 @@ pub struct NorthstarServer {
pub player_count: i32,
}
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+pub enum InstallState {
+ DOWNLOADING,
+ EXTRACTING,
+ DONE,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, TS)]
+#[ts(export)]
+struct InstallProgress {
+ current_downloaded: u64,
+ total_size: u64,
+ state: InstallState,
+}
+
/// Check version number of a mod
-pub fn check_mod_version_number(path_to_mod_folder: String) -> Result<String, anyhow::Error> {
+pub fn check_mod_version_number(path_to_mod_folder: &str) -> Result<String, anyhow::Error> {
// println!("{}", format!("{}/mod.json", path_to_mod_folder));
- let data = std::fs::read_to_string(format!("{}/mod.json", path_to_mod_folder))?;
+ let data = std::fs::read_to_string(format!("{path_to_mod_folder}/mod.json"))?;
let parsed_json: serde_json::Value = serde_json::from_str(&data)?;
// println!("{}", parsed_json);
let mod_version_number = match parsed_json.get("Version").and_then(|value| value.as_str()) {
@@ -61,7 +79,7 @@ pub fn check_mod_version_number(path_to_mod_folder: String) -> Result<String, an
None => return Err(anyhow!("No version number found")),
};
- println!("{}", mod_version_number);
+ log::info!("{}", mod_version_number);
Ok(mod_version_number.to_string())
}
@@ -81,8 +99,7 @@ pub fn linux_checks_librs() -> Result<(), String> {
return Err(format!(
"GLIBC is not version {} or greater",
min_required_ldd_version
- )
- .to_string());
+ ));
};
// All checks passed
@@ -94,7 +111,7 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
// Attempt parsing Steam library directly
match steamlocate::SteamDir::locate() {
Some(mut steamdir) => {
- let titanfall2_steamid = 1237970;
+ let titanfall2_steamid = TITANFALL2_STEAM_ID.parse().unwrap();
match steamdir.app(&titanfall2_steamid) {
Some(app) => {
// println!("{:#?}", app);
@@ -104,10 +121,10 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
};
return Ok(game_install);
}
- None => println!("Couldn't locate Titanfall2"),
+ None => log::info!("Couldn't locate Titanfall2 Steam instal"),
}
}
- None => println!("Couldn't locate Steam on this computer!"),
+ None => log::info!("Couldn't locate Steam on this computer!"),
}
// (On Windows only) try parsing Windows registry for Origin install path
@@ -115,13 +132,13 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
match windows::origin_install_location_detection() {
Ok(game_path) => {
let game_install = GameInstall {
- game_path: game_path,
+ game_path,
install_type: InstallType::ORIGIN,
};
return Ok(game_install);
}
Err(err) => {
- println!("{}", err);
+ log::info!("{}", err);
}
};
@@ -130,13 +147,13 @@ pub fn find_game_install_location() -> Result<GameInstall, String> {
/// Checks whether the provided path is a valid Titanfall2 gamepath by checking against a certain set of criteria
pub fn check_is_valid_game_path(game_install_path: &str) -> Result<(), String> {
- let path_to_titanfall2_exe = format!("{}/Titanfall2.exe", game_install_path);
+ let path_to_titanfall2_exe = format!("{game_install_path}/Titanfall2.exe");
let is_correct_game_path = std::path::Path::new(&path_to_titanfall2_exe).exists();
- println!("Titanfall2.exe exists in path? {}", is_correct_game_path);
+ log::info!("Titanfall2.exe exists in path? {}", is_correct_game_path);
// Exit early if wrong game path
if !is_correct_game_path {
- return Err(format!("Incorrect game path \"{}\"", game_install_path)); // Return error cause wrong game path
+ return Err(format!("Incorrect game path \"{game_install_path}\"")); // Return error cause wrong game path
}
Ok(())
}
@@ -159,12 +176,12 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
);
if (*f.name()).ends_with('/') {
- println!("Create directory {}", f.name());
+ log::info!("Create directory {}", f.name());
std::fs::create_dir_all(target.join(f.name()))
.context("Unable to create directory")?;
continue;
} else if let Some(p) = out.parent() {
- std::fs::create_dir_all(&p).context("Unable to create directory")?;
+ std::fs::create_dir_all(p).context("Unable to create directory")?;
}
let mut outfile = std::fs::OpenOptions::new()
@@ -173,7 +190,7 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
.truncate(true)
.open(&out)?;
- println!("Write file {}", out.display());
+ log::info!("Write file {}", out.display());
std::io::copy(&mut f, &mut outfile).context("Unable to write to file")?;
}
@@ -186,33 +203,83 @@ fn extract(zip_file: std::fs::File, target: &std::path::Path) -> Result<()> {
///Install N* from the provided mod
///
///Checks cache, else downloads the latest version
-async fn do_install(nmod: &thermite::model::ModVersion, game_path: &std::path::Path) -> Result<()> {
+async fn do_install(
+ window: tauri::Window,
+ nmod: &thermite::model::ModVersion,
+ game_path: &std::path::Path,
+) -> Result<()> {
let filename = format!("northstar-{}.zip", nmod.version);
let download_directory = format!("{}/___flightcore-temp-download-dir/", game_path.display());
std::fs::create_dir_all(download_directory.clone())?;
- let download_path = format!("{}/{}", download_directory.clone(), filename);
- println!("{}", download_path);
-
- let nfile = match thermite::core::manage::download_file(&nmod.url, download_path) {
+ let download_path = format!("{}/{}", download_directory, filename);
+ log::info!("Download path: {download_path}");
+
+ let last_emit = RefCell::new(Instant::now()); // Keep track of the last time a signal was emitted
+ let nfile = match thermite::core::manage::download_file_with_progress(
+ &nmod.url,
+ download_path,
+ |delta, current, total| {
+ if delta != 0 {
+ // Only emit a signal once every 100ms
+ // This way we don't bombard the frontend with events on fast download speeds
+ let time_since_last_emit = Instant::now().duration_since(*last_emit.borrow());
+ if time_since_last_emit >= Duration::from_millis(100) {
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: current,
+ total_size: total,
+ state: InstallState::DOWNLOADING,
+ },
+ )
+ .unwrap();
+ *last_emit.borrow_mut() = Instant::now();
+ }
+ }
+ },
+ ) {
Ok(res) => res,
Err(err) => return Err(anyhow!("Failed downloading Northstar {}", err)),
};
- println!("Extracting Northstar...");
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: 0,
+ total_size: 0,
+ state: InstallState::EXTRACTING,
+ },
+ )
+ .unwrap();
+
+ log::info!("Extracting Northstar...");
extract(nfile, game_path)?;
// Delete old copy
- println!("Delete temp folder again");
+ log::info!("Delete temp folder again");
std::fs::remove_dir_all(download_directory).unwrap();
- println!("Done!");
+ log::info!("Done installing Northstar!");
+ window
+ .emit(
+ "northstar-install-download-progress",
+ InstallProgress {
+ current_downloaded: 0,
+ total_size: 0,
+ state: InstallState::DONE,
+ },
+ )
+ .unwrap();
Ok(())
}
pub async fn install_northstar(
+ window: tauri::Window,
game_path: &str,
northstar_package_name: Option<String>,
) -> Result<String, String> {
@@ -234,20 +301,30 @@ pub async fn install_northstar(
.ok_or_else(|| panic!("Couldn't find Northstar on thunderstore???"))
.unwrap();
- // Breadcrumb for sentry to debug crash
- add_breadcrumb(Breadcrumb {
- // category: Some("auth".into()),
- message: Some(format!("Install path \"{}\"", game_path)),
- level: Level::Info,
- ..Default::default()
- });
+ log::info!("Install path \"{}\"", game_path);
- do_install(
+ match do_install(
+ window,
nmod.versions.get(&nmod.latest).unwrap(),
std::path::Path::new(game_path),
)
.await
- .unwrap();
+ {
+ Ok(_) => (),
+ Err(err) => {
+ if game_path
+ .to_lowercase()
+ .contains(&r#"C:\Program Files\"#.to_lowercase())
+ // default is `C:\Program Files\EA Games\Titanfall2`
+ {
+ return Err(
+ "Cannot install to default EA App install path, please move Titanfall2 to a different install location.".to_string(),
+ );
+ } else {
+ return Err(err.to_string());
+ }
+ }
+ }
Ok(nmod.latest.clone())
}
@@ -258,7 +335,7 @@ pub fn get_host_os() -> String {
}
pub fn launch_northstar(
- game_install: GameInstall,
+ game_install: &GameInstall,
bypass_checks: Option<bool>,
) -> Result<String, String> {
dbg!(game_install.clone());
@@ -278,15 +355,12 @@ pub fn launch_northstar(
));
}
- let bypass_checks = match bypass_checks {
- Some(bypass_checks) => bypass_checks,
- None => false,
- };
+ let bypass_checks = bypass_checks.unwrap_or(false);
// Only check guards if bypassing checks is not enabled
if !bypass_checks {
// Some safety checks before, should have more in the future
- if get_northstar_version_number(game_install.game_path.clone()).is_err() {
+ if get_northstar_version_number(&game_install.game_path).is_err() {
return Err(anyhow!("Not all checks were met").to_string());
}
@@ -314,7 +388,7 @@ pub fn launch_northstar(
{
let ns_exe_path = format!("{}/NorthstarLauncher.exe", game_install.game_path);
let _output = std::process::Command::new("C:\\Windows\\System32\\cmd.exe")
- .args(&["/C", "start", "", &ns_exe_path])
+ .args(["/C", "start", "", &ns_exe_path])
.spawn()
.expect("failed to execute process");
return Ok("Launched game".to_string());
@@ -327,31 +401,109 @@ pub fn launch_northstar(
))
}
-pub fn check_origin_running() -> bool {
- let s = sysinfo::System::new_all();
- for _process in s.processes_by_name("Origin.exe") {
- // check here if this is your process
- // dbg!(process);
- // There's at least one Origin process, so we can launch
- return true;
+/// Prepare Northstar and Launch through Steam using the Browser Protocol
+pub fn launch_northstar_steam(
+ game_install: &GameInstall,
+ _bypass_checks: Option<bool>,
+) -> Result<String, String> {
+ if !matches!(game_install.install_type, InstallType::STEAM) {
+ return Err("Titanfall2 was not installed via Steam".to_string());
}
- // Alternatively, check for EA Desktop
- for _process in s.processes_by_name("EADesktop.exe") {
- // There's at least one EADesktop process, so we can launch
- return true;
+
+ match steamlocate::SteamDir::locate() {
+ Some(mut steamdir) => {
+ if get_host_os() != "windows" {
+ let titanfall2_steamid: u32 = TITANFALL2_STEAM_ID.parse().unwrap();
+ match steamdir.compat_tool(&titanfall2_steamid) {
+ Some(compat) => {
+ if !compat
+ .name
+ .clone()
+ .unwrap()
+ .to_ascii_lowercase()
+ .contains("northstarproton")
+ {
+ return Err(
+ "Titanfall2 was not configured to use NorthstarProton".to_string()
+ );
+ }
+ }
+ None => {
+ return Err(
+ "Titanfall2 was not configured to use a compatibility tool".to_string()
+ );
+ }
+ }
+ }
+ }
+ None => {
+ return Err("Couldn't access Titanfall2 directory".to_string());
+ }
}
- false
+
+ // Switch to Titanfall2 directory to set everything up
+ if std::env::set_current_dir(game_install.game_path.clone()).is_err() {
+ // We failed to get to Titanfall2 directory
+ return Err("Couldn't access Titanfall2 directory".to_string());
+ }
+
+ let run_northstar = "run_northstar.txt";
+ let run_northstar_bak = "run_northstar.txt.bak";
+
+ if Path::new(run_northstar).exists() {
+ // rename should ovewrite existing files
+ fs::rename(run_northstar, run_northstar_bak).unwrap();
+ }
+
+ // Passing arguments gives users a prompt, so we use run_northstar.txt
+ fs::write(run_northstar, b"1").unwrap();
+
+ let retval = match open::that(format!("steam://run/{}/", TITANFALL2_STEAM_ID)) {
+ Ok(()) => Ok("Started game".to_string()),
+ Err(_err) => Err("Failed to launch Titanfall 2 via Steam".to_string()),
+ };
+
+ let is_err = retval.is_err();
+
+ // Handle the rest in the backround
+ tauri::async_runtime::spawn(async move {
+ // Starting the EA app and Titanfall might take a good minute or three
+ let mut wait_countdown = 60 * 3;
+ while wait_countdown > 0 && !check_northstar_running() && !is_err {
+ sleep(Duration::from_millis(1000)).await;
+ wait_countdown -= 1;
+ }
+
+ // Northstar may be running, but it may not have loaded the file yet
+ sleep(Duration::from_millis(2000)).await;
+
+ // intentionally ignore Result
+ let _ = fs::remove_file(run_northstar);
+
+ if Path::new(run_northstar_bak).exists() {
+ fs::rename(run_northstar_bak, run_northstar).unwrap();
+ }
+ });
+
+ retval
+}
+
+pub fn check_origin_running() -> bool {
+ let s = sysinfo::System::new_all();
+ let x = s.processes_by_name("Origin.exe").next().is_some()
+ || s.processes_by_name("EADesktop.exe").next().is_some();
+ x
}
/// Checks if Northstar process is running
pub fn check_northstar_running() -> bool {
let s = sysinfo::System::new_all();
- for _process in s.processes_by_name("NorthstarLauncher.exe") {
- // check here if this is your process
- // dbg!(process);
- return true;
- }
- false
+ let x = s
+ .processes_by_name("NorthstarLauncher.exe")
+ .next()
+ .is_some()
+ || s.processes_by_name("Titanfall2.exe").next().is_some();
+ x
}
/// Helps with converting release candidate numbers which are different on Thunderstore
@@ -364,7 +516,7 @@ pub fn convert_release_candidate_number(version_number: String) -> String {
}
/// Returns a serde json object of the parsed `enabledmods.json` file
-pub fn get_enabled_mods(game_install: GameInstall) -> Result<serde_json::value::Value, String> {
+pub fn get_enabled_mods(game_install: &GameInstall) -> Result<serde_json::value::Value, String> {
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
// Check for JSON file
@@ -381,7 +533,7 @@ pub fn get_enabled_mods(game_install: GameInstall) -> Result<serde_json::value::
// Parse JSON
let res: serde_json::Value = match serde_json::from_str(&data) {
Ok(result) => result,
- Err(err) => return Err(format!("Failed to read JSON due to: {}", err.to_string())),
+ Err(err) => return Err(format!("Failed to read JSON due to: {}", err)),
};
// Return parsed data
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 50e439c8..3aa17459 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -10,15 +10,18 @@ use std::{
};
use app::{
- constants::{APP_USER_AGENT, MASTER_SERVER_URL, SERVER_BROWSER_ENDPOINT},
+ constants::{APP_USER_AGENT, MASTER_SERVER_URL, REFRESH_DELAY, SERVER_BROWSER_ENDPOINT},
*,
};
mod github;
-use github::pull_requests::{apply_launcher_pr, apply_mods_pr, get_pull_requests_wrapper};
+use github::pull_requests::{
+ apply_launcher_pr, apply_mods_pr, get_launcher_download_link, get_pull_requests_wrapper,
+};
use github::release_notes::{
check_is_flightcore_outdated, get_newest_flightcore_version, get_northstar_release_notes,
};
+use github::{compare_tags, get_list_of_tags};
mod repair_and_verify;
use repair_and_verify::{
@@ -38,13 +41,20 @@ mod thunderstore;
use thunderstore::query_thunderstore_packages_api;
use tauri::{Manager, Runtime};
-use tauri_plugin_store::PluginBuilder;
use tokio::time::sleep;
#[derive(Default)]
struct Counter(Arc<Mutex<i32>>);
fn main() {
+ // Setup logger
+ let mut log_builder = pretty_env_logger::formatted_builder();
+ log_builder.parse_filters("info");
+ let logger = sentry_log::SentryLogger::with_dest(log_builder.build());
+
+ log::set_boxed_logger(Box::new(logger)).unwrap();
+ log::set_max_level(log::LevelFilter::Info);
+
// Only enable Sentry crash logs on release
#[cfg(not(debug_assertions))]
let _guard = sentry::init((
@@ -57,7 +67,7 @@ fn main() {
));
tauri::Builder::default()
- .plugin(PluginBuilder::default().build())
+ .plugin(tauri_plugin_store::Builder::default().build())
.setup(|app| {
let app_handle = app.app_handle();
tauri::async_runtime::spawn(async move {
@@ -86,6 +96,17 @@ fn main() {
}
});
+ // Emit updated player and server count to GUI
+ let app_handle = app.app_handle();
+ tauri::async_runtime::spawn(async move {
+ loop {
+ sleep(REFRESH_DELAY).await;
+ app_handle
+ .emit_all("northstar-statistics", get_server_player_count().await)
+ .unwrap();
+ }
+ });
+
Ok(())
})
.manage(Counter(Default::default()))
@@ -100,6 +121,7 @@ fn main() {
install_northstar_caller,
update_northstar_caller,
launch_northstar_caller,
+ launch_northstar_steam_caller,
check_is_flightcore_outdated_caller,
get_log_list,
verify_game_files,
@@ -117,9 +139,12 @@ fn main() {
delete_thunderstore_mod,
open_repair_window,
query_thunderstore_packages_api,
+ get_list_of_tags,
+ compare_tags,
get_pull_requests_wrapper,
apply_launcher_pr,
apply_mods_pr,
+ get_launcher_download_link,
close_application,
])
.run(tauri::generate_context!())
@@ -142,7 +167,7 @@ fn force_panic() {
#[tauri::command]
/// Returns true if built in debug mode
async fn is_debug_mode() -> bool {
- return cfg!(debug_assertions);
+ cfg!(debug_assertions)
}
#[tauri::command]
@@ -177,7 +202,7 @@ async fn get_flightcore_version_number() -> String {
#[tauri::command]
async fn get_northstar_version_number_caller(game_path: String) -> Result<String, String> {
- match get_northstar_version_number(game_path) {
+ match get_northstar_version_number(&game_path) {
Ok(version_number) => Ok(version_number),
Err(err) => Err(err.to_string()),
}
@@ -209,10 +234,10 @@ async fn check_is_northstar_outdated(
.expect("Couldn't find Northstar on thunderstore???");
// .ok_or_else(|| anyhow!("Couldn't find Northstar on thunderstore???"))?;
- let version_number = match get_northstar_version_number(game_path) {
+ let version_number = match get_northstar_version_number(&game_path) {
Ok(version_number) => version_number,
Err(err) => {
- println!("{}", err);
+ log::warn!("{}", err);
// If we fail to get new version just assume we are up-to-date
return Err(err.to_string());
}
@@ -222,10 +247,10 @@ async fn check_is_northstar_outdated(
let version_number = convert_release_candidate_number(version_number);
if version_number != nmod.latest {
- println!("Installed Northstar version outdated");
+ log::info!("Installed Northstar version outdated");
Ok(true)
} else {
- println!("Installed Northstar version up-to-date");
+ log::info!("Installed Northstar version up-to-date");
Ok(false)
}
}
@@ -244,7 +269,7 @@ async fn verify_install_location(game_path: String) -> bool {
match check_is_valid_game_path(&game_path) {
Ok(()) => true,
Err(err) => {
- println!("{}", err);
+ log::warn!("{}", err);
false
}
}
@@ -259,15 +284,16 @@ async fn get_host_os_caller() -> String {
#[tauri::command]
/// Installs Northstar to the given path
async fn install_northstar_caller(
+ window: tauri::Window,
game_path: String,
northstar_package_name: Option<String>,
) -> Result<bool, String> {
- println!("Running");
- match install_northstar(&game_path, northstar_package_name).await {
+ log::info!("Running");
+ match install_northstar(window, &game_path, northstar_package_name).await {
Ok(_) => Ok(true),
Err(err) => {
- println!("{}", err);
- Err(err.to_string())
+ log::error!("{}", err);
+ Err(err)
}
}
}
@@ -275,17 +301,18 @@ async fn install_northstar_caller(
#[tauri::command]
/// Update Northstar install in the given path
async fn update_northstar_caller(
+ window: tauri::Window,
game_path: String,
northstar_package_name: Option<String>,
) -> Result<bool, String> {
- println!("Updating");
+ log::info!("Updating Northstar");
// Simply re-run install with up-to-date version for upate
- match install_northstar(&game_path, northstar_package_name).await {
+ match install_northstar(window, &game_path, northstar_package_name).await {
Ok(_) => Ok(true),
Err(err) => {
- println!("{}", err);
- Err(err.to_string())
+ log::error!("{}", err);
+ Err(err)
}
}
}
@@ -296,7 +323,16 @@ async fn launch_northstar_caller(
game_install: GameInstall,
bypass_checks: Option<bool>,
) -> Result<String, String> {
- launch_northstar(game_install, bypass_checks)
+ launch_northstar(&game_install, bypass_checks)
+}
+
+#[tauri::command]
+/// Launches Northstar
+async fn launch_northstar_steam_caller(
+ game_install: GameInstall,
+ bypass_checks: Option<bool>,
+) -> Result<String, String> {
+ launch_northstar_steam(&game_install, bypass_checks)
}
#[tauri::command]
@@ -305,11 +341,11 @@ async fn install_mod_caller(
game_install: GameInstall,
thunderstore_mod_string: String,
) -> Result<(), String> {
- fc_download_mod_and_install(game_install.clone(), thunderstore_mod_string).await?;
- match clean_up_download_folder(game_install, false) {
+ fc_download_mod_and_install(&game_install, &thunderstore_mod_string).await?;
+ match clean_up_download_folder(&game_install, false) {
Ok(()) => Ok(()),
Err(err) => {
- println!("Failed to delete download folder due to {}", err);
+ log::info!("Failed to delete download folder due to {}", err);
// Failure to delete download folder is not an error in mod install
// As such ignore. User can still force delete if need be
Ok(())
@@ -323,7 +359,7 @@ async fn clean_up_download_folder_caller(
game_install: GameInstall,
force: bool,
) -> Result<(), String> {
- match clean_up_download_folder(game_install, force) {
+ match clean_up_download_folder(&game_install, force) {
Ok(()) => Ok(()),
Err(err) => Err(err.to_string()),
}
@@ -353,7 +389,8 @@ async fn get_server_player_count() -> Result<(i32, usize), String> {
// Sum up player count
let total_player_count: i32 = ns_servers.iter().map(|server| server.player_count).sum();
- dbg!((total_player_count, server_count));
+ log::info!("total_player_count: {}", total_player_count);
+ log::info!("server_count: {}", server_count);
Ok((total_player_count, server_count))
}
diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs
index d2b5040b..728e72c0 100644
--- a/src-tauri/src/mod_management/mod.rs
+++ b/src-tauri/src/mod_management/mod.rs
@@ -12,8 +12,6 @@ use std::path::PathBuf;
use app::get_enabled_mods;
use app::GameInstall;
-use json5;
-
#[derive(Debug, Clone)]
struct ParsedThunderstoreModString {
author_name: String,
@@ -25,7 +23,7 @@ impl std::str::FromStr for ParsedThunderstoreModString {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let mut parts = s.split("-");
+ let mut parts = s.split('-');
let author_name = parts.next().unwrap().to_string();
let mod_name = parts.next().unwrap().to_string();
@@ -56,9 +54,9 @@ pub struct ModJson {
}
/// Gets all currently installed and enabled/disabled mods to rebuild `enabledmods.json`
-pub fn rebuild_enabled_mods_json(game_install: GameInstall) -> Result<(), String> {
+pub fn rebuild_enabled_mods_json(game_install: &GameInstall) -> Result<(), String> {
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
- let mods_and_properties = get_installed_mods_and_properties(game_install)?;
+ let mods_and_properties = get_installed_mods_and_properties(game_install.clone())?;
// Create new mapping
let mut my_map = serde_json::Map::new();
@@ -91,28 +89,27 @@ pub fn set_mod_enabled_status(
let enabledmods_json_path = format!("{}/R2Northstar/enabledmods.json", game_install.game_path);
// Parse JSON
- let mut res: serde_json::Value = match get_enabled_mods(game_install.clone()) {
+ let mut res: serde_json::Value = match get_enabled_mods(&game_install) {
Ok(res) => res,
Err(err) => {
- println!("Couldn't parse `enabledmod.json`: {}", err);
- println!("Rebuilding file.");
+ log::warn!("Couldn't parse `enabledmod.json`: {}", err);
+ log::warn!("Rebuilding file.");
- rebuild_enabled_mods_json(game_install.clone())?;
+ rebuild_enabled_mods_json(&game_install)?;
// Then try again
- let res = get_enabled_mods(game_install.clone())?;
- res
+ get_enabled_mods(&game_install)?
}
};
// Check if key exists
if res.get(mod_name.clone()).is_none() {
// If it doesn't exist, rebuild `enabledmod.json`
- println!("Value not found in `enabledmod.json`. Rebuilding file");
- rebuild_enabled_mods_json(game_install.clone())?;
+ log::info!("Value not found in `enabledmod.json`. Rebuilding file");
+ rebuild_enabled_mods_json(&game_install)?;
// Then try again
- res = get_enabled_mods(game_install)?;
+ res = get_enabled_mods(&game_install)?;
}
// Update value
@@ -129,7 +126,7 @@ pub fn set_mod_enabled_status(
}
/// Parses `manifest.json` for Thunderstore mod string
-fn parse_for_thunderstore_mod_string(nsmod_path: String) -> Result<String, anyhow::Error> {
+fn parse_for_thunderstore_mod_string(nsmod_path: &str) -> Result<String, anyhow::Error> {
let manifest_json_path = format!("{}/manifest.json", nsmod_path);
let ts_author_txt_path = format!("{}/thunderstore_author.txt", nsmod_path);
@@ -152,7 +149,7 @@ fn parse_for_thunderstore_mod_string(nsmod_path: String) -> Result<String, anyho
}
/// Parse `mods` folder for installed mods.
-fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>, anyhow::Error> {
+fn parse_installed_mods(game_install: &GameInstall) -> Result<Vec<NorthstarMod>, anyhow::Error> {
let ns_mods_folder = format!("{}/R2Northstar/mods/", game_install.game_path);
let paths = match std::fs::read_dir(ns_mods_folder) {
@@ -189,7 +186,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
let parsed_mod_json: ModJson = match json5::from_str(&data) {
Ok(parsed_json) => parsed_json,
Err(err) => {
- println!("Failed parsing {} with {}", mod_json_path, err.to_string());
+ log::warn!("Failed parsing {} with {}", mod_json_path, err.to_string());
continue;
}
};
@@ -198,7 +195,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
// Attempt legacy method for getting Thunderstore string first
Some(ts_mod_string) => Some(ts_mod_string),
// Legacy method failed
- None => match parse_for_thunderstore_mod_string(directory_str) {
+ None => match parse_for_thunderstore_mod_string(&directory_str) {
Ok(thunderstore_mod_string) => Some(thunderstore_mod_string),
Err(_err) => None,
},
@@ -209,7 +206,7 @@ fn parse_installed_mods(game_install: GameInstall) -> Result<Vec<NorthstarMod>,
let ns_mod = NorthstarMod {
name: parsed_mod_json.name,
version: parsed_mod_json.version,
- thunderstore_mod_string: thunderstore_mod_string,
+ thunderstore_mod_string,
enabled: false, // Placeholder
directory: mod_directory,
};
@@ -229,13 +226,13 @@ pub fn get_installed_mods_and_properties(
game_install: GameInstall,
) -> Result<Vec<NorthstarMod>, String> {
// Get actually installed mods
- let found_installed_mods = match parse_installed_mods(game_install.clone()) {
+ let found_installed_mods = match parse_installed_mods(&game_install) {
Ok(res) => res,
Err(err) => return Err(err.to_string()),
};
// Get enabled mods as JSON
- let enabled_mods: serde_json::Value = match get_enabled_mods(game_install) {
+ let enabled_mods: serde_json::Value = match get_enabled_mods(&game_install) {
Ok(enabled_mods) => enabled_mods,
Err(_) => serde_json::from_str("{}").unwrap(), // `enabledmods.json` not found, create empty object
};
@@ -256,7 +253,7 @@ pub fn get_installed_mods_and_properties(
Ok(installed_mods)
}
-async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<String, String> {
+async fn get_ns_mod_download_url(thunderstore_mod_string: &str) -> 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().unwrap().to_vec();
@@ -276,7 +273,7 @@ async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<Stri
for ns_mod in index {
// Iterate over all versions of a given mod
- for (_key, ns_mod) in &ns_mod.versions {
+ for ns_mod in ns_mod.versions.values() {
if ns_mod.url.contains(&ts_mod_string_url) {
dbg!(ns_mod.clone());
return Ok(ns_mod.url.clone());
@@ -288,21 +285,19 @@ async fn get_ns_mod_download_url(thunderstore_mod_string: String) -> Result<Stri
}
/// 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());
+async fn get_mod_dependencies(thunderstore_mod_string: &str) -> Result<Vec<String>, anyhow::Error> {
+ dbg!(thunderstore_mod_string);
// TODO: This will crash the thread if not internet connection exist. `match` should be used instead
let index = thermite::api::get_package_index().unwrap().to_vec();
// String replace works but more care should be taken in the future
- let ts_mod_string_url = thunderstore_mod_string.replace("-", "/");
+ 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 {
+ for ns_mod in ns_mod.versions.values() {
if ns_mod.url.contains(&ts_mod_string_url) {
dbg!(ns_mod.clone());
return Ok(ns_mod.deps.clone());
@@ -317,8 +312,8 @@ async fn get_mod_dependencies(
/// 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,
+ game_install: &GameInstall,
+ thunderstore_mod_string: &str,
) -> Result<(), String> {
// Get mods and download directories
let download_directory = format!(
@@ -328,11 +323,11 @@ pub async fn fc_download_mod_and_install(
let mods_directory = format!("{}/R2Northstar/mods/", game_install.game_path);
// Early return on empty string
- if thunderstore_mod_string.len() == 0 {
+ if thunderstore_mod_string.is_empty() {
return Err("Passed empty string".to_string());
}
- let deps = match get_mod_dependencies(thunderstore_mod_string.clone()).await {
+ let deps = match get_mod_dependencies(thunderstore_mod_string).await {
Ok(deps) => deps,
Err(err) => return Err(err.to_string()),
};
@@ -340,13 +335,13 @@ pub async fn fc_download_mod_and_install(
// Recursively install dependencies
for dep in deps {
- match fc_download_mod_and_install(game_install.clone(), dep).await {
+ match fc_download_mod_and_install(game_install, &dep).await {
Ok(()) => (),
Err(err) => {
- if err.to_string() == "Cannot install Northstar as a mod!" {
+ if err == "Cannot install Northstar as a mod!" {
continue; // For Northstar as a dependency, we just skip it
} else {
- return Err(err.to_string());
+ return Err(err);
}
}
};
@@ -361,7 +356,7 @@ pub async fn fc_download_mod_and_install(
}
// Get download URL for the specified mod
- let download_url = get_ns_mod_download_url(thunderstore_mod_string.clone()).await?;
+ let download_url = get_ns_mod_download_url(thunderstore_mod_string).await?;
// Create download directory
match std::fs::create_dir_all(download_directory.clone()) {
@@ -369,20 +364,19 @@ pub async fn fc_download_mod_and_install(
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
+ "{}/___flightcore-temp-download-dir/{thunderstore_mod_string}.zip",
+ game_install.game_path
);
// Download the mod
- let f = match thermite::core::manage::download_file(&download_url, path.clone()) {
+ let f = match thermite::core::manage::download_file(download_url, path.clone()) {
Ok(f) => f,
Err(e) => return Err(e.to_string()),
};
// Get Thunderstore mod author
- let author = thunderstore_mod_string.split("-").next().unwrap();
+ let author = thunderstore_mod_string.split('-').next().unwrap();
// Extract the mod to the mods directory
match thermite::core::manage::install_mod(author, &f, std::path::Path::new(&mods_directory)) {
@@ -397,7 +391,7 @@ pub async fn fc_download_mod_and_install(
}
/// Deletes a given Northstar mod folder
-fn delete_mod_folder(ns_mod_directory: String) -> Result<(), String> {
+fn delete_mod_folder(ns_mod_directory: &str) -> Result<(), String> {
let ns_mod_dir_path = std::path::Path::new(&ns_mod_directory);
// Safety check: Check whether `mod.json` exists and exit early if not
@@ -408,7 +402,7 @@ fn delete_mod_folder(ns_mod_directory: String) -> Result<(), String> {
return Err(format!("mod.json does not exist in {}", ns_mod_directory));
}
- match std::fs::remove_dir_all(&ns_mod_directory) {
+ match std::fs::remove_dir_all(ns_mod_directory) {
Ok(()) => Ok(()),
Err(err) => Err(format!("Failed deleting mod: {err}")),
}
@@ -432,7 +426,7 @@ pub fn delete_northstar_mod(game_install: GameInstall, nsmod_name: String) -> Re
// Installed mod matches specified mod
if installed_ns_mod.name == nsmod_name {
// Delete folder
- return delete_mod_folder(installed_ns_mod.directory);
+ return delete_mod_folder(&installed_ns_mod.directory);
}
}
@@ -483,7 +477,7 @@ pub fn delete_thunderstore_mod(
}
}
- if !(mod_folders_to_remove.len() > 0) {
+ if mod_folders_to_remove.is_empty() {
return Err(format!(
"No mods removed as no Northstar mods matching {thunderstore_mod_string} were found to be installed."
));
@@ -491,7 +485,7 @@ pub fn delete_thunderstore_mod(
// Delete given folders
for mod_folder in mod_folders_to_remove {
- delete_mod_folder(mod_folder)?;
+ delete_mod_folder(&mod_folder)?;
}
Ok(())
diff --git a/src-tauri/src/northstar/mod.rs b/src-tauri/src/northstar/mod.rs
index a043632c..7bd0b0a3 100644
--- a/src-tauri/src/northstar/mod.rs
+++ b/src-tauri/src/northstar/mod.rs
@@ -5,25 +5,23 @@ use crate::{check_mod_version_number, constants::CORE_MODS};
use anyhow::anyhow;
/// Returns the current Northstar version number as a string
-pub fn get_northstar_version_number(game_path: String) -> Result<String, anyhow::Error> {
- println!("{}", game_path);
- // println!("{:?}", install_type);
+pub fn get_northstar_version_number(game_path: &str) -> Result<String, anyhow::Error> {
+ log::info!("{}", game_path);
// TODO:
// Check if NorthstarLauncher.exe exists and check its version number
let profile_folder = "R2Northstar";
- let initial_version_number = match check_mod_version_number(format!(
- "{}/{}/mods/{}",
- game_path, profile_folder, CORE_MODS[0]
+ let initial_version_number = match check_mod_version_number(&format!(
+ "{game_path}/{profile_folder}/mods/{}",
+ CORE_MODS[0]
)) {
Ok(version_number) => version_number,
Err(err) => return Err(err),
};
for core_mod in CORE_MODS {
- let current_version_number = match check_mod_version_number(format!(
- "{}/{}/mods/{}",
- game_path, profile_folder, core_mod
+ let current_version_number = match check_mod_version_number(&format!(
+ "{game_path}/{profile_folder}/mods/{core_mod}",
)) {
Ok(version_number) => version_number,
Err(err) => return Err(err),
@@ -33,7 +31,7 @@ pub fn get_northstar_version_number(game_path: String) -> Result<String, anyhow:
return Err(anyhow!("Found version number mismatch"));
}
}
- println!("All mods same version");
+ log::info!("All mods same version");
Ok(initial_version_number)
}
diff --git a/src-tauri/src/platform_specific/linux.rs b/src-tauri/src/platform_specific/linux.rs
index eb9fbea6..4b9964e9 100644
--- a/src-tauri/src/platform_specific/linux.rs
+++ b/src-tauri/src/platform_specific/linux.rs
@@ -14,10 +14,10 @@ pub fn check_glibc_v() -> f32 {
let lddvl: Vec<&str> = lddva.split('\n').collect();
let lddvlo = &lddvl[0];
let reg = Regex::new(r"(2.\d{2}$)").unwrap();
- for caps in reg.captures_iter(lddvlo) {
+ if let Some(caps) = reg.captures_iter(lddvlo).next() {
return caps.get(1).unwrap().as_str().parse::<f32>().unwrap(); // theres prolly a better way ijdk how tho
}
- return 0.0; // this shouldnt ever be reached but it has to be here
+ 0.0 // this shouldnt ever be reached but it has to be here
}
/*
diff --git a/src-tauri/src/platform_specific/windows.rs b/src-tauri/src/platform_specific/windows.rs
index 21772b91..004beb6e 100644
--- a/src-tauri/src/platform_specific/windows.rs
+++ b/src-tauri/src/platform_specific/windows.rs
@@ -3,7 +3,7 @@ use anyhow::{anyhow, Result};
use crate::{check_is_valid_game_path, constants::TITANFALL2_ORIGIN_IDS};
-/// Runs a powershell command and parses output to get Titanfall2 install location on Origin
+/// Gets Titanfall2 install location on Origin
pub fn origin_install_location_detection() -> Result<String, anyhow::Error> {
// Iterate over known Titanfall2 Origin IDs
for origin_id in TITANFALL2_ORIGIN_IDS {
@@ -18,14 +18,14 @@ pub fn origin_install_location_detection() -> Result<String, anyhow::Error> {
return Ok(game_path_str.to_string());
}
Err(err) => {
- println!("{}", err.to_string());
+ log::warn!("{}", err);
continue; // Not a valid game path
}
}
}
}
Err(err) => {
- println!("Couldn't find {origin_id}: {err}")
+ log::warn!("Couldn't find {origin_id}: {err}")
}
}
}
diff --git a/src-tauri/src/repair_and_verify/mod.rs b/src-tauri/src/repair_and_verify/mod.rs
index 9d020f15..ecf5e1ec 100644
--- a/src-tauri/src/repair_and_verify/mod.rs
+++ b/src-tauri/src/repair_and_verify/mod.rs
@@ -15,9 +15,9 @@ pub fn verify_game_files(game_install: GameInstall) -> Result<String, String> {
#[tauri::command]
pub fn disable_all_but_core(game_install: GameInstall) -> Result<(), String> {
// Rebuild `enabledmods.json` first to ensure all mods are added
- rebuild_enabled_mods_json(game_install.clone())?;
+ rebuild_enabled_mods_json(&game_install)?;
- let current_mods = get_enabled_mods(game_install.clone())?;
+ let current_mods = get_enabled_mods(&game_install)?;
// Disable all mods, set core mods to enabled
for (key, _value) in current_mods.as_object().unwrap() {
@@ -37,7 +37,7 @@ pub fn disable_all_but_core(game_install: GameInstall) -> Result<(), String> {
/// If `force` is FALSE, bails on non-empty folder
/// If `force` is TRUE, deletes folder even if non-empty
pub fn clean_up_download_folder(
- game_install: GameInstall,
+ game_install: &GameInstall,
force: bool,
) -> Result<(), anyhow::Error> {
// Get download directory
@@ -51,7 +51,7 @@ pub fn clean_up_download_folder(
// dbg!(download_dir_contents);
let mut count = 0;
- download_dir_contents.inspect(|_| count += 1).for_each(drop);
+ download_dir_contents.for_each(|_| count += 1);
if count > 0 && !force {
return Err(anyhow!("Folder not empty, not deleting"));
@@ -84,7 +84,7 @@ pub fn get_log_list(game_install: GameInstall) -> Result<Vec<std::path::PathBuf>
}
}
- if log_files.len() > 0 {
+ if !log_files.is_empty() {
Ok(log_files)
} else {
Err("No logs found".to_string())
diff --git a/src-tauri/src/thunderstore/mod.rs b/src-tauri/src/thunderstore/mod.rs
index 9151ba7c..e9eb30d7 100644
--- a/src-tauri/src/thunderstore/mod.rs
+++ b/src-tauri/src/thunderstore/mod.rs
@@ -43,7 +43,7 @@ pub struct ThunderstoreModVersion {
/// Queries Thunderstore packages API
#[tauri::command]
pub async fn query_thunderstore_packages_api() -> Result<Vec<ThunderstoreMod>, String> {
- println!("Fetching Thunderstore API");
+ log::info!("Fetching Thunderstore API");
// Fetches
let url = "https://northstar.thunderstore.io/api/v1/package/";
@@ -66,7 +66,7 @@ pub async fn query_thunderstore_packages_api() -> Result<Vec<ThunderstoreMod>, S
};
// Remove some mods from listing
- let to_remove_set: HashSet<&str> = BLACKLISTED_MODS.iter().map(|s| s.as_ref()).collect();
+ let to_remove_set: HashSet<&str> = BLACKLISTED_MODS.iter().copied().collect();
let filtered_packages = parsed_json
.into_iter()
.filter(|package| !to_remove_set.contains(&package.full_name.as_ref()))