Skip to content

Commit

Permalink
Request steam app install for steam app id dependencies (#239)
Browse files Browse the repository at this point in the history
* Request steam app install for steam app id dependencies

* Add return

* steam_app_id_install_wait_in_seconds

* Update steam app install locate to use state flags

* Check for fully installed flag on initial app check
  • Loading branch information
d10sfan authored Sep 26, 2023
1 parent af70133 commit 021db9a
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 28 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ A configuration json file named `config.json` will be located in the `~/.config/
- enable_steam_cloud - If this parameter is set to true, each engine & game with steam cloud support will enable it. This defaults to false. This can also be done by setting ```LUX_STEAM_CLOUD=1 %command%``` in the launch options of a particular game.
- hash_check_install - If this parameter is set to true, hash checking will be enabled for each file the engine needs to run, so that if a file has already been extracted/installed, it won't do it again until there is an update to one of the engine files. This is defaulted to false.
- close_client_on_launch - If this parameter is set to true, the client will close as soon as the game launches, instead of waiting for the engine to complete like normal behavior. This is defaulted to false.
- steam_app_id_install_wait_in_seconds - How long to wait for the install to complete when installing game dependencies. Defaults to 600.
Logs will be written to file if ```LUX_WRITE_LOGGING=1``` is set. The log file will be located at ```~/.local/state/luxtorpeda/luxtorpeda.log```.
Expand Down
45 changes: 34 additions & 11 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,17 +303,6 @@ impl LuxClient {
}
}

if let Some(app_ids_deps) = &game_info.app_ids_deps {
match package::get_app_id_deps_paths(app_ids_deps) {
Some(()) => {
info!("download_all. get_app_id_deps_paths completed");
}
None => {
info!("download_all. warning: get_app_id_deps_paths not completed");
}
}
}

let downloads = package::json_to_downloads(app_id.as_str(), &game_info).unwrap();

if downloads.is_empty() {
Expand Down Expand Up @@ -612,6 +601,40 @@ impl LuxClient {
}
};

if let Some(app_ids_deps) = &game_info.app_ids_deps {
let status_obj = StatusObj {
log_line: Some("Checking for steam app dependency paths".to_string()),
..Default::default()
};
let status_str = serde_json::to_string(&status_obj).unwrap();
sender_err.send(status_str).unwrap();

let sender_paths = sender_err.clone();

match package::get_app_id_deps_paths(app_ids_deps, false, &sender_paths) {
Ok(()) => {
info!("run_game. get_app_id_deps_paths completed");
}
Err(err) => {
let error_message = std::format!(
"run_game. error: get_app_id_deps_paths not completed, error: {:?}",
err
);

error!("{}", error_message);

let status_obj = StatusObj {
error: Some(error_message),
..Default::default()
};
let status_str = serde_json::to_string(&status_obj).unwrap();
sender_err.send(status_str).unwrap();

return;
}
}
}

if let Some(setup_info) = &game_info.setup {
if !after_setup_question_mode && !package::is_setup_complete(setup_info) {
match command::process_setup_details(setup_info) {
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct Config {
pub enable_steam_cloud: bool,
pub hash_check_install: bool,
pub close_client_on_launch: bool,
pub steam_app_id_install_wait_in_seconds: u32,
}

impl Default for Config {
Expand All @@ -25,6 +26,7 @@ impl Default for Config {
enable_steam_cloud: false,
hash_check_install: true,
close_client_on_launch: false,
steam_app_id_install_wait_in_seconds: 600,
}
}
}
Expand Down
199 changes: 182 additions & 17 deletions src/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use std::io;
use std::io::Write;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::thread;
use std::time::Duration;
use tar::Archive;
use xz2::read::XzDecoder;

Expand Down Expand Up @@ -634,38 +637,200 @@ pub fn get_game_info(app_id: &str) -> io::Result<package_metadata::Game> {
}
}

pub fn get_app_id_deps_paths(deps: &Vec<u32>) -> Option<()> {
pub fn get_app_id_deps_paths(
deps: &Vec<u32>,
retry: bool,
sender: &std::sync::mpsc::Sender<String>,
) -> io::Result<()> {
let mut steam_app_id_install_completed = false;
match SteamDir::locate() {
Some(mut steamdir) => {
for app_id in deps {
info!("get_app_id_deps_paths. searching for app id {}.", app_id);

match steamdir.app(app_id) {
Some(app_location) => {
let mut found_app = false;
let app_location_path = app_location.path.clone();
let app_location_str =
&app_location_path.into_os_string().into_string().unwrap();
info!(
"get_app_id_deps_paths. app id {} found at {:#?}.",
app_id, app_location_str
);
user_env::set_env_var(
&std::format!("DEPPATH_{}", app_id).to_string(),
app_location_str,
);
}
None => {
info!("get_app_id_deps_paths. app id {} not found.", app_id);

if app_location_path.exists()
&& app_location_path.read_dir()?.next().is_some()
{
if let Some(state_flags) = &app_location.state_flags {
for state_flag in state_flags.iter() {
if let steamlocate::steamapp::StateFlag::FullyInstalled =
state_flag
{
found_app = true;
break;
}
}
}
}

if found_app {
let app_location_str =
&app_location_path.into_os_string().into_string().unwrap();
let info_message = std::format!(
"get_app_id_deps_paths. app id {} found at {:#?}.",
app_id,
app_location_str
);
info!("{}", info_message);
user_env::set_env_var(
&std::format!("DEPPATH_{}", app_id).to_string(),
app_location_str,
);

let status_obj = client::StatusObj {
log_line: Some(info_message),
..Default::default()
};
let status_str = serde_json::to_string(&status_obj).unwrap();
sender.send(status_str).unwrap();
} else {
match get_app_id_dep_path_retry(app_id, retry, &sender.clone()) {
Ok(()) => steam_app_id_install_completed = true,
Err(err) => {
return Err(err);
}
}
}
}
None => match get_app_id_dep_path_retry(app_id, retry, &sender.clone()) {
Ok(()) => steam_app_id_install_completed = true,
Err(err) => {
return Err(err);
}
},
}
}

Some(())
if steam_app_id_install_completed {
get_app_id_deps_paths(deps, true, sender)
} else {
Ok(())
}
}
None => {
info!("get_app_id_deps_paths. steamdir not found.");
None
let error_message = "get_app_id_deps_paths. steamdir not found.";
error!("{}", error_message);
Err(Error::new(ErrorKind::Other, error_message))
}
}
}

pub fn get_app_id_dep_path_retry(
app_id: &u32,
retry: bool,
sender: &std::sync::mpsc::Sender<String>,
) -> io::Result<()> {
let error_message = std::format!(
"get_app_id_deps_paths. app id {} not found. retry = {}",
app_id,
retry
);
info!("{}", error_message);

if retry {
Err(Error::new(ErrorKind::Other, error_message))
} else {
let status_obj = client::StatusObj {
log_line: Some(std::format!(
"get_app_id_deps_paths. app id {} requesting install. This game needs this dependency to work, please press install on the steam dialog and wait for the install to complete. Luxtorpeda will launch the game when ready.",
app_id
)),
..Default::default()
};
let status_str = serde_json::to_string(&status_obj).unwrap();
sender.send(status_str).unwrap();
match request_steam_app_id_install(app_id) {
Ok(()) => {
let info_message = std::format!("steam_app_id_install_completed for {}", app_id);
info!("{}", info_message);
let status_obj = client::StatusObj {
log_line: Some(info_message),
..Default::default()
};
let status_str = serde_json::to_string(&status_obj).unwrap();
sender.send(status_str).unwrap();
Ok(())
}
Err(err) => {
let error_message = std::format!(
"get_app_id_deps_paths. app id {} not found. error = {:?}",
app_id,
err
);
error!("{}", error_message);
Err(Error::new(ErrorKind::Other, error_message))
}
}
}
}

pub fn request_steam_app_id_install(app_id: &u32) -> io::Result<()> {
match Command::new("xdg-open")
.args([std::format!("steam://install/{}", app_id)])
.spawn()
{
Ok(mut child) => {
match child.wait() {
Ok(status) => {
info!("request_steam_app_id_install returned with {}", status);
let mut tries = 1;
let mut found_app = false;

let config = config::Config::from_config_file();
let num_tries = config.steam_app_id_install_wait_in_seconds / 5;

while tries < num_tries {
// wait for 5 seconds
info!(
"request_steam_app_id_install checking app of {} installed tries = {}",
app_id, tries
);
if let Some(mut steamdir) = SteamDir::locate() {
if let Some(app_metadata) = steamdir.app(app_id) {
if let Some(state_flags) = &app_metadata.state_flags {
for state_flag in state_flags.iter() {
if let steamlocate::steamapp::StateFlag::FullyInstalled =
state_flag
{
info!(
"request_steam_app_id_install found app location of {}",
app_id
);
found_app = true;
break;
}
}

if found_app {
break;
}
}
}
}
tries += 1;
thread::sleep(Duration::from_secs(5));
}

if found_app {
Ok(())
} else {
let error_message = std::format!(
"request_steam_app_id_install. app not found of {}.",
app_id
);
error!("{}", error_message);
Err(Error::new(ErrorKind::Other, error_message))
}
}
Err(err) => Err(err),
}
}
Err(err) => Err(err),
}
}

Expand Down

0 comments on commit 021db9a

Please sign in to comment.