From dd1e870b279f990981dcbaff79c9db58fde18ba9 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 6 Apr 2023 15:06:38 +0200 Subject: feat: Add ability to launch via Steam to DevView (#178) * feat: Add ability to launch via Steam * document what get_titanfall_proton() does * revert explicit use of newly imported Path * Format source code to pass CI * Use new steamlocate compat_tool helper * cargo fmt * fix: Address various clippy issues Addresses clippy warnings caused by newly introduced code * fix: Cargo toml dependency formatting --- src-tauri/Cargo.lock | 56 +++++++++++++++++---- src-tauri/Cargo.toml | 4 +- src-tauri/src/constants.rs | 3 ++ src-tauri/src/lib.rs | 99 +++++++++++++++++++++++++++++++++++-- src-tauri/src/main.rs | 10 ++++ src-vue/src/plugins/store.ts | 26 ++++++++++ src-vue/src/views/DeveloperView.vue | 7 +++ 7 files changed, 191 insertions(+), 14 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ec1756b4..733831e4 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -97,6 +97,7 @@ dependencies = [ "json5", "libthermite", "log", + "open", "pretty_env_logger", "regex", "reqwest", @@ -800,16 +801,16 @@ version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] name = "dirs" -version = "3.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.0", ] [[package]] @@ -833,6 +834,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +dependencies = [ + "libc", + "redox_users", + "windows-sys 0.45.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1136,7 +1148,7 @@ dependencies = [ "serde_yaml", "sysinfo 0.23.13", "url", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -1864,6 +1876,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "keyvalues-serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f" +dependencies = [ + "keyvalues-parser", + "once_cell", + "paste", + "regex", + "serde", + "thiserror", +] + [[package]] name = "kuchiki" version = "0.8.1" @@ -3054,7 +3080,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -3652,14 +3678,16 @@ dependencies = [ [[package]] name = "steamlocate" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf364a9446f9f4423694ad8db5be8c478088970138e557ff2ddb3167fb0a2944" +checksum = "6b3962619891cd4c87dc0ae9332b97285eb59ab7cf65c9bec3fba62188390e74" dependencies = [ "dirs", "keyvalues-parser", + "keyvalues-serde", + "serde", "steamy-vdf", - "winreg", + "winreg 0.11.0", ] [[package]] @@ -5002,6 +5030,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "winres" version = "0.1.12" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c7d52d89..2267c239 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -29,7 +29,7 @@ tokio = { version = "1", features = ["full"] } sentry = "0.30" sentry-log = "0.30.0" # Find steam games -steamlocate = "1.0.2" +steamlocate = "1.2" # Error messages anyhow = "1.0" # libthermite for Northstar/mod install handling @@ -61,6 +61,8 @@ pretty_env_logger = "0.4.0" log = "0.4.17" # Extracting zip files easily zip-extract = "0.1.2" +# open urls +open = "3.2.0" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs index 0f4e9ab4..8c88df1e 100644 --- a/src-tauri/src/constants.rs +++ b/src-tauri/src/constants.rs @@ -29,6 +29,9 @@ 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", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ff1445e5..a3553448 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{env, fs, path::Path, time::Duration}; use anyhow::{anyhow, Context, Result}; @@ -14,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, @@ -92,7 +95,7 @@ pub fn find_game_install_location() -> Result { // 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); @@ -328,6 +331,93 @@ pub fn launch_northstar( )) } +/// Prepare Northstar and Launch through Steam using the Browser Protocol +pub fn launch_northstar_steam( + game_install: GameInstall, + _bypass_checks: Option, +) -> Result { + if !matches!(game_install.install_type, InstallType::STEAM) { + return Err("Titanfall2 was not installed via Steam".to_string()); + } + + 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()); + } + } + + // Switch to Titanfall2 directory to set everything up + if std::env::set_current_dir(game_install.game_path).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(); s.processes_by_name("Origin.exe").next().is_some() @@ -336,10 +426,11 @@ pub fn check_origin_running() -> bool { /// Checks if Northstar process is running pub fn check_northstar_running() -> bool { - sysinfo::System::new_all() - .processes_by_name("NorthstarLauncher.exe") + let s = sysinfo::System::new_all(); + s.processes_by_name("NorthstarLauncher.exe") .next() .is_some() + || s.processes_by_name("Titanfall2.exe").next().is_some() } /// Helps with converting release candidate numbers which are different on Thunderstore diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 21045db1..16b951ec 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -121,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, @@ -323,6 +324,15 @@ async fn launch_northstar_caller( launch_northstar(game_install, bypass_checks) } +#[tauri::command] +/// Launches Northstar +async fn launch_northstar_steam_caller( + game_install: GameInstall, + bypass_checks: Option, +) -> Result { + launch_northstar_steam(game_install, bypass_checks) +} + #[tauri::command] /// Installs the specified mod async fn install_mod_caller( diff --git a/src-vue/src/plugins/store.ts b/src-vue/src/plugins/store.ts index 60208879..cdbd6738 100644 --- a/src-vue/src/plugins/store.ts +++ b/src-vue/src/plugins/store.ts @@ -243,6 +243,32 @@ export const store = createStore({ break; } }, + async launchGameSteam(state: any, no_checks = false) { + let game_install = { + game_path: state.game_path, + install_type: state.install_type + } as GameInstall; + + await invoke("launch_northstar_steam_caller", { gameInstall: game_install, bypassChecks: no_checks }) + .then((message) => { + ElNotification({ + title: 'Success', + type: 'success', + position: 'bottom-right' + }); + }) + .catch((error) => { + console.error(error); + ElNotification({ + title: 'Error', + message: error, + type: 'error', + position: 'bottom-right' + }); + }); + + return; + }, async fetchReleaseNotes(state: FlightCoreStore) { if (state.releaseNotes.length !== 0) return; state.releaseNotes = await invoke("get_northstar_release_notes"); diff --git a/src-vue/src/views/DeveloperView.vue b/src-vue/src/views/DeveloperView.vue index bca473fb..7712756c 100644 --- a/src-vue/src/views/DeveloperView.vue +++ b/src-vue/src/views/DeveloperView.vue @@ -59,6 +59,10 @@ Launch Northstar (bypass all checks) + + Launch Northstar via Steam + +

Mod install:

@@ -156,6 +160,9 @@ export default defineComponent({ async launchGameWithoutChecks() { this.$store.commit('launchGame', true); }, + async launchGameViaSteam() { + this.$store.commit('launchGameSteam', true); + }, async getInstalledMods() { let game_install = { game_path: this.$store.state.game_path, -- cgit v1.2.3