Skip to content

Commit 6bb0cd3

Browse files
improve launcher UX
Squashed commit of the following: commit 2e28f93 Author: emerald <emerald_actual@proton.me> Date: Thu Mar 20 18:36:43 2025 -0400 clippy commit f2425f4 Author: emerald <emerald_actual@proton.me> Date: Thu Mar 20 18:33:11 2025 -0400 allow passing extra args when launching commit 16d40a6 Author: emerald <emerald@mecha.garden> Date: Thu Mar 20 14:30:28 2025 -0400 better launcher experience
1 parent a527dd9 commit 6bb0cd3

File tree

8 files changed

+239
-40
lines changed

8 files changed

+239
-40
lines changed

Cargo.lock

+67-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ copy_dir = "0.1.3"
6262
clap_complete = { version = "4.5", features = ["unstable-dynamic"] }
6363
tracing-appender = "0.2.3"
6464
clap_lex = "0.7.4"
65+
which = "7.0.2"
66+
steamlocate = { git = "https://github.com/WilliamVenner/steamlocate-rs", version = "2.0.1" }
6567
# rustyline = {version = "10.1.0", default_features = false}
6668

6769
[package.metadata.wix]

src/config.rs

+42-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use std::collections::HashSet;
22
use std::fmt::Display;
33
use std::fs;
44
use std::path::PathBuf;
5+
use std::process::Command;
56
use std::sync::LazyLock;
67

7-
use anyhow::anyhow;
88
use anyhow::Result;
9+
use anyhow::anyhow;
910
use directories::ProjectDirs;
10-
use figment::providers::{Env, Format, Serialized, Toml};
1111
use figment::Figment;
12-
use owo_colors::OwoColorize;
12+
use figment::providers::{Env, Format, Serialized, Toml};
1313
use serde::{Deserialize, Serialize};
1414

1515
use crate::IGNORED_DIRS;
@@ -40,6 +40,7 @@ pub struct Config {
4040
ignore: HashSet<String>,
4141
#[serde(default)]
4242
install_type: InstallType,
43+
#[serde(default)]
4344
is_server: bool,
4445
}
4546

@@ -145,7 +146,7 @@ pub fn default_ignore_list() -> HashSet<String> {
145146
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)]
146147
#[serde(rename_all = "lowercase")]
147148
pub enum InstallType {
148-
Steam,
149+
Steam(SteamType),
149150
Origin,
150151
EA,
151152
#[default]
@@ -157,3 +158,40 @@ impl Display for InstallType {
157158
write!(f, "{:?}", self)
158159
}
159160
}
161+
162+
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)]
163+
pub enum SteamType {
164+
#[default]
165+
Native,
166+
Flatpak,
167+
}
168+
169+
impl SteamType {
170+
pub fn to_launch_command(self) -> Command {
171+
match self {
172+
Self::Native => Command::new("steam"),
173+
Self::Flatpak => {
174+
let mut cmd = Command::new("flatpak");
175+
cmd.args(["run", "com.valvesoftware.Steam"]);
176+
cmd
177+
}
178+
}
179+
}
180+
181+
pub fn determine() -> Result<Self> {
182+
use which::which;
183+
184+
if which("steam").is_ok() {
185+
Ok(Self::Native)
186+
} else if Command::new("flatpak")
187+
.args(["info", "com.valvesoftware.Steam"])
188+
.spawn()?
189+
.wait()?
190+
.success()
191+
{
192+
Ok(Self::Flatpak)
193+
} else {
194+
Err(anyhow!("Unable to find steam installation?"))
195+
}
196+
}
197+
}

src/core/commands/northstar.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fs::{self, OpenOptions};
22
use std::path::{Path, PathBuf};
33
use std::time::Duration;
44

5-
use crate::config::DIRS;
5+
use crate::config::{DIRS, InstallType, SteamType};
66
use crate::model::Cache;
77
use crate::traits::{Answer, Indexed};
88
use crate::utils::{ensure_dir, init_msg};
@@ -11,6 +11,7 @@ use crate::{get_answer, modfile};
1111
use anyhow::{Result, anyhow};
1212
use indicatif::{ProgressBar, ProgressStyle};
1313
use owo_colors::OwoColorize;
14+
use steamlocate::SteamDir;
1415
use thermite::model::{Mod, ModJSON};
1516
use thermite::prelude::*;
1617
use tracing::{debug, warn};
@@ -35,14 +36,24 @@ pub fn northstar(commands: &NstarCommands) -> Result<()> {
3536
Ok(())
3637
}
3738

39+
fn get_titanfall() -> Result<PathBuf> {
40+
let maybe = SteamDir::locate_multiple()?
41+
.iter()
42+
.filter_map(|dir| dir.find_app(TITANFALL2_STEAM_ID).ok().flatten())
43+
.map(|(app, lib)| lib.resolve_app_dir(&app))
44+
.next();
45+
46+
maybe.ok_or_else(|| anyhow!("Failed to find titanfall 2"))
47+
}
48+
3849
fn init_ns(force: bool, path: Option<impl AsRef<Path>>, no_cache: bool) -> Result<()> {
3950
let (titanfall_path, steam) = if let Some(path) = path {
4051
(path.as_ref().to_path_buf(), false)
41-
} else if let Ok(dir) = titanfall2_dir() {
52+
} else if let Ok(dir) = get_titanfall() {
4253
(dir, true)
4354
} else {
44-
println!(
45-
"Couldn't automatically locate your Titanfall installation.\nPlease provide a path."
55+
eprintln!(
56+
"Couldn't automatically locate your Titanfall installation.\nPlease make sure it's installed or provide a path."
4657
);
4758
return Err(anyhow!("Unable to locate Titanfall 2 in Steam libraries"));
4859
};
@@ -56,7 +67,7 @@ fn init_ns(force: bool, path: Option<impl AsRef<Path>>, no_cache: bool) -> Resul
5667
new_config.set_game_dir(titanfall_path.clone());
5768

5869
if steam {
59-
new_config.set_install_type(crate::config::InstallType::Steam);
70+
new_config.set_install_type(InstallType::Steam(SteamType::determine()?));
6071
}
6172

6273
new_config.save()?;
@@ -113,7 +124,7 @@ fn init_ns(force: bool, path: Option<impl AsRef<Path>>, no_cache: bool) -> Resul
113124
let mut new_config = CONFIG.clone();
114125
new_config.set_game_dir(titanfall_path.clone());
115126
if steam {
116-
new_config.set_install_type(crate::config::InstallType::Steam);
127+
new_config.set_install_type(InstallType::Steam(SteamType::determine()?));
117128
}
118129
new_config.save()?;
119130

src/core/commands/profile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
path::{Path, PathBuf},
66
};
77

8-
use anyhow::{anyhow, Result};
8+
use anyhow::{Result, anyhow};
99
use clap::{Subcommand, ValueHint};
1010
use clap_complete::ArgValueCompleter;
1111
use copy_dir::copy_dir;

src/core/commands/run.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,61 @@
1+
use std::time::Duration;
2+
13
use anyhow::Result;
24
use owo_colors::OwoColorize;
5+
use thermite::TITANFALL2_STEAM_ID;
36

4-
use crate::config::InstallType::*;
57
use crate::config::CONFIG;
8+
use crate::config::InstallType::*;
69

7-
pub fn run(no_profile: bool) -> Result<()> {
10+
pub fn run(no_profile: bool, no_wait: bool, extra: Vec<String>) -> Result<()> {
811
match CONFIG.install_type() {
9-
Steam => {
12+
Steam(t) => {
1013
println!("Launching Titanfall 2 using steam...");
1114
let profile = if no_profile {
1215
String::new()
1316
} else {
1417
println!("Using profile {}", CONFIG.current_profile().bright_cyan());
1518
format!("-profile={}", CONFIG.current_profile())
1619
};
17-
open::that_detached(format!(
18-
"steam://run/{}//{profile} -northstar/",
19-
thermite::TITANFALL2_STEAM_ID
20-
))?;
20+
// open::that_detached(format!(
21+
// "steam://run/{}//{profile} -northstar/",
22+
// thermite::TITANFALL2_STEAM_ID
23+
// ))?;
24+
25+
let mut child = t
26+
.to_launch_command()
27+
.arg("-applaunch")
28+
.arg(TITANFALL2_STEAM_ID.to_string())
29+
.arg("-northstar")
30+
.arg(profile)
31+
.args(extra)
32+
.spawn()?;
33+
34+
if !no_wait {
35+
let spinner = indicatif::ProgressBar::new_spinner().with_message("Gaming...");
36+
spinner.enable_steady_tick(Duration::from_millis(100));
37+
child.wait()?;
38+
spinner.finish_and_clear();
39+
}
2140
}
2241
Origin => {
2342
println!("Launching Titanfall 2 using origin...");
2443
if CONFIG.current_profile() != "R2Northstar" {
25-
println!("{}Papa doesn't support using profiles with Origin. Make sure to manually set the launch args to use your profile.", "!! ".bright_red());
44+
println!(
45+
"{0} Papa doesn't support using profiles with Origin. Make sure to manually set the launch args to use your profile. {0}",
46+
"!!".bright_red()
47+
);
2648
}
2749
open::that_detached(format!(
2850
"origin://LaunchGame/{}",
2951
thermite::TITANFALL2_ORIGIN_IDS[0]
3052
))?;
3153
}
3254
Other => {
33-
println!("Can't launch the game for this type of installation.\nIf you think this is a mistake, try running {}.", "papa ns init".bright_cyan());
55+
println!(
56+
"Can't launch the game for this type of installation.\nIf you think this is a mistake, try running {}.",
57+
"papa ns init".bright_cyan()
58+
);
3459
}
3560
_ => todo!(),
3661
}

0 commit comments

Comments
 (0)