can now set in the config file a launch command with --set-options

This commit is contained in:
ALEZ-DEV 2024-07-17 22:48:57 +02:00
parent dcf90310e3
commit 735ca25e08
5 changed files with 248 additions and 198 deletions

View File

@ -3,7 +3,13 @@ use clap::Parser;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Args { pub struct Args {
/// Pass launch options to tinker the behavior of the game /// Pass launch options to tinker the behavior of the game, this parameter have priotiy over the
/// --set-options param
#[arg(long)] #[arg(long)]
pub options: Option<String>, pub options: Option<String>,
/// Set to the config launch options to tinker the behavior of the game, you need to run this
/// command one time to set your launch options to the configuration
#[arg(long)]
pub set_options: Option<String>,
} }

View File

@ -0,0 +1,175 @@
use std::{path::PathBuf, str::FromStr, sync::Arc};
use babylonia_terminal_sdk::{
components::{
dxvk_component::{DXVK_DEV, DXVK_REPO},
proton_component::{ProtonComponent, PROTON_DEV, PROTON_REPO},
},
game_config::GameConfig,
game_manager::GameManager,
game_state::GameState,
};
use log::{debug, info};
use tokio::io::{AsyncBufReadExt, BufReader};
use wincompatlib::prelude::*;
use crate::{reporter::DownloadReporter, utils};
pub async fn run(launch_options: Option<String>) {
let mut proton_component: Option<ProtonComponent> = None;
let mut proton: Option<Proton> = None;
loop {
let state_result = GameState::get_current_state().await;
if let Err(error) = state_result {
info!("Something goes wrong : {:?}", error);
break;
}
let state = state_result.unwrap();
if state != GameState::ProtonNotInstalled && proton == None {
let proton_component = ProtonComponent::new(GameConfig::get_config_directory().await);
match proton_component.init_proton() {
Ok(p) => proton = Some(p),
Err(err) => panic!("{}", err),
};
}
match state {
GameState::ProtonNotInstalled => {
let release;
if utils::use_latest("Do you want to install latest version of Proton GE or a specific version of it?") {
release = 0;
} else {
release = utils::choose_release_version(
PROTON_DEV,
PROTON_REPO,
"Please, select a version of Proton GE to install.",
)
.await
.expect("Failed to fetch proton version!");
}
info!("Proton not installed, installing it...");
proton_component = Some(
GameManager::install_wine(
GameConfig::get_config_directory().await,
release,
Some(DownloadReporter::create(false)),
)
.await
.expect("Failed to install Wine"),
);
info!("Proton installed");
}
GameState::DXVKNotInstalled => {
let release;
if utils::use_latest(
"Do you want to install latest version of DXVK or a specific version of it?",
) {
release = 0;
} else {
release = utils::choose_release_version(
DXVK_DEV,
DXVK_REPO,
"Please, select a version of DXVK to install.",
)
.await
.expect("Failed to fetch DXVK version!");
}
info!("DXVK not installed, installing it...");
debug!("{:?}", proton_component);
GameManager::install_dxvk(
&proton.clone().unwrap(),
GameConfig::get_config_directory().await,
release,
Some(DownloadReporter::create(false)),
)
.await
.expect("Failed to installed DXVK");
info!("DXVK installed");
}
GameState::FontNotInstalled => {
info!("Fonts not installed, installing it...");
GameManager::install_font(&proton.clone().unwrap(), None::<Arc<DownloadReporter>>)
.await
.expect("Failed to install fonts");
info!("Fonts installed");
}
GameState::DependecieNotInstalled => {
info!("Dependecies not installed, installing it...");
GameManager::install_dependencies(&proton.clone().unwrap())
.await
.expect("Failed to install dependecies");
info!("Dependecies installed");
}
GameState::GameNotInstalled => {
info!("Game not installed, installing it...");
if GameConfig::get_game_dir().await.is_none() {
info!(
"You can choose where to put your game directory, (default '{}')",
GameConfig::get_config_directory().await.to_str().unwrap(),
);
info!("Please enter your wanted game directory : ");
let mut input = BufReader::new(tokio::io::stdin())
.lines()
.next_line()
.await
.unwrap();
let dir;
if let Some(i) = &mut input {
if i.is_empty() {
dir = GameConfig::get_config_directory().await;
} else {
dir = PathBuf::from_str(i).expect("This is not a valid directory!\n Please restart the launcher and put a valid path.");
}
} else {
dir = GameConfig::get_config_directory().await;
}
GameConfig::set_game_dir(Some(dir)).await.expect(
"Failed to save the game directory into the config file, please retry!",
);
}
GameManager::install_game(
GameConfig::get_game_dir().await.unwrap(),
DownloadReporter::create(false),
)
.await
.expect("Failed to install the game");
}
GameState::GameNeedUpdate => {
info!("Game need an update, updating it");
info!("This will restart the installation process...");
GameManager::update_game()
.await
.expect("Failed to start the installation process");
}
GameState::GameNotPatched => {
info!("Patching game...");
GameManager::patch_game(GameConfig::get_game_dir().await.unwrap())
.await
.expect("Failed to patch the game");
info!("Game patched!");
}
GameState::GameInstalled => {
break;
}
}
}
info!("Starting game...");
debug!("{:?}", proton);
GameManager::start_game(
&proton.unwrap(),
GameConfig::get_game_dir()
.await
.expect("Failed to start game, the game directory was not found"),
launch_options,
)
.await;
}

View File

@ -1,25 +1,14 @@
use std::{path::PathBuf, str::FromStr, sync::Arc}; use babylonia_terminal_sdk::game_config::GameConfig;
use babylonia_terminal_sdk::{
components::{
dxvk_component::{DXVK_DEV, DXVK_REPO},
proton_component::{ProtonComponent, PROTON_DEV, PROTON_REPO},
},
game_manager::GameManager,
game_state::{GameConfig, GameState},
};
use clap::Parser; use clap::Parser;
use log::{debug, info, LevelFilter}; use log::{debug, LevelFilter};
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use tokio::io::{AsyncBufReadExt, BufReader};
use wincompatlib::prelude::*;
mod arguments; mod arguments;
pub mod game;
pub mod reporter; pub mod reporter;
pub mod utils; pub mod utils;
use crate::arguments::Args; use crate::arguments::Args;
use crate::reporter::DownloadReporter;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -40,159 +29,11 @@ async fn main() {
let args = Args::parse(); let args = Args::parse();
debug!("Launch option -> {:?}", args.options); debug!("Launch option -> {:?}", args.options);
let mut proton_component: Option<ProtonComponent> = None; if let Some(command) = args.set_options {
let mut proton: Option<Proton> = None; GameConfig::set_launch_options(command)
.await
loop { .expect("Failed to save launch options into the config file");
let state_result = GameState::get_current_state().await;
if let Err(error) = state_result {
info!("Something goes wrong : {:?}", error);
break;
}
let state = state_result.unwrap();
if state != GameState::ProtonNotInstalled && proton == None {
let proton_component = ProtonComponent::new(GameConfig::get_config_directory().await);
match proton_component.init_proton() {
Ok(p) => proton = Some(p),
Err(err) => panic!("{}", err),
};
} }
match state { game::run(args.options).await;
GameState::ProtonNotInstalled => {
let release;
if utils::use_latest("Do you want to install latest version of Proton GE or a specific version of it?") {
release = 0;
} else {
release = utils::choose_release_version(
PROTON_DEV,
PROTON_REPO,
"Please, select a version of Proton GE to install.",
)
.await
.expect("Failed to fetch proton version!");
}
info!("Proton not installed, installing it...");
proton_component = Some(
GameManager::install_wine(
GameConfig::get_config_directory().await,
release,
Some(DownloadReporter::create(false)),
)
.await
.expect("Failed to install Wine"),
);
info!("Proton installed");
}
GameState::DXVKNotInstalled => {
let release;
if utils::use_latest(
"Do you want to install latest version of DXVK or a specific version of it?",
) {
release = 0;
} else {
release = utils::choose_release_version(
DXVK_DEV,
DXVK_REPO,
"Please, select a version of DXVK to install.",
)
.await
.expect("Failed to fetch DXVK version!");
}
info!("DXVK not installed, installing it...");
debug!("{:?}", proton_component);
GameManager::install_dxvk(
&proton.clone().unwrap(),
GameConfig::get_config_directory().await,
release,
Some(DownloadReporter::create(false)),
)
.await
.expect("Failed to installed DXVK");
info!("DXVK installed");
}
GameState::FontNotInstalled => {
info!("Fonts not installed, installing it...");
GameManager::install_font(&proton.clone().unwrap(), None::<Arc<DownloadReporter>>)
.await
.expect("Failed to install fonts");
info!("Fonts installed");
}
GameState::DependecieNotInstalled => {
info!("Dependecies not installed, installing it...");
GameManager::install_dependencies(&proton.clone().unwrap())
.await
.expect("Failed to install dependecies");
info!("Dependecies installed");
}
GameState::GameNotInstalled => {
info!("Game not installed, installing it...");
if GameConfig::get_game_dir().await.is_none() {
info!(
"You can choose where to put your game directory, (default '{}')",
GameConfig::get_config_directory().await.to_str().unwrap(),
);
info!("Please enter your wanted game directory : ");
let mut input = BufReader::new(tokio::io::stdin())
.lines()
.next_line()
.await
.unwrap();
let dir;
if let Some(i) = &mut input {
if i.is_empty() {
dir = GameConfig::get_config_directory().await;
} else {
dir = PathBuf::from_str(i).expect("This is not a valid directory!\n Please restart the launcher and put a valid path.");
}
} else {
dir = GameConfig::get_config_directory().await;
}
GameConfig::set_game_dir(Some(dir)).await.expect(
"Failed to save the game directory into the config file, please retry!",
);
}
GameManager::install_game(
GameConfig::get_game_dir().await.unwrap(),
DownloadReporter::create(false),
)
.await
.expect("Failed to install the game");
}
GameState::GameNeedUpdate => {
info!("Game need an update, updating it");
info!("This will restart the installation process...");
GameManager::update_game()
.await
.expect("Failed to start the installation process");
}
GameState::GameNotPatched => {
info!("Patching game...");
GameManager::patch_game(GameConfig::get_game_dir().await.unwrap())
.await
.expect("Failed to patch the game");
info!("Game patched!");
}
GameState::GameInstalled => {
break;
}
}
}
info!("Starting game...");
debug!("{:?}", proton);
GameManager::start_game(
&proton.unwrap(),
GameConfig::get_game_dir()
.await
.expect("Failed to start game, the game directory was not found"),
args.options,
)
.await;
} }

View File

@ -17,6 +17,7 @@ pub struct GameConfig {
pub game_dir: Option<PathBuf>, pub game_dir: Option<PathBuf>,
pub is_game_installed: bool, pub is_game_installed: bool,
pub is_game_patched: bool, pub is_game_patched: bool,
pub launch_options: Option<String>,
} }
impl GameConfig { impl GameConfig {
@ -69,6 +70,17 @@ impl GameConfig {
Err(_) => return Self::default(), Err(_) => return Self::default(),
} }
} }
pub async fn set_launch_options(command: String) -> anyhow::Result<()> {
let mut config = Self::get_config().await;
config.launch_options = Some(command);
Self::save_config(config).await?;
Ok(())
}
pub async fn get_launch_options() -> anyhow::Result<Option<String>> {
Ok(Self::get_config().await.launch_options)
}
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -87,6 +99,7 @@ impl Default for GameConfig {
game_dir: None, game_dir: None,
is_game_installed: false, is_game_installed: false,
is_game_patched: false, is_game_patched: false,
launch_options: None,
} }
} }
} }

View File

@ -1,7 +1,7 @@
use std::{ use std::{
io::{BufRead, BufReader}, io::{BufRead, BufReader},
path::PathBuf, path::PathBuf,
process::{Command, Stdio}, process::{Child, Command, Stdio},
sync::Arc, sync::Arc,
}; };
@ -171,13 +171,46 @@ impl GameManager {
pub async fn start_game(proton: &Proton, game_dir: PathBuf, options: Option<String>) { pub async fn start_game(proton: &Proton, game_dir: PathBuf, options: Option<String>) {
let proton_version = proton.wine().version().unwrap(); let proton_version = proton.wine().version().unwrap();
let exec_path = game_dir let binary_path = game_dir
.join(get_game_name()) .join(get_game_name())
.join(get_game_name_with_executable()); .join(get_game_name_with_executable());
debug!("Wine version : {:?}", proton_version); debug!("Wine version : {:?}", proton_version);
let mut child = if let Some(custom_command) = options { let mut child = if let Some(custom_command) = options {
Self::run(proton, binary_path, custom_command)
.await
.unwrap()
} else {
if let Some(custom_command) = GameConfig::get_launch_options().await.unwrap() {
Self::run(proton, binary_path, custom_command)
.await
.unwrap()
} else {
debug!("Starting game without --options");
proton.run(binary_path).unwrap()
}
};
let stdout = child.stdout.take().unwrap();
let mut bufread = BufReader::new(stdout);
let mut buf = String::new();
while let Ok(n) = bufread.read_line(&mut buf) {
if n > 0 {
info!("[Wine {:?}] : {}", proton_version, buf.trim());
buf.clear();
} else {
break;
}
}
}
async fn run(
proton: &Proton,
binary_path: PathBuf,
custom_command: String,
) -> Result<Child, std::io::Error> {
debug!("Starting game with --options -> {}", custom_command); debug!("Starting game with --options -> {}", custom_command);
let tokens: Vec<&str> = custom_command.split_whitespace().collect(); let tokens: Vec<&str> = custom_command.split_whitespace().collect();
@ -197,31 +230,13 @@ impl GameManager {
.join("proton"), .join("proton"),
) )
.arg("run") .arg("run")
.arg(exec_path) .arg(binary_path)
.args(&tokens[(index + 1)..tokens.len()]) .args(&tokens[(index + 1)..tokens.len()])
.envs(proton.get_envs()) .envs(proton.get_envs())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn() .spawn()
.unwrap()
} else {
debug!("Starting game without --options");
proton.run(exec_path).unwrap()
};
let stdout = child.stdout.take().unwrap();
let mut bufread = BufReader::new(stdout);
let mut buf = String::new();
while let Ok(n) = bufread.read_line(&mut buf) {
if n > 0 {
info!("[Wine {:?}] : {}", proton_version, buf.trim());
buf.clear();
} else {
break;
}
}
} }
} }