mirror of
https://github.com/ALEZ-DEV/Babylonia-terminal.git
synced 2025-12-15 17:08:51 +00:00
can now set in the config file a launch command with --set-options
This commit is contained in:
parent
dcf90310e3
commit
735ca25e08
@ -3,7 +3,13 @@ use clap::Parser;
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
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)]
|
||||
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>,
|
||||
}
|
||||
|
||||
175
babylonia-terminal-cli/src/game.rs
Normal file
175
babylonia-terminal-cli/src/game.rs
Normal 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;
|
||||
}
|
||||
@ -1,25 +1,14 @@
|
||||
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_manager::GameManager,
|
||||
game_state::{GameConfig, GameState},
|
||||
};
|
||||
use babylonia_terminal_sdk::game_config::GameConfig;
|
||||
use clap::Parser;
|
||||
use log::{debug, info, LevelFilter};
|
||||
use log::{debug, LevelFilter};
|
||||
use simple_logger::SimpleLogger;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use wincompatlib::prelude::*;
|
||||
|
||||
mod arguments;
|
||||
pub mod game;
|
||||
pub mod reporter;
|
||||
pub mod utils;
|
||||
|
||||
use crate::arguments::Args;
|
||||
use crate::reporter::DownloadReporter;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
@ -40,159 +29,11 @@ async fn main() {
|
||||
let args = Args::parse();
|
||||
debug!("Launch option -> {:?}", args.options);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
if let Some(command) = args.set_options {
|
||||
GameConfig::set_launch_options(command)
|
||||
.await
|
||||
.expect("Failed to save launch options into the config file");
|
||||
}
|
||||
|
||||
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;
|
||||
game::run(args.options).await;
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ pub struct GameConfig {
|
||||
pub game_dir: Option<PathBuf>,
|
||||
pub is_game_installed: bool,
|
||||
pub is_game_patched: bool,
|
||||
pub launch_options: Option<String>,
|
||||
}
|
||||
|
||||
impl GameConfig {
|
||||
@ -69,6 +70,17 @@ impl GameConfig {
|
||||
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)]
|
||||
@ -87,6 +99,7 @@ impl Default for GameConfig {
|
||||
game_dir: None,
|
||||
is_game_installed: false,
|
||||
is_game_patched: false,
|
||||
launch_options: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
io::{BufRead, BufReader},
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
process::{Child, Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@ -171,43 +171,25 @@ impl GameManager {
|
||||
|
||||
pub async fn start_game(proton: &Proton, game_dir: PathBuf, options: Option<String>) {
|
||||
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_with_executable());
|
||||
|
||||
debug!("Wine version : {:?}", proton_version);
|
||||
|
||||
let mut child = if let Some(custom_command) = options {
|
||||
debug!("Starting game with --options -> {}", custom_command);
|
||||
let tokens: Vec<&str> = custom_command.split_whitespace().collect();
|
||||
|
||||
// position of the %command%
|
||||
let index = tokens
|
||||
.iter()
|
||||
.position(|&s| s == "%command%")
|
||||
.expect("You forget to put %command% in your custom launch command");
|
||||
|
||||
Command::new(tokens.get(0).unwrap())
|
||||
.args(&tokens[0..(index - 1)])
|
||||
.arg(proton.python.as_os_str())
|
||||
.arg(
|
||||
GameConfig::get_config_directory()
|
||||
.await
|
||||
.join("proton")
|
||||
.join("proton"),
|
||||
)
|
||||
.arg("run")
|
||||
.arg(exec_path)
|
||||
.args(&tokens[(index + 1)..tokens.len()])
|
||||
.envs(proton.get_envs())
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
Self::run(proton, binary_path, custom_command)
|
||||
.await
|
||||
.unwrap()
|
||||
} else {
|
||||
debug!("Starting game without --options");
|
||||
proton.run(exec_path).unwrap()
|
||||
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();
|
||||
@ -223,6 +205,39 @@ impl GameManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(
|
||||
proton: &Proton,
|
||||
binary_path: PathBuf,
|
||||
custom_command: String,
|
||||
) -> Result<Child, std::io::Error> {
|
||||
debug!("Starting game with --options -> {}", custom_command);
|
||||
let tokens: Vec<&str> = custom_command.split_whitespace().collect();
|
||||
|
||||
// position of the %command%
|
||||
let index = tokens
|
||||
.iter()
|
||||
.position(|&s| s == "%command%")
|
||||
.expect("You forget to put %command% in your custom launch command");
|
||||
|
||||
Command::new(tokens.get(0).unwrap())
|
||||
.args(&tokens[0..(index - 1)])
|
||||
.arg(proton.python.as_os_str())
|
||||
.arg(
|
||||
GameConfig::get_config_directory()
|
||||
.await
|
||||
.join("proton")
|
||||
.join("proton"),
|
||||
)
|
||||
.arg("run")
|
||||
.arg(binary_path)
|
||||
.args(&tokens[(index + 1)..tokens.len()])
|
||||
.envs(proton.get_envs())
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_fonts_progress<P>(nbr: u64, progress: &Option<Arc<P>>)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user