mirror of
https://github.com/ALEZ-DEV/Babylonia-terminal.git
synced 2025-12-16 17:38:51 +00:00
Merge pull request #6 from ALEZ-DEV/enable-arguments
Enable launch options when launching the game You can use `--options` or `--set-options` with this feature
This commit is contained in:
commit
f3898e6805
26
README.md
26
README.md
@ -26,7 +26,7 @@ You need Steam to be installed to run the game
|
|||||||
|
|
||||||
To install the cli version of the launcher, just start this command :
|
To install the cli version of the launcher, just start this command :
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cargo install --git https://github.com/ALEZ-DEV/Babylonia-terminal --bin
|
cargo install --git https://github.com/ALEZ-DEV/Babylonia-terminal --bin
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -37,6 +37,30 @@ babylonia-terminal-cli
|
|||||||
```
|
```
|
||||||
If you have any issue installing it on Steam deck or any other distro, [go check the wiki](https://github.com/ALEZ-DEV/Babylonia-terminal/wiki)
|
If you have any issue installing it on Steam deck or any other distro, [go check the wiki](https://github.com/ALEZ-DEV/Babylonia-terminal/wiki)
|
||||||
|
|
||||||
|
## Launch options
|
||||||
|
|
||||||
|
If you want to wrap mangohud, gamescope, gamemoderun or any other process over the game, you can't just wrap the launcher for the wrapper to work, example : `mangohud babylonia-terminal-cli`.
|
||||||
|
The launcher has a parameter you can use to pass special launch options, you can pass options like this :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# %command% will be replaced by the actual command that Babylonia-terminal will generate
|
||||||
|
babylonia-terminal-cli --options "<custom launch command> %command%"
|
||||||
|
```
|
||||||
|
|
||||||
|
So for example, if I want to wrap the game with mangohud :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
babylonia-terminal-cli --options "mangohud %command%"
|
||||||
|
```
|
||||||
|
|
||||||
|
But start the game with the `--options` parameter every time is a bit annoying, so you can just run the command with `--set-options` instead the first time you want to setup the launch options :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
babylonia-terminal-cli --set-options "mangohud %command%"
|
||||||
|
```
|
||||||
|
|
||||||
|
and the next time you want to start the launcher, you will just need to start it with `babylonia-terminal-cli`
|
||||||
|
|
||||||
## Special thank
|
## Special thank
|
||||||
|
|
||||||
Thank to [krypt0nn](https://github.com/krypt0nn) to made the [wincompatlib](https://github.com/krypt0nn/wincompatlib) library!
|
Thank to [krypt0nn](https://github.com/krypt0nn) to made the [wincompatlib](https://github.com/krypt0nn/wincompatlib) library!
|
||||||
|
|||||||
@ -3,7 +3,7 @@ name = "babylonia-terminal-cli"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["ALEZ-DEV <raidersfocus@gmail.com>"]
|
authors = ["ALEZ-DEV <raidersfocus@gmail.com>"]
|
||||||
description = "A launcher for a certain anime game "
|
description = "A launcher for a certain anime game on linux"
|
||||||
repository = "https://github.com/ALEZ-DEV/Babylonia-terminal"
|
repository = "https://github.com/ALEZ-DEV/Babylonia-terminal"
|
||||||
license = "LGPL-3.0-or-later"
|
license = "LGPL-3.0-or-later"
|
||||||
keywords = ["launcher", "game"]
|
keywords = ["launcher", "game"]
|
||||||
@ -16,6 +16,8 @@ name = "babylonia-terminal-cli"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.83"
|
anyhow = "1.0.83"
|
||||||
babylonia-terminal-sdk = { path = "./../babylonia-terminal-sdk" }
|
babylonia-terminal-sdk = { path = "./../babylonia-terminal-sdk" }
|
||||||
|
clap = { version = "4.5.7", features = ["derive"] }
|
||||||
|
derive = "1.0.0"
|
||||||
dialoguer = "0.11.0"
|
dialoguer = "0.11.0"
|
||||||
downloader = { git = "https://github.com/ALEZ-DEV/downloader" } # version = "0.2.7",
|
downloader = { git = "https://github.com/ALEZ-DEV/downloader" } # version = "0.2.7",
|
||||||
indicatif = "0.17.8"
|
indicatif = "0.17.8"
|
||||||
|
|||||||
15
babylonia-terminal-cli/src/arguments.rs
Normal file
15
babylonia-terminal-cli/src/arguments.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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, 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,22 +1,14 @@
|
|||||||
use std::{path::PathBuf, str::FromStr, sync::Arc};
|
use babylonia_terminal_sdk::game_config::GameConfig;
|
||||||
|
use clap::Parser;
|
||||||
use babylonia_terminal_sdk::{
|
use log::{debug, LevelFilter};
|
||||||
components::{
|
|
||||||
dxvk_component::{DXVK_DEV, DXVK_REPO},
|
|
||||||
proton_component::{ProtonComponent, PROTON_DEV, PROTON_REPO},
|
|
||||||
},
|
|
||||||
game_manager::GameManager,
|
|
||||||
game_state::GameState,
|
|
||||||
};
|
|
||||||
use log::{debug, info, LevelFilter};
|
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
|
||||||
use wincompatlib::prelude::*;
|
|
||||||
|
|
||||||
|
mod arguments;
|
||||||
|
pub mod game;
|
||||||
pub mod reporter;
|
pub mod reporter;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use crate::reporter::DownloadReporter;
|
use crate::arguments::Args;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@ -34,158 +26,14 @@ async fn main() {
|
|||||||
simple_logger.with_level(LevelFilter::Info).init().unwrap();
|
simple_logger.with_level(LevelFilter::Info).init().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut proton_component: Option<ProtonComponent> = None;
|
let args = Args::parse();
|
||||||
let mut proton: Option<Proton> = None;
|
debug!("Launch option -> {:?}", args.options);
|
||||||
|
|
||||||
loop {
|
if let Some(command) = args.set_options {
|
||||||
let state_result = GameState::get_current_state().await;
|
GameConfig::set_launch_options(command)
|
||||||
if let Err(error) = state_result {
|
.await
|
||||||
info!("Something goes wrong : {:?}", error);
|
.expect("Failed to save launch options into the config file");
|
||||||
break;
|
|
||||||
}
|
|
||||||
let state = state_result.unwrap();
|
|
||||||
|
|
||||||
if state != GameState::ProtonNotInstalled && proton == None {
|
|
||||||
let proton_component = ProtonComponent::new(GameState::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(
|
|
||||||
GameState::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(),
|
|
||||||
GameState::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 GameState::get_game_dir().await.is_none() {
|
|
||||||
info!(
|
|
||||||
"You can choose where to put your game directory, (default '{}')",
|
|
||||||
GameState::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 = GameState::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 = GameState::get_config_directory().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameState::set_game_dir(Some(dir)).await.expect(
|
|
||||||
"Failed to save the game directory into the config file, please retry!",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameManager::install_game(
|
|
||||||
GameState::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(GameState::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(),
|
|
||||||
GameState::get_game_dir()
|
|
||||||
.await
|
|
||||||
.expect("Failed to start game, the game directory was not found"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,7 +79,7 @@ impl GameComponent {
|
|||||||
match v {
|
match v {
|
||||||
Some(str) => {
|
Some(str) => {
|
||||||
if str == "done" {
|
if str == "done" {
|
||||||
if let Ok(size) = current_size {
|
if let Ok(_size) = current_size {
|
||||||
progress.done();
|
progress.done();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
105
babylonia-terminal-sdk/src/game_config.rs
Normal file
105
babylonia-terminal-sdk/src/game_config.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use dirs::home_dir;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::{
|
||||||
|
fs::{create_dir_all, read_to_string, File},
|
||||||
|
io::AsyncWriteExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GameConfig {
|
||||||
|
pub config_dir: PathBuf,
|
||||||
|
pub is_wine_installed: bool,
|
||||||
|
pub is_dxvk_installed: bool,
|
||||||
|
pub is_font_installed: bool,
|
||||||
|
pub is_dependecies_installed: bool,
|
||||||
|
pub game_dir: Option<PathBuf>,
|
||||||
|
pub is_game_installed: bool,
|
||||||
|
pub is_game_patched: bool,
|
||||||
|
pub launch_options: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameConfig {
|
||||||
|
pub async fn get_config_directory() -> PathBuf {
|
||||||
|
let path = home_dir().unwrap().join(".babylonia-terminal"); // I will try to change that to a dynamic one if people want to change the config dir
|
||||||
|
|
||||||
|
let _ = create_dir_all(path.clone()).await;
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_config_file_path() -> PathBuf {
|
||||||
|
Self::get_config_directory()
|
||||||
|
.await
|
||||||
|
.join("babylonia-terminal-config")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_game_dir(path: Option<PathBuf>) -> anyhow::Result<()> {
|
||||||
|
let mut config = Self::get_config().await;
|
||||||
|
config.game_dir = path;
|
||||||
|
Self::save_config(config).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_game_dir() -> Option<PathBuf> {
|
||||||
|
Self::get_config().await.game_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_get_config_file() -> anyhow::Result<File> {
|
||||||
|
let _ = tokio::fs::create_dir(Self::get_config_directory().await).await;
|
||||||
|
|
||||||
|
Ok(tokio::fs::File::create(Self::get_config_file_path().await).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save_config(config: Self) -> anyhow::Result<()> {
|
||||||
|
let mut file = Self::try_get_config_file().await?;
|
||||||
|
let content = serde_json::to_string(&config)?;
|
||||||
|
file.write_all(content.as_bytes()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_config() -> Self {
|
||||||
|
let content = match read_to_string(Self::get_config_file_path().await).await {
|
||||||
|
Err(_) => return Self::default(),
|
||||||
|
Ok(c) => c,
|
||||||
|
};
|
||||||
|
match serde_json::from_str::<Self>(&content) {
|
||||||
|
Ok(config) => return config,
|
||||||
|
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)]
|
||||||
|
pub struct GameConfigPath {
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
GameConfig {
|
||||||
|
config_dir: dirs::home_dir().unwrap().join(".babylonia-terminal"),
|
||||||
|
is_wine_installed: false,
|
||||||
|
is_dxvk_installed: false,
|
||||||
|
is_font_installed: false,
|
||||||
|
is_dependecies_installed: false,
|
||||||
|
game_dir: None,
|
||||||
|
is_game_installed: false,
|
||||||
|
is_game_patched: false,
|
||||||
|
launch_options: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,9 @@
|
|||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Child, Command, Stdio},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use downloader::progress::Reporter;
|
use downloader::progress::Reporter;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
@ -10,8 +15,8 @@ use crate::{
|
|||||||
component_downloader::ComponentDownloader, dxvk_component::DXVKComponent,
|
component_downloader::ComponentDownloader, dxvk_component::DXVKComponent,
|
||||||
game_component::GameComponent, proton_component::ProtonComponent,
|
game_component::GameComponent, proton_component::ProtonComponent,
|
||||||
},
|
},
|
||||||
|
game_config::GameConfig,
|
||||||
game_patcher,
|
game_patcher,
|
||||||
game_state::GameState,
|
|
||||||
utils::{get_game_name, get_game_name_with_executable, github_requester::GithubRequester},
|
utils::{get_game_name, get_game_name_with_executable, github_requester::GithubRequester},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,9 +36,9 @@ impl GameManager {
|
|||||||
|
|
||||||
wine_component.install(progress).await?;
|
wine_component.install(progress).await?;
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_wine_installed = true;
|
config.is_wine_installed = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(wine_component)
|
Ok(wine_component)
|
||||||
}
|
}
|
||||||
@ -52,9 +57,9 @@ impl GameManager {
|
|||||||
|
|
||||||
dxvk_component.install(progress).await?;
|
dxvk_component.install(progress).await?;
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_dxvk_installed = true;
|
config.is_dxvk_installed = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -103,9 +108,9 @@ impl GameManager {
|
|||||||
wine_with_proton_prefix.install_font(Font::Webdings)?;
|
wine_with_proton_prefix.install_font(Font::Webdings)?;
|
||||||
notify_fonts_progress(10, &progress);
|
notify_fonts_progress(10, &progress);
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_font_installed = true;
|
config.is_font_installed = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -124,9 +129,9 @@ impl GameManager {
|
|||||||
.wait()
|
.wait()
|
||||||
.expect("Something failed when waiting for the installation");
|
.expect("Something failed when waiting for the installation");
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_dependecies_installed = true;
|
config.is_dependecies_installed = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -140,9 +145,9 @@ impl GameManager {
|
|||||||
let game_component = GameComponent::new(game_dir);
|
let game_component = GameComponent::new(game_dir);
|
||||||
game_component.install(Some(progress)).await?;
|
game_component.install(Some(progress)).await?;
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_game_installed = true;
|
config.is_game_installed = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -150,10 +155,10 @@ impl GameManager {
|
|||||||
// this function just pass is_game_installed and is_game_patched to false,
|
// this function just pass is_game_installed and is_game_patched to false,
|
||||||
// so the launcher on the next iteration download the new file and delete the old one with the check process in the installation process
|
// so the launcher on the next iteration download the new file and delete the old one with the check process in the installation process
|
||||||
pub async fn update_game() -> anyhow::Result<()> {
|
pub async fn update_game() -> anyhow::Result<()> {
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_game_installed = false;
|
config.is_game_installed = false;
|
||||||
config.is_game_patched = false;
|
config.is_game_patched = false;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -164,16 +169,74 @@ impl GameManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start_game(proton: &Proton, game_dir: PathBuf) {
|
pub async fn start_game(proton: &Proton, game_dir: PathBuf, options: Option<String>) {
|
||||||
debug!("Wine version : {:?}", proton.wine().version().unwrap());
|
let proton_version = proton.wine().version().unwrap();
|
||||||
let mut child = proton
|
let binary_path = game_dir
|
||||||
.run(
|
|
||||||
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.arg("run")
|
||||||
child.wait().expect("The game failed to run");
|
.arg(binary_path)
|
||||||
|
.args(&tokens[(index + 1)..tokens.len()])
|
||||||
|
.envs(proton.get_envs())
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
game_state::GameState,
|
game_config::GameConfig,
|
||||||
utils::{get_game_name, get_game_name_with_executable},
|
utils::{get_game_name, get_game_name_with_executable},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -82,9 +82,9 @@ pub async fn patch_game(game_dir: PathBuf) -> anyhow::Result<()> {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut config = GameState::get_config().await;
|
let mut config = GameConfig::get_config().await;
|
||||||
config.is_game_patched = true;
|
config.is_game_patched = true;
|
||||||
GameState::save_config(config).await?;
|
GameConfig::save_config(config).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,4 @@
|
|||||||
use dirs::home_dir;
|
use crate::{game_config::GameConfig, utils::kuro_prod_api::GameInfo};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tokio::{
|
|
||||||
fs::{create_dir_all, read_to_string, File},
|
|
||||||
io::AsyncWriteExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::utils::kuro_prod_api::GameInfo;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum GameState {
|
pub enum GameState {
|
||||||
@ -20,91 +12,9 @@ pub enum GameState {
|
|||||||
GameInstalled,
|
GameInstalled,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GameConfig {
|
|
||||||
pub config_dir: PathBuf,
|
|
||||||
pub is_wine_installed: bool,
|
|
||||||
pub is_dxvk_installed: bool,
|
|
||||||
pub is_font_installed: bool,
|
|
||||||
pub is_dependecies_installed: bool,
|
|
||||||
pub game_dir: Option<PathBuf>,
|
|
||||||
pub is_game_installed: bool,
|
|
||||||
pub is_game_patched: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GameConfigPath {
|
|
||||||
path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GameConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
GameConfig {
|
|
||||||
config_dir: dirs::home_dir().unwrap().join(".babylonia-terminal"),
|
|
||||||
is_wine_installed: false,
|
|
||||||
is_dxvk_installed: false,
|
|
||||||
is_font_installed: false,
|
|
||||||
is_dependecies_installed: false,
|
|
||||||
game_dir: None,
|
|
||||||
is_game_installed: false,
|
|
||||||
is_game_patched: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GameState {
|
impl GameState {
|
||||||
pub async fn get_config_directory() -> PathBuf {
|
|
||||||
let path = home_dir().unwrap().join(".babylonia-terminal"); // I will try to change that to a dynamic one if people want to change the config dir
|
|
||||||
|
|
||||||
let _ = create_dir_all(path.clone()).await;
|
|
||||||
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_config_file_path() -> PathBuf {
|
|
||||||
GameState::get_config_directory()
|
|
||||||
.await
|
|
||||||
.join("babylonia-terminal-config")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_game_dir(path: Option<PathBuf>) -> anyhow::Result<()> {
|
|
||||||
let mut config = GameState::get_config().await;
|
|
||||||
config.game_dir = path;
|
|
||||||
GameState::save_config(config).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_game_dir() -> Option<PathBuf> {
|
|
||||||
GameState::get_config().await.game_dir
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn try_get_config_file() -> anyhow::Result<File> {
|
|
||||||
let _ = tokio::fs::create_dir(GameState::get_config_directory().await).await;
|
|
||||||
|
|
||||||
Ok(tokio::fs::File::create(GameState::get_config_file_path().await).await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn save_config(config: GameConfig) -> anyhow::Result<()> {
|
|
||||||
let mut file = GameState::try_get_config_file().await?;
|
|
||||||
let content = serde_json::to_string(&config)?;
|
|
||||||
file.write_all(content.as_bytes()).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_config() -> GameConfig {
|
|
||||||
let content = match read_to_string(GameState::get_config_file_path().await).await {
|
|
||||||
Err(_) => return GameConfig::default(),
|
|
||||||
Ok(c) => c,
|
|
||||||
};
|
|
||||||
match serde_json::from_str::<GameConfig>(&content) {
|
|
||||||
Ok(config) => return config,
|
|
||||||
Err(_) => return GameConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_current_state() -> anyhow::Result<Self> {
|
pub async fn get_current_state() -> anyhow::Result<Self> {
|
||||||
let config = GameState::get_config().await;
|
let config = GameConfig::get_config().await;
|
||||||
|
|
||||||
if !config.is_wine_installed {
|
if !config.is_wine_installed {
|
||||||
return Ok(GameState::ProtonNotInstalled);
|
return Ok(GameState::ProtonNotInstalled);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod components;
|
pub mod components;
|
||||||
|
pub mod game_config;
|
||||||
pub mod game_manager;
|
pub mod game_manager;
|
||||||
pub mod game_patcher;
|
pub mod game_patcher;
|
||||||
pub mod game_state;
|
pub mod game_state;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use serde::Serialize;
|
|||||||
use tokio::fs::read_to_string;
|
use tokio::fs::read_to_string;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
use crate::game_state::GameState;
|
use crate::game_config::GameConfig;
|
||||||
|
|
||||||
// start data ---------------------------------------------------------------------
|
// start data ---------------------------------------------------------------------
|
||||||
|
|
||||||
@ -157,13 +157,13 @@ impl GameInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cache_file_path() -> PathBuf {
|
async fn get_cache_file_path() -> PathBuf {
|
||||||
GameState::get_config_directory()
|
GameConfig::get_config_directory()
|
||||||
.await
|
.await
|
||||||
.join("version-cache")
|
.join("version-cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_in_cache(&self) -> anyhow::Result<()> {
|
async fn save_in_cache(&self) -> anyhow::Result<()> {
|
||||||
let _ = tokio::fs::create_dir(GameState::get_config_directory().await).await;
|
let _ = tokio::fs::create_dir(GameConfig::get_config_directory().await).await;
|
||||||
let mut file = tokio::fs::File::create(GameInfo::get_cache_file_path().await).await?;
|
let mut file = tokio::fs::File::create(GameInfo::get_cache_file_path().await).await?;
|
||||||
|
|
||||||
let content = serde_json::to_string(self)?;
|
let content = serde_json::to_string(self)?;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user