From f5d2a4f75ade23f21da18051a5469f2a084ec300 Mon Sep 17 00:00:00 2001 From: Dylan Madisetti Date: Thu, 23 May 2024 21:39:37 -0400 Subject: [PATCH] Misc fixes and updates (#72) --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 21 +++++++++-- src/client.rs | 85 +++++++++++++++++++++++++++---------------- src/interface/game.rs | 9 +++-- src/main.rs | 18 ++++----- src/util/paths.rs | 9 ++++- 7 files changed, 94 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e52f4d7..47c0a30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1316,7 +1316,7 @@ dependencies = [ [[package]] name = "steam-tui" -version = "0.2.2" +version = "0.3.0" dependencies = [ "atty", "crossterm 0.25.0", diff --git a/Cargo.toml b/Cargo.toml index 5b8069f..d844195 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "steam-tui" -version = "0.2.2" +version = "0.3.0" description = "TUI client for steamcmd." readme = "README.md" authors = ["madisetti "] diff --git a/README.md b/README.md index 97b1a03..5569dd1 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,12 @@ Just a simple TUI client for `steamcmd`. Allows for the graphical launching, updating, and downloading of steam games through a simple terminal client. Methodology informed by [steam-cli](https://github.com/berenm/steam-cli). +> [!NOTE] +> Steam no longer has a backend only mode. This client has become more limited, +> but still potenitally useful for those who don't want to use the steam client. +

- Example of rules_euler in action + Example of steam-tui in action

## Usage @@ -20,9 +24,17 @@ steamcmd ``` Launch the binary `steam-tui`, and rejoice :tada:. Help is in the client. -Unable to launch games? Pressing space will start a bare-bones steam client (no -graphics) in the background and will let you launch games that need steam -libraries or have some sort of DRM. +Unable to launch games? Pressing space will start a steam client and will let +you launch games that need steam libraries or have some sort of DRM. + +> [!WARNING] +> ## Still unable to launch games? +> +> You are not alone, but gaming on linux is getting better. You can define a +> custom script to launch a specific game by creating +> `$STEAM_TUI_SCRIPT_DIR/.sh`, steam TUI will pass in the file it +> thinks should be run, but you can ignore this and just do whatever. +> This reduces steam-tui to more of a launcher, but it's better than nothing. ## Features not in the help @@ -64,6 +76,7 @@ time into this. Thank you to those who have heeded my call for more coffee! + - @abowen @KDanisme @jharlan-hash (sponsored major update 0.3.0) - @MathiasSven (sponsored minor update 0.2.1) - @vaelund (sponsored major update 0.2.0) diff --git a/src/client.rs b/src/client.rs index dd06c8f..c4892b2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -203,11 +203,23 @@ fn execute( ) { let err = format!("{:?}", err); log!("Run script for:", name, "failed", err); - let mut reference = status.lock().unwrap(); - *reference = Some(GameStatus::msg( - &*reference, - &format!("Error: {}", err), - )); + { + let mut reference = status.lock().unwrap(); + *reference = Some(GameStatus::msg( + &*reference, + &format!("Error with script (trying direct): {}", err), + )); + } + // Try again as per #51 + run_process( + "steam".to_string(), + vec![ + "-silent".to_string(), + "-applaunch".to_string(), + id.to_string(), + ], + status, + ); } }); break; @@ -232,14 +244,17 @@ fn execute( .collect::>(); command.append(&mut args); log!("Finding entry"); - let entry = match steam_run_wrapper() { + let entry = match steam_run_wrapper(id) { Ok(wrapper) => wrapper.into_os_string().into_string().unwrap(), Err(STError::Problem(_)) => command.remove(0), Err(err) => { let mut reference = status.lock().unwrap(); - *reference = Some(GameStatus::msg(&*reference, "Could not find entry program.")); + *reference = Some(GameStatus::msg( + &*reference, + "Could not find entry program.", + )); return Err(err); - }, // unwrap and rewrap to explicitly note this is an err. + } // unwrap and rewrap to explicitly note this is an err. }; log!("Exits loop"); let status = status.clone(); @@ -248,28 +263,7 @@ fn execute( let mut reference = status.lock().unwrap(); *reference = Some(GameStatus::msg(&*reference, "running...")); } - match process::Command::new(entry).args(command).output() { - Ok(output) => { - let mut reference = status.lock().unwrap(); - *reference = - Some(GameStatus::msg(&*reference, "Fully Installed")); - log!( - "Launching stdout:", - &std::str::from_utf8(&output.stdout) - ); - log!( - "Launching stderr:", - &std::str::from_utf8(&output.stderr) - ); - } - Err(err) => { - let mut reference = status.lock().unwrap(); - *reference = Some(GameStatus::msg( - &*reference, - &format!("failed to launch: {}", err), - )); - } - } + run_process(entry, command, status); }); launched = true; break; @@ -444,6 +438,34 @@ fn execute( } } +fn run_process(entry: String, command: Vec, status: Arc>>) { + match process::Command::new(entry).args(command).output() { + Ok(output) => { + let stderr = output.stderr.clone(); + let stderr_snippet = &(String::from_utf8_lossy(&stderr)[..50]); + let mut reference = status.lock().unwrap(); + *reference = Some(GameStatus::msg( + &*reference, + &(match output.status.code() { + Some(0) => format!("ran (success)"), + Some(n) => format!("failed with code {}: ({}...)", n, stderr_snippet), + None => format!("Process terminated."), + }), + )); + + log!("Launching stdout:", &std::str::from_utf8(&output.stdout)); + log!("Launching stderr:", &std::str::from_utf8(&output.stderr)); + } + Err(err) => { + let mut reference = status.lock().unwrap(); + *reference = Some(GameStatus::msg( + &*reference, + &format!("failed to launch: {}", err), + )); + } + } +} + /// Manages and interfaces with SteamCmd threads. pub struct Client { receiver: Mutex>, @@ -547,6 +569,7 @@ impl Client { /// Binds data from 'app_status' to a `GameStatus` object. pub fn status(&self, id: i32) -> Result { + log!("Getting status for", id); let sender = self.sender.lock()?; sender.send(Command::Cli(format!("app_status {}", id)))?; let receiver = self.receiver.lock()?; @@ -615,7 +638,7 @@ fn keys_from_licenses(licenses: String) -> Vec { #[cfg(test)] mod tests { - use crate::client::{keys_from_licenses, Client, Command, State}; + use crate::client::{Client, Command, State}; use crate::util::error::STError; use std::sync::mpsc::channel; use std::sync::Arc; diff --git a/src/interface/game.rs b/src/interface/game.rs index 4f9ded6..764566c 100644 --- a/src/interface/game.rs +++ b/src/interface/game.rs @@ -100,11 +100,14 @@ impl Game { }; return Ok(game); } else { - return Err(STError::Problem("File a github issue. Something may have changed in the Steam definition.".to_string())); + return Err(STError::Problem( + "File a github issue. Something may have changed in the Steam definition." + .to_string(), + )); } } else { - log!("Cannot get key", key); - log!(parse(lines)); + log!("Cannot get key", key); + log!(parse(lines)); } } else { log!("Cannot get nest"); diff --git a/src/main.rs b/src/main.rs index bcc02c8..896c56c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,7 @@ use std::io; use crossterm::event::KeyCode; -use crossterm::terminal::{ - disable_raw_mode, enable_raw_mode, -}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use tui::style::{Color, Style}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; @@ -59,6 +57,11 @@ fn entry() -> Result<(), Box> { let events = Events::new(); let client = Client::new(); + // Login before cache, otherwise metadata is empty. + if !app.user.is_empty() { + client.login(&app.user)?; + } + // Attempt to load from cache. If not, continue as usual. let mut game_list: StatefulList = StatefulList::new(); let mut cached: bool = false; @@ -71,11 +74,6 @@ fn entry() -> Result<(), Box> { _ => game_list.restart(), } - // Login after cache load, since a failed login needs to show the failure screen. - if !app.user.is_empty() { - client.login(&app.user)?; - } - loop { terminal.draw(|frame| { let layout = App::build_layout(); @@ -238,7 +236,7 @@ fn entry() -> Result<(), Box> { break; } } - KeyCode::Char('\n') | KeyCode::Enter => { + KeyCode::Char('\n') | KeyCode::Enter => { let mut user = app.user.clone(); user.retain(|c| !c.is_whitespace()); terminal.hide_cursor()?; @@ -263,7 +261,7 @@ fn entry() -> Result<(), Box> { game_list.query = "".to_string(); img = update_img(&game_list.selected()); } - KeyCode::Char('\n') | KeyCode::Enter => { + KeyCode::Char('\n') | KeyCode::Enter => { terminal.hide_cursor()?; app.mode = Mode::Searched; } diff --git a/src/util/paths.rs b/src/util/paths.rs index 958072a..6f230fa 100644 --- a/src/util/paths.rs +++ b/src/util/paths.rs @@ -58,12 +58,17 @@ pub fn icon_directory() -> Result { pub fn steam_directory() -> Result { let dir = match env::var("STEAM_APP_DIR") { Ok(dir) => dir, - _ => "~/.steam/steam/Steamapps/common/".to_string(), + _ => "~/.steam/steam/steamapps/common/".to_string(), }; mkdir(dir) } -pub fn steam_run_wrapper() -> Result { +pub fn steam_run_wrapper(id: i32) -> Result { + // Custom script always takes precedence, then env, then hardcoded path. + let custom_script = script_directory()?.join(&format!("{}.sh", id)); + if custom_script.exists() { + return Ok(custom_script); + } let run = match env::var("STEAM_RUN_WRAPPER") { Ok(run) => run, _ => "~/.steam/bin32/steam-runtime/run.sh".to_string(),