the-honkers-railway-launcher/src/main.rs

309 lines
11 KiB
Rust
Raw Normal View History

use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
2023-01-18 16:37:53 +00:00
use relm4::prelude::*;
2022-06-28 21:59:20 +00:00
use anime_launcher_sdk::config::ConfigExt;
2023-04-22 21:32:08 +00:00
use anime_launcher_sdk::star_rail::config::{Config, Schema};
2023-04-22 21:32:08 +00:00
use anime_launcher_sdk::star_rail::states::LauncherState;
use anime_launcher_sdk::star_rail::consts::*;
use anime_launcher_sdk::anime_game_core::prelude::*;
2023-04-22 21:32:08 +00:00
use anime_launcher_sdk::anime_game_core::star_rail::prelude::*;
2022-07-27 15:37:52 +00:00
use anime_launcher_sdk::sessions::SessionsExt;
2023-08-20 15:05:42 +00:00
use anime_launcher_sdk::star_rail::sessions::Sessions;
use tracing_subscriber::prelude::*;
use tracing_subscriber::filter::*;
pub mod move_files;
2023-01-18 16:37:53 +00:00
pub mod i18n;
pub mod background;
pub mod ui;
2022-06-28 21:59:20 +00:00
use ui::main::*;
use ui::first_run::main::*;
2023-04-22 21:32:08 +00:00
pub const APP_ID: &str = "moe.launcher.the-honkers-railway-launcher";
pub const APP_RESOURCE_PATH: &str = "/moe/launcher/the-honkers-railway-launcher";
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_DEBUG: bool = cfg!(debug_assertions);
/// Sets to `true` when the `App` component is ready (fully initialized)
pub static READY: AtomicBool = AtomicBool::new(false);
// TODO: get rid of using this function in all the components' events
// e.g. by converting preferences pages into Relm4 Components
/// Check if the app is ready
pub fn is_ready() -> bool {
READY.load(Ordering::Relaxed)
}
lazy_static::lazy_static! {
/// Config loaded on the app's start. Use `Config::get()` to get up to date config instead.
/// This one is used to prepare some launcher UI components on start
pub static ref CONFIG: Schema = Config::get().expect("Failed to load config");
2023-05-21 09:41:47 +00:00
pub static ref GAME: Game = Game::new(CONFIG.game.path.for_edition(CONFIG.launcher.edition), CONFIG.launcher.edition);
2023-06-12 21:33:43 +00:00
/// Path to launcher folder. Standard is `$HOME/.local/share/honkers-railway-launcher`
2023-03-31 08:21:06 +00:00
pub static ref LAUNCHER_FOLDER: PathBuf = launcher_dir().expect("Failed to get launcher folder");
2023-06-12 21:33:43 +00:00
/// Path to launcher's cache folder. Standard is `$HOME/.cache/honkers-railway-launcher`
pub static ref CACHE_FOLDER: PathBuf = cache_dir().expect("Failed to get launcher's cache folder");
2023-06-12 21:33:43 +00:00
/// Path to `debug.log` file. Standard is `$HOME/.local/share/honkers-railway-launcher/debug.log`
pub static ref DEBUG_FILE: PathBuf = LAUNCHER_FOLDER.join("debug.log");
/// Path to `background` file. Standard is `$HOME/.local/share/honkers-railway-launcher/background`
pub static ref BACKGROUND_FILE: PathBuf = LAUNCHER_FOLDER.join("background");
/// Path to `background-primary` file. Standard is `$HOME/.local/share/anime-game-launcher/background-primary`
pub static ref BACKGROUND_PRIMARY_FILE: PathBuf = LAUNCHER_FOLDER.join("background-primary");
/// Path to `.keep-background` file. Used to mark launcher that it shouldn't update background picture
2023-06-12 21:33:43 +00:00
///
/// Standard is `$HOME/.local/share/honkers-railway-launcher/.keep-background`
pub static ref KEEP_BACKGROUND_FILE: PathBuf = LAUNCHER_FOLDER.join(".keep-background");
/// Path to `.first-run` file. Used to mark launcher that it should run FirstRun window
2023-06-12 21:33:43 +00:00
///
/// Standard is `$HOME/.local/share/honkers-railway-launcher/.first-run`
pub static ref FIRST_RUN_FILE: PathBuf = LAUNCHER_FOLDER.join(".first-run");
2024-03-24 07:56:04 +00:00
/// Global app's css
static ref GLOBAL_CSS: String = format!("
progressbar > text {{
margin-bottom: 4px;
}}
window.classic-style {{
background: url(\"file://{}\");
background-repeat: no-repeat;
background-size: cover;
}}
window.classic-style progressbar {{
background-color: #00000020;
border-radius: 16px;
padding: 8px 16px;
}}
window.classic-style progressbar:hover {{
background-color: #00000060;
color: #ffffff;
transition-duration: 0.5s;
transition-timing-function: linear;
}}
.round-bin {{
border-radius: 24px;
}}
", BACKGROUND_PRIMARY_FILE.to_string_lossy());
}
fn main() -> anyhow::Result<()> {
// Setup custom panic handler
human_panic::setup_panic!(human_panic::metadata!());
// Create launcher folder if it isn't
if !LAUNCHER_FOLDER.exists() {
std::fs::create_dir_all(LAUNCHER_FOLDER.as_path()).expect("Failed to create launcher folder");
// This one is kinda critical buy well, I can't do something with it
std::fs::write(FIRST_RUN_FILE.as_path(), "").expect("Failed to create .first-run file");
// Set initial launcher language based on system language
// CONFIG is initialized lazily so it will contain following changes as well
let mut config = Config::get().expect("Failed to get config");
2023-02-26 13:14:40 +00:00
config.launcher.language = i18n::format_lang(&i18n::get_default_lang());
Config::update_raw(config).expect("Failed to update config");
}
// Force debug output
let mut force_debug = false;
// Run the game
let mut run_game = false;
2023-09-28 15:38:17 +00:00
// Force run the game
let mut just_run_game = false;
2023-09-28 15:38:17 +00:00
// Force disable verbose tracing output in stdout
let mut no_verbose_tracing = false;
let args = std::env::args().collect::<Vec<_>>();
2024-03-24 09:23:15 +00:00
let mut gtk_args = Vec::new();
// Parse arguments
for i in 0..args.len() {
2024-03-24 09:23:15 +00:00
match args[i].as_str() {
"--debug" => force_debug = true,
"--run-game" => run_game = true,
"--just-run-game" => just_run_game = true,
"--no-verbose-tracing" => no_verbose_tracing = true,
"--session" => {
// Switch active session prior running the app
if let Some(session) = args.get(i + 1) {
Sessions::set_current(session.to_owned())?;
}
},
arg => gtk_args.push(arg.to_string())
}
}
// Prepare stdout logger
let stdout = tracing_subscriber::fmt::layer()
.pretty()
.with_filter({
if APP_DEBUG || force_debug {
LevelFilter::TRACE
} else {
LevelFilter::WARN
}
})
.with_filter(filter_fn(move |metadata| {
!metadata.target().contains("rustls") && !no_verbose_tracing
}));
// Prepare debug file logger
let file = std::fs::File::create(DEBUG_FILE.as_path())?;
let debug_log = tracing_subscriber::fmt::layer()
.pretty()
.with_ansi(false)
.with_writer(std::sync::Arc::new(file))
.with_filter(filter_fn(|metadata| {
!metadata.target().contains("rustls")
}));
tracing_subscriber::registry()
.with(stdout)
.with(debug_log)
2023-01-18 16:37:53 +00:00
.init();
2022-06-28 21:59:20 +00:00
tracing::info!("Starting application ({APP_VERSION})");
2022-07-26 08:57:12 +00:00
2022-09-24 14:52:31 +00:00
adw::init().expect("Libadwaita initialization failed");
2022-06-28 21:59:20 +00:00
// Register and include resources
2023-01-18 16:37:53 +00:00
gtk::gio::resources_register_include!("resources.gresource")
.expect("Failed to register resources");
// Set icons search path
gtk::IconTheme::for_display(&gtk::gdk::Display::default().unwrap())
.add_resource_path(&format!("{APP_RESOURCE_PATH}/icons"));
// Set application's title
2023-04-18 10:49:56 +00:00
gtk::glib::set_application_name("The Honkers Railway Launcher");
gtk::glib::set_program_name(Some("The Honkers Railway Launcher"));
// Set UI language
let lang = CONFIG.launcher.language.parse().expect("Wrong language format used in config");
i18n::set_lang(lang).expect("Failed to set launcher language");
tracing::info!("Set UI language to {}", i18n::get_lang());
// Run FirstRun window if .first-run file persist
if FIRST_RUN_FILE.exists() {
// Create the app
2024-03-24 09:23:15 +00:00
let app = RelmApp::new(APP_ID)
.with_args(gtk_args);
2024-03-24 07:56:04 +00:00
// Set global css
app.set_global_css(&GLOBAL_CSS);
// Show first run window
app.run::<FirstRunApp>(());
}
// Run the app if everything's ready
else {
// Temporary workaround for old patches which HAVE to be reverted
// I don't believe to users to read announcements so better do this
//
// There's 2 files which were modified by the old patch, but since the game
// was updated those files were updated as well, so no need for additional actions
//
// Should be removed in future
let game_path = CONFIG.game.path.for_edition(CONFIG.launcher.edition);
if game_path.join("Generated").exists() {
std::fs::remove_dir_all(game_path.join("Generated"))
.expect("Failed to delete 'Generated' folder");
}
if game_path.join("TVMBootstrap.dll").exists() {
std::fs::remove_file(game_path.join("TVMBootstrap.dll"))
.expect("Failed to delete 'TVMBootstrap.dll' file");
}
// AC won't say a thing about this file anyway but for consistency I decided
// to delete it as well
if game_path.join("launch.bat").exists() {
std::fs::remove_file(game_path.join("launch.bat"))
.expect("Failed to delete 'launch.bat' file");
}
// Patch was renaming crash reporter to disable it
if game_path.join("UnityCrashHandler64.exe.bak").exists() {
if game_path.join("UnityCrashHandler64.exe").exists() {
std::fs::remove_file(game_path.join("UnityCrashHandler64.exe.bak"))
.expect("Failed to delete 'UnityCrashHandler64.exe.bak' file");
}
else {
std::fs::rename(game_path.join("UnityCrashHandler64.exe.bak"), game_path.join("UnityCrashHandler64.exe"))
.expect("Failed to rename 'UnityCrashHandler64.exe.bak' file to 'UnityCrashHandler64.exe'");
}
}
// End of temporary workaround ^
if run_game || just_run_game {
let state = LauncherState::get_from_config(|_| {})
.expect("Failed to get launcher state");
2023-06-17 17:22:07 +00:00
match state {
LauncherState::Launch => {
2023-04-22 21:32:08 +00:00
anime_launcher_sdk::star_rail::game::run().expect("Failed to run the game");
2023-06-17 17:22:07 +00:00
return Ok(());
}
2023-06-17 17:22:07 +00:00
LauncherState::PatchNotVerified |
LauncherState::PredownloadAvailable { .. } |
LauncherState::PatchUpdateAvailable => {
if just_run_game {
anime_launcher_sdk::star_rail::game::run().expect("Failed to run the game");
return Ok(());
2023-06-17 17:22:07 +00:00
}
}
_ => ()
}
}
// Create the app
2024-03-24 09:23:15 +00:00
let app = RelmApp::new(APP_ID)
.with_args(gtk_args);
2024-03-24 07:56:04 +00:00
// Set global css
app.set_global_css(&GLOBAL_CSS);
// Show main window
app.run::<App>(());
}
Ok(())
2022-06-28 21:59:20 +00:00
}