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)); - } } } }