diff options
-rw-r--r-- | src-tauri/Cargo.lock | 44 | ||||
-rw-r--r-- | src-tauri/Cargo.toml | 4 | ||||
-rw-r--r-- | src-tauri/src/constants.rs | 3 | ||||
-rw-r--r-- | src-tauri/src/github/release_notes.rs | 45 | ||||
-rw-r--r-- | src-tauri/src/main.rs | 1 | ||||
-rw-r--r-- | src-tauri/src/mod_management/mod.rs | 12 | ||||
-rw-r--r-- | src-tauri/src/platform_specific/mod.rs | 10 | ||||
-rw-r--r-- | src-tauri/src/platform_specific/windows.rs | 70 | ||||
-rw-r--r-- | src-tauri/tauri.conf.json | 2 | ||||
-rw-r--r-- | src-vue/src/views/DeveloperView.vue | 12 |
10 files changed, 177 insertions, 26 deletions
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index db7379e2..1e7eacbc 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1165,7 +1165,7 @@ dependencies = [ [[package]] name = "flightcore" -version = "2.23.2" +version = "2.24.0" dependencies = [ "anyhow", "async-recursion", @@ -1179,6 +1179,7 @@ dependencies = [ "octocrab", "open 5.3.0", "pretty_env_logger", + "rand 0.8.5", "regex", "remove-markdown-links", "reqwest 0.11.27", @@ -1716,9 +1717,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -2504,13 +2505,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2686,16 +2688,6 @@ dependencies = [ ] [[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] name = "num_enum" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5041,28 +5033,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2 0.5.5", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -5922,6 +5913,15 @@ dependencies = [ ] [[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] name = "windows-targets" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c9a559a7..210c76b8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "flightcore" -version = "2.23.2" +version = "2.24.0" description = "Mod-manager for Northstar" authors = ["https://github.com/R2NorthstarTools/FlightCore/graphs/contributors"] license = "MIT" @@ -65,6 +65,8 @@ semver = "1.0" # simplified filesystem access glob = "0.3.1" dirs = "5" +# Random number stuff +rand = "0.8.5" # Interacting with GitHub octocrab = "0.38.0" diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs index 47eeef19..3ad2d6e8 100644 --- a/src-tauri/src/constants.rs +++ b/src-tauri/src/constants.rs @@ -26,6 +26,9 @@ pub const BLACKLISTED_MODS: [&str; 3] = [ "ebkr-r2modman", ]; +/// List of Thunderstoremods that have some specific install requirements that makes them different from standard mods +pub const MODS_WITH_SPECIAL_REQUIREMENTS: [&str; 1] = ["NanohmProtogen-VanillaPlus"]; + /// Order in which the sections for release notes should be displayed pub const SECTION_ORDER: [&str; 11] = [ "feat", "fix", "docs", "style", "refactor", "build", "test", "i18n", "ci", "chore", "other", diff --git a/src-tauri/src/github/release_notes.rs b/src-tauri/src/github/release_notes.rs index e3a14537..4adfb24b 100644 --- a/src-tauri/src/github/release_notes.rs +++ b/src-tauri/src/github/release_notes.rs @@ -1,3 +1,4 @@ +use rand::prelude::SliceRandom; use serde::{Deserialize, Serialize}; use std::vec::Vec; use ts_rs::TS; @@ -168,9 +169,51 @@ pub async fn generate_release_note_announcement() -> Result<String, String> { let modders_info = "Mod compatibility should not be impacted"; let server_hosters_info = "REPLACE ME"; + let mut rng = rand::thread_rng(); + let attributes = vec![ + "adorable", + "amazing", + "beautiful", + "blithsome", + "brilliant", + "compassionate", + "dazzling", + "delightful", + "distinguished", + "elegant", + "enigmatic", + "enthusiastic", + "fashionable", + "fortuitous", + "friendly", + "generous", + "gleeful", + "gorgeous", + "handsome", + "lively", + "lovely", + "lucky", + "lustrous", + "marvelous", + "merry", + "mirthful", + "phantasmagorical", + "pretty", + "propitious", + "ravishing", + "sincere", + "sophisticated fellow", + "stupendous", + "vivacious", + "wonderful", + "zestful", + ]; + + let selected_attribute = attributes.choose(&mut rng).unwrap(); + // Build announcement string let return_string = format!( - r"Hello beautiful people <3 + r"Hello {selected_attribute} people <3 **Northstar `{current_ns_version}` is out!** {general_info} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ee7da27f..a9f484f5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -136,6 +136,7 @@ fn main() { northstar::profile::delete_profile, northstar::profile::fetch_profiles, northstar::profile::validate_profile, + platform_specific::check_cgnat, platform_specific::get_host_os, platform_specific::get_local_northstar_proton_wrapper_version, platform_specific::install_northstar_proton_wrapper, diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs index ebbcf431..2a018920 100644 --- a/src-tauri/src/mod_management/mod.rs +++ b/src-tauri/src/mod_management/mod.rs @@ -1,6 +1,6 @@ // This file contains various mod management functions -use crate::constants::{BLACKLISTED_MODS, CORE_MODS}; +use crate::constants::{BLACKLISTED_MODS, CORE_MODS, MODS_WITH_SPECIAL_REQUIREMENTS}; use async_recursion::async_recursion; use thermite::prelude::ThermiteError; @@ -609,6 +609,16 @@ pub async fn fc_download_mod_and_install( } } + // Prevent installing mods that have specific install requirements + for special_mod in MODS_WITH_SPECIAL_REQUIREMENTS { + if thunderstore_mod_string.contains(special_mod) { + return Err(format!( + "{} has special install requirements and cannot be installed with FlightCore", + thunderstore_mod_string + )); + } + } + // Get download URL for the specified mod let download_url = get_ns_mod_download_url(thunderstore_mod_string).await?; diff --git a/src-tauri/src/platform_specific/mod.rs b/src-tauri/src/platform_specific/mod.rs index 6fdb1ed1..4e0514d4 100644 --- a/src-tauri/src/platform_specific/mod.rs +++ b/src-tauri/src/platform_specific/mod.rs @@ -38,3 +38,13 @@ pub async fn get_local_northstar_proton_wrapper_version() -> Result<String, Stri #[cfg(target_os = "windows")] Err("Not supported on Windows".to_string()) } + +/// Check whether the current device might be behind a CGNAT +#[tauri::command] +pub async fn check_cgnat() -> Result<String, String> { + #[cfg(target_os = "linux")] + return Err("Not supported on Linux".to_string()); + + #[cfg(target_os = "windows")] + windows::check_cgnat().await +} diff --git a/src-tauri/src/platform_specific/windows.rs b/src-tauri/src/platform_specific/windows.rs index 678e5be5..fc6aab5d 100644 --- a/src-tauri/src/platform_specific/windows.rs +++ b/src-tauri/src/platform_specific/windows.rs @@ -1,5 +1,6 @@ /// Windows specific code use anyhow::{anyhow, Result}; +use std::net::Ipv4Addr; #[cfg(target_os = "windows")] use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey}; @@ -32,3 +33,72 @@ pub fn origin_install_location_detection() -> Result<String, anyhow::Error> { Err(anyhow!("No Origin / EA App install path found")) } + +/// Check whether the current device might be behind a CGNAT +pub async fn check_cgnat() -> Result<String, String> { + // Use external service to grap IP + let url = "https://api.ipify.org"; + let response = reqwest::get(url).await.unwrap().text().await.unwrap(); + + // Check if valid IPv4 address and return early if not + if response.parse::<Ipv4Addr>().is_err() { + return Err(format!("Not valid IPv4 address: {}", response)); + } + + let hops_count = run_tracert(&response)?; + Ok(format!("Counted {} hops to {}", hops_count, response)) +} + +/// Count number of hops in tracert output +fn count_hops(output: &str) -> usize { + // Split the output into lines + let lines: Vec<&str> = output.lines().collect(); + + // Filter lines that appear to represent hops + let hop_lines: Vec<&str> = lines + .iter() + .filter(|&line| line.contains("ms") || line.contains("*")) // TODO check if it contains just the `ms` surrounded by whitespace, otherwise it might falsely pick up some domain names as well + .cloned() + .collect(); + + // Return the number of hops + hop_lines.len() +} + +/// Run `tracert` +fn run_tracert(target_ip: &str) -> Result<usize, String> { + // Ensure valid IPv4 address to avoid prevent command injection + assert!(target_ip.parse::<Ipv4Addr>().is_ok()); + + // Execute the `tracert` command + let output = match std::process::Command::new("tracert") + .arg("-4") // Force IPv4 + .arg("-d") // Prevent resolving intermediate IP addresses + .arg("-w") // Set timeout to 1 second + .arg("1000") + .arg("-h") // Set max hop count + .arg("5") + .arg(target_ip) + .output() + { + Ok(res) => res, + Err(err) => return Err(format!("Failed running tracert: {}", err)), + }; + + // Check if the command was successful + if output.status.success() { + // Convert the output to a string + let stdout = + std::str::from_utf8(&output.stdout).expect("Invalid UTF-8 sequence in command output"); + println!("{}", stdout); + + // Count the number of hops + let hop_count = count_hops(stdout); + Ok(hop_count) + } else { + let stderr = std::str::from_utf8(&output.stderr) + .expect("Invalid UTF-8 sequence in command error output"); + println!("{}", stderr); + Err(format!("Failed collecting tracert output: {}", stderr)) + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 63721461..182c907d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "FlightCore", - "version": "2.23.2" + "version": "2.24.0" }, "tauri": { "allowlist": { diff --git a/src-vue/src/views/DeveloperView.vue b/src-vue/src/views/DeveloperView.vue index a08c73f3..19214157 100644 --- a/src-vue/src/views/DeveloperView.vue +++ b/src-vue/src/views/DeveloperView.vue @@ -65,6 +65,9 @@ <h3>Repair:</h3> + <el-button type="primary" @click="checkCgnat"> + Run tracert and collect hop count + </el-button> <el-button type="primary" @click="getInstalledMods"> Get installed mods @@ -330,6 +333,15 @@ export default defineComponent({ .then((message) => { showNotification(`NSProton Version`, message as string); }) .catch((error) => { showNotification(`Error`, error, "error"); }) }, + async checkCgnat() { + await invoke<string>("check_cgnat") + .then((message) => { + showNotification(message); + }) + .catch((error) => { + showErrorNotification(error); + }); + }, async copyReleaseNotesToClipboard() { navigator.clipboard.writeText(this.release_notes_text) .then(() => { |