From 0e07cb06988a748af8e019ab7be70f9228b8b23b Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sat, 25 Feb 2023 18:08:25 +0200 Subject: [PATCH] feat(core): reworked main window's messages Some messages (relm4 component's input commands) were renamed Added `UpdateLauncherState` message to, well, update launcher state. By calling this status page will appear with information about current status getting progress Added some new error messages. Before, heavy tasks were sending errors only to tracing logs. Now they also will generate toast messages Added patch applying functionality to the main button. Also made main window disappear when the game is running (like it works now in GTK launcher) --- assets/locales/en/errors.ftl | 8 + assets/locales/ru/errors.ftl | 8 + src/ui/main.rs | 334 +++++++++++++++++++++++----------- src/ui/preferences/general.rs | 10 +- src/ui/preferences/main.rs | 27 +-- 5 files changed, 267 insertions(+), 120 deletions(-) diff --git a/assets/locales/en/errors.ftl b/assets/locales/en/errors.ftl index 2db4aff..230dbd5 100644 --- a/assets/locales/en/errors.ftl +++ b/assets/locales/en/errors.ftl @@ -10,6 +10,10 @@ config-update-error = Failed to save config wine-prefix-update-failed = Failed to update wine prefix dxvk-install-failed = Failed to install DXVK +game-diff-finding-error = Failed to find game diff +patch-info-fetching-error = Failed to fetch patch info +launcher-state-updating-error = Failed to update launcher state + package-not-available = Package is not available: {$package} wine-download-error = Failed to download wine wine-unpack-errror = Failed to unpack wine @@ -17,3 +21,7 @@ wine-install-failed = Failed to install wine dxvk-download-error = Failed to download dxvk dxvk-unpack-error = Failed to unpack dxvk dxvk-apply-error = Failed to apply DXVK + +patch-sync-failed = Failed to sync patch folder +patch-state-check-failed = Failed to check patch folder state +game-patching-error = Failed to patch game diff --git a/assets/locales/ru/errors.ftl b/assets/locales/ru/errors.ftl index 48d4a08..a081e0f 100644 --- a/assets/locales/ru/errors.ftl +++ b/assets/locales/ru/errors.ftl @@ -10,6 +10,10 @@ config-update-error = Ошибка сохранения настроек wine-prefix-update-failed = Ошибка обновления префикса Wine dxvk-install-failed = Ошибка установки DXVK +game-diff-finding-error = Не удалось вычислить обновление игры +patch-info-fetching-error = Не удалось получить информацию о патче +launcher-state-updating-error = Не удалось обновить состояние лаунчера + package-not-available = Пакет недоступен: {$package} wine-download-error = Ошибка скачивания Wine wine-unpack-errror = Ошибка распаковки Wine @@ -17,3 +21,7 @@ wine-install-failed = Ошибка установки Wine dxvk-download-error = Ошибка скачивания DXVK dxvk-unpack-error = Ошибка распаковки DXVK dxvk-apply-error = Не удалось применить DXVK + +patch-sync-failed = Ошибка синхронизации папки патча +patch-state-check-failed = Ошибка проверки статуса папки патча +game-patching-error = Не удалось установить патч игры diff --git a/src/ui/main.rs b/src/ui/main.rs index 5e440a6..5917454 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -37,32 +37,38 @@ pub struct App { loading: Option>, style: LauncherStyle, - state: Option + state: Option, + + disable_buttons: bool } #[derive(Debug)] pub enum AppMsg { + UpdateLauncherState, + /// Supposed to be called automatically on app's run when the latest game version /// was retrieved from the API - UpdateGameDiff(Option), + SetGameDiff(Option), /// Supposed to be called automatically on app's run when the latest patch version /// was retrieved from remote repos - UpdatePatch(Option), + SetPatch(Option), /// Supposed to be called automatically on app's run when the launcher state was chosen - UpdateLauncherState(Option), + SetLauncherState(Option), + + SetLauncherStyle(LauncherStyle), + SetLoadingStatus(Option>), + + OpenPreferences, + ClosePreferences, + DisableButtons(bool), + PerformAction, Toast { title: String, description: Option - }, - - UpdateLoadingStatus(Option>), - OpenPreferences, - ClosePreferences, - UpdateLauncherStyle(LauncherStyle), - PerformAction + } } #[relm4::component(pub)] @@ -282,6 +288,9 @@ impl SimpleComponent for App { _ => String::new() }), + #[watch] + set_sensitive: !model.disable_buttons, + set_hexpand: false, set_width_request: 200, @@ -295,6 +304,9 @@ impl SimpleComponent for App { LauncherStyle::Classic => 40 }, + #[watch] + set_sensitive: !model.disable_buttons, + set_icon_name: "emblem-system-symbolic", connect_clicked => AppMsg::OpenPreferences @@ -319,7 +331,9 @@ impl SimpleComponent for App { loading: Some(None), style: CONFIG.launcher.style, - state: None + state: None, + + disable_buttons: false }; let toast_overlay = &model.toast_overlay; @@ -408,7 +422,7 @@ impl SimpleComponent for App { // Download background picture if needed if download_picture { - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("downloading-background-picture"))))); + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("downloading-background-picture"))))); if let Err(err) = crate::background::download_background() { tracing::error!("Failed to download background picture"); @@ -422,12 +436,17 @@ impl SimpleComponent for App { // Update initial game version status - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("loading-game-version"))))); + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-game-version"))))); - sender.input(AppMsg::UpdateGameDiff(match GAME.try_get_diff() { + sender.input(AppMsg::SetGameDiff(match GAME.try_get_diff() { Ok(diff) => Some(diff), Err(err) => { - tracing::error!("Failed to get game diff: {err}"); + tracing::error!("Failed to find game diff: {err}"); + + sender.input(AppMsg::Toast { + title: tr("game-diff-finding-error"), + description: Some(err.to_string()) + }); None } @@ -437,13 +456,18 @@ impl SimpleComponent for App { // Update initial patch status - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("loading-patch-status"))))); + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-patch-status"))))); - sender.input(AppMsg::UpdatePatch(match Patch::try_fetch(&CONFIG.patch.servers, None) { + sender.input(AppMsg::SetPatch(match Patch::try_fetch(&CONFIG.patch.servers, None) { Ok(patch) => Some(patch), Err(err) => { tracing::error!("Failed to fetch patch info: {err}"); + sender.input(AppMsg::Toast { + title: tr("patch-info-fetching-error"), + description: Some(err.to_string()) + }); + None } })); @@ -451,42 +475,7 @@ impl SimpleComponent for App { tracing::info!("Updated patch status"); // Update launcher state - - let updater = clone!(@strong sender => move |state| { - use anime_launcher_sdk::states::StateUpdating; - - match state { - StateUpdating::Game => { - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("loading-launcher-state--game"))))); - } - - StateUpdating::Voice(locale) => { - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr_args("loading-launcher-state--voice", [ - ("locale", locale.to_name().to_owned().into()) - ]))))); - } - - StateUpdating::Patch => { - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("loading-launcher-state--patch"))))); - } - } - }); - - sender.input(AppMsg::UpdateLoadingStatus(Some(Some(tr("loading-launcher-state"))))); - - sender.input(AppMsg::UpdateLauncherState(match LauncherState::get_from_config(updater) { - Ok(state) => Some(state), - Err(err) => { - tracing::error!("Failed to fetch patch info: {err}"); - - None - } - })); - - tracing::info!("Updated launcher state"); - - // Hide loading page - sender.input(AppMsg::UpdateLoadingStatus(None)); + sender.input(AppMsg::UpdateLauncherState); // Mark app as loaded unsafe { @@ -503,20 +492,203 @@ impl SimpleComponent for App { tracing::debug!("Called main window event: {:?}", msg); match msg { - #[allow(unused_must_use)] - AppMsg::UpdateGameDiff(diff) => unsafe { - PREFERENCES_WINDOW.as_ref().unwrap_unchecked().sender().send(PreferencesAppMsg::UpdateGameDiff(diff)); + AppMsg::UpdateLauncherState => { + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-launcher-state"))))); + + let updater = clone!(@strong sender => move |state| { + use anime_launcher_sdk::states::StateUpdating; + + match state { + StateUpdating::Game => { + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-launcher-state--game"))))); + } + + StateUpdating::Voice(locale) => { + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr_args("loading-launcher-state--voice", [ + ("locale", locale.to_name().to_owned().into()) + ]))))); + } + + StateUpdating::Patch => { + sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-launcher-state--patch"))))); + } + } + }); + + let state = match LauncherState::get_from_config(updater) { + Ok(state) => Some(state), + Err(err) => { + tracing::error!("Failed to update launcher state: {err}"); + + sender.input(AppMsg::Toast { + title: tr("launcher-state-updating-error"), + description: Some(err.to_string()) + }); + + None + } + }; + + sender.input(AppMsg::SetLauncherState(state)); + sender.input(AppMsg::SetLoadingStatus(None)); } #[allow(unused_must_use)] - AppMsg::UpdatePatch(patch) => unsafe { - PREFERENCES_WINDOW.as_ref().unwrap_unchecked().sender().send(PreferencesAppMsg::UpdatePatch(patch)); + AppMsg::SetGameDiff(diff) => unsafe { + PREFERENCES_WINDOW.as_ref().unwrap_unchecked().sender().send(PreferencesAppMsg::SetGameDiff(diff)); } - AppMsg::UpdateLauncherState(state) => { + #[allow(unused_must_use)] + AppMsg::SetPatch(patch) => unsafe { + PREFERENCES_WINDOW.as_ref().unwrap_unchecked().sender().send(PreferencesAppMsg::SetPatch(patch)); + } + + AppMsg::SetLauncherState(state) => { self.state = state; } + AppMsg::SetLoadingStatus(status) => { + self.loading = status; + } + + AppMsg::SetLauncherStyle(style) => { + self.style = style; + } + + AppMsg::OpenPreferences => unsafe { + PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().show(); + } + + AppMsg::ClosePreferences => unsafe { + PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().hide(); + } + + AppMsg::DisableButtons(state) => { + self.disable_buttons = state; + } + + AppMsg::PerformAction => unsafe { + match self.state.as_ref().unwrap_unchecked() { + LauncherState::PatchAvailable(Patch::NotAvailable) | + LauncherState::PredownloadAvailable { .. } | + LauncherState::Launch => { + if let Err(err) = anime_launcher_sdk::game::run() { + tracing::error!("Failed to launch game: {err}"); + + sender.input(AppMsg::Toast { + title: tr("game-launching-failed"), + description: Some(err.to_string()) + }); + } + + else { + MAIN_WINDOW.as_ref().unwrap_unchecked().hide(); + + std::thread::sleep(std::time::Duration::from_secs(2)); + + /*if config.launcher.discord_rpc.enabled { + this.widgets.preferences_stack.enhancements_page.discord_rpc.update(RpcUpdates::Connect); + }*/ + + loop { + std::thread::sleep(std::time::Duration::from_secs(3)); + + match std::process::Command::new("ps").arg("-A").stdout(std::process::Stdio::piped()).output() { + Ok(output) => { + let output = String::from_utf8_lossy(&output.stdout); + + if !output.contains("GenshinImpact.e") && !output.contains("unlocker.exe") { + break; + } + } + + Err(_) => break + } + } + + /*if config.launcher.discord_rpc.enabled { + this.widgets.preferences_stack.enhancements_page.discord_rpc.update(RpcUpdates::Disconnect); + }*/ + + MAIN_WINDOW.as_ref().unwrap_unchecked().show(); + } + } + + LauncherState::PatchAvailable(patch) => { + match patch.to_owned() { + Patch::NotAvailable | + Patch::Outdated { .. } | + Patch::Preparation { .. } => unreachable!(), + + Patch::Testing { version, host, .. } | + Patch::Available { version, host, .. } => { + self.disable_buttons = true; + + let config = config::get().unwrap(); + + std::thread::spawn(move || { + let applier = PatchApplier::new(&config.patch.path); + + let mut synced = false; + + match applier.is_sync_with(&host) { + Ok(true) => synced = true, + + Ok(false) => { + match applier.sync(host) { + Ok(true) => synced = true, + + Ok(false) => { + sender.input(AppMsg::Toast { + title: tr("patch-sync-failed"), + description: None + }); + } + + Err(err) => { + sender.input(AppMsg::Toast { + title: tr("patch-sync-failed"), + description: Some(err.to_string()) + }); + } + } + } + + Err(err) => { + sender.input(AppMsg::Toast { + title: tr("patch-state-check-failed"), + description: Some(err.to_string()) + }); + } + } + + if synced { + if let Err(err) = applier.apply(&config.game.path, version, config.patch.root) { + sender.input(AppMsg::Toast { + title: tr("game-patching-error"), + description: Some(err.to_string()) + }); + } + } + + sender.input(AppMsg::DisableButtons(false)); + sender.input(AppMsg::UpdateLauncherState); + }); + } + } + } + + LauncherState::WineNotInstalled => todo!(), + LauncherState::PrefixNotExists => todo!(), + LauncherState::VoiceUpdateAvailable(_) => todo!(), + LauncherState::VoiceOutdated(_) => todo!(), + LauncherState::VoiceNotInstalled(_) => todo!(), + LauncherState::GameUpdateAvailable(_) => todo!(), + LauncherState::GameOutdated(_) => todo!(), + LauncherState::GameNotInstalled(_) => todo!(), + } + } + AppMsg::Toast { title, description } => unsafe { let toast = adw::Toast::new(&title); @@ -550,48 +722,6 @@ impl SimpleComponent for App { self.toast_overlay.add_toast(&toast); } - - AppMsg::UpdateLoadingStatus(status) => { - self.loading = status; - } - - AppMsg::OpenPreferences => unsafe { - PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().show(); - } - - AppMsg::ClosePreferences => unsafe { - PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().hide(); - } - - AppMsg::UpdateLauncherStyle(style) => { - self.style = style; - } - - AppMsg::PerformAction => unsafe { - match self.state.as_ref().unwrap_unchecked() { - LauncherState::Launch => { - if let Err(err) = anime_launcher_sdk::game::run() { - sender.input(AppMsg::Toast { - title: tr("game-launching-failed"), - description: Some(err.to_string()) - }); - - tracing::error!("Failed to launch game: {err}"); - } - } - - LauncherState::PredownloadAvailable { .. } => todo!(), - LauncherState::PatchAvailable(_) => todo!(), - LauncherState::WineNotInstalled => todo!(), - LauncherState::PrefixNotExists => todo!(), - LauncherState::VoiceUpdateAvailable(_) => todo!(), - LauncherState::VoiceOutdated(_) => todo!(), - LauncherState::VoiceNotInstalled(_) => todo!(), - LauncherState::GameUpdateAvailable(_) => todo!(), - LauncherState::GameOutdated(_) => todo!(), - LauncherState::GameNotInstalled(_) => todo!(), - } - } } } } diff --git a/src/ui/preferences/general.rs b/src/ui/preferences/general.rs index 5603262..887856c 100644 --- a/src/ui/preferences/general.rs +++ b/src/ui/preferences/general.rs @@ -38,11 +38,11 @@ pub struct GeneralApp { pub enum GeneralAppMsg { /// Supposed to be called automatically on app's run when the latest game version /// was retrieved from the API - UpdateGameDiff(Option), + SetGameDiff(Option), /// Supposed to be called automatically on app's run when the latest patch version /// was retrieved from remote repos - UpdatePatch(Option), + SetPatch(Option), Toast { title: String, @@ -522,11 +522,11 @@ impl SimpleAsyncComponent for GeneralApp { tracing::debug!("Called general settings event: {:?}", msg); match msg { - GeneralAppMsg::UpdateGameDiff(diff) => { + GeneralAppMsg::SetGameDiff(diff) => { self.game_diff = diff; } - GeneralAppMsg::UpdatePatch(patch) => { + GeneralAppMsg::SetPatch(patch) => { self.patch = patch; } @@ -553,7 +553,7 @@ impl SimpleAsyncComponent for GeneralApp { self.style = style; - sender.output(Self::Output::UpdateLauncherStyle(style)); + sender.output(Self::Output::SetLauncherStyle(style)); } #[allow(unused_must_use)] diff --git a/src/ui/preferences/main.rs b/src/ui/preferences/main.rs index c9c710b..e39028a 100644 --- a/src/ui/preferences/main.rs +++ b/src/ui/preferences/main.rs @@ -24,17 +24,18 @@ pub struct PreferencesApp { pub enum PreferencesAppMsg { /// Supposed to be called automatically on app's run when the latest game version /// was retrieved from the API - UpdateGameDiff(Option), + SetGameDiff(Option), /// Supposed to be called automatically on app's run when the latest patch version /// was retrieved from remote repos - UpdatePatch(Option), + SetPatch(Option), + + SetLauncherStyle(LauncherStyle), Toast { title: String, description: Option - }, - UpdateLauncherStyle(LauncherStyle) + } } #[relm4::component(async, pub)] @@ -104,13 +105,18 @@ impl SimpleAsyncComponent for PreferencesApp { match msg { #[allow(unused_must_use)] - PreferencesAppMsg::UpdateGameDiff(diff) => { - self.general.sender().send(GeneralAppMsg::UpdateGameDiff(diff)); + PreferencesAppMsg::SetGameDiff(diff) => { + self.general.sender().send(GeneralAppMsg::SetGameDiff(diff)); } #[allow(unused_must_use)] - PreferencesAppMsg::UpdatePatch(patch) => { - self.general.sender().send(GeneralAppMsg::UpdatePatch(patch)); + PreferencesAppMsg::SetPatch(patch) => { + self.general.sender().send(GeneralAppMsg::SetPatch(patch)); + } + + #[allow(unused_must_use)] + PreferencesAppMsg::SetLauncherStyle(style) => { + sender.output(Self::Output::SetLauncherStyle(style)); } PreferencesAppMsg::Toast { title, description } => unsafe { @@ -146,11 +152,6 @@ impl SimpleAsyncComponent for PreferencesApp { PREFERENCES_WINDOW.as_ref().unwrap_unchecked().add_toast(&toast); } - - #[allow(unused_must_use)] - PreferencesAppMsg::UpdateLauncherStyle(style) => { - sender.output(Self::Output::UpdateLauncherStyle(style)); - } } } }