From 21772ff73590408687dd9975c145c9bee4f5abb6 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sat, 10 Jun 2023 22:10:24 +0200 Subject: [PATCH] feat: integrated jadeite patch --- Cargo.lock | 8 +- Cargo.toml | 4 +- src/main.rs | 2 +- src/ui/main/apply_patch.rs | 45 ---------- src/ui/main/create_prefix.rs | 1 - src/ui/main/download_diff.rs | 1 - src/ui/main/download_wine.rs | 2 - src/ui/main/mod.rs | 136 ++++++++++++------------------ src/ui/main/update_patch.rs | 64 ++++++++++++++ src/ui/preferences/general/mod.rs | 88 ++++--------------- src/ui/preferences/main.rs | 4 +- 11 files changed, 144 insertions(+), 211 deletions(-) delete mode 100644 src/ui/main/apply_patch.rs create mode 100644 src/ui/main/update_patch.rs diff --git a/Cargo.lock b/Cargo.lock index 8c2d67c..51414d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,8 +48,8 @@ dependencies = [ [[package]] name = "anime-game-core" -version = "1.11.8" -source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.11.8#eaa07d40c321a6285ca81892d32b6e0f7b056fc6" +version = "1.12.0" +source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.12.0#71dd691d1698c3f8d23744c364b24824c43a6e33" dependencies = [ "anyhow", "bzip2", @@ -73,8 +73,8 @@ dependencies = [ [[package]] name = "anime-launcher-sdk" -version = "1.6.7" -source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.6.7#60d800a23d2359d40aaf3b1d62e0b953e9c08062" +version = "1.7.0" +source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=1.7.0#40708d6b1072e5699ffa46acbe200289adb34a05" dependencies = [ "anime-game-core", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f32325e..b343555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,8 @@ glib-build-tools = "0.17" [dependencies.anime-launcher-sdk] git = "https://github.com/an-anime-team/anime-launcher-sdk" -tag = "1.6.7" -features = ["all", "star-rail"] +tag = "1.7.0" +features = ["all", "star-rail", "star-rail-patch"] # path = "../anime-launcher-sdk" # ! for dev purposes only diff --git a/src/main.rs b/src/main.rs index 16760f4..c25ce60 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,7 +197,7 @@ fn main() { } LauncherState::PredownloadAvailable { .. } | - LauncherState::MainPatchAvailable(MainPatch { status: PatchStatus::NotAvailable, .. }) => { + LauncherState::PatchUpdateAvailable => { if just_run_game { anime_launcher_sdk::star_rail::game::run().expect("Failed to run the game"); diff --git a/src/ui/main/apply_patch.rs b/src/ui/main/apply_patch.rs deleted file mode 100644 index 938dd64..0000000 --- a/src/ui/main/apply_patch.rs +++ /dev/null @@ -1,45 +0,0 @@ -use relm4::prelude::*; - -use crate::*; -use crate::i18n::*; -use super::{App, AppMsg}; - -pub fn apply_patch(sender: ComponentSender, patch: MainPatch) { - match patch.status() { - PatchStatus::NotAvailable | - PatchStatus::Outdated { .. } => unreachable!(), - - PatchStatus::Testing { .. } | - PatchStatus::Available { .. } => { - sender.input(AppMsg::DisableButtons(true)); - - let config = Config::get().unwrap(); - - std::thread::spawn(move || { - let mut apply_patch_if_needed = true; - - let game_path = config.game.path.for_edition(config.launcher.edition); - - if let Err(err) = patch.apply(game_path, config.patch.root) { - tracing::error!("Failed to patch the game"); - - sender.input(AppMsg::Toast { - title: tr("game-patching-error"), - description: Some(err.to_string()) - }); - - // Don't try to apply the patch after state updating - // because we just failed to do it - apply_patch_if_needed = false; - } - - sender.input(AppMsg::DisableButtons(false)); - sender.input(AppMsg::UpdateLauncherState { - perform_on_download_needed: false, - apply_patch_if_needed, - show_status_page: true - }); - }); - } - } -} diff --git a/src/ui/main/create_prefix.rs b/src/ui/main/create_prefix.rs index 9e976fc..0cdecd1 100644 --- a/src/ui/main/create_prefix.rs +++ b/src/ui/main/create_prefix.rs @@ -34,7 +34,6 @@ pub fn create_prefix(sender: ComponentSender) { sender.input(AppMsg::DisableButtons(false)); sender.input(AppMsg::UpdateLauncherState { perform_on_download_needed: false, - apply_patch_if_needed: false, show_status_page: true }); }); diff --git a/src/ui/main/download_diff.rs b/src/ui/main/download_diff.rs index 5195110..08826a5 100644 --- a/src/ui/main/download_diff.rs +++ b/src/ui/main/download_diff.rs @@ -68,7 +68,6 @@ pub fn download_diff(sender: ComponentSender, progress_bar_input: Sender, progress_bar_input: Sender, progress_bar_input: Sender), + SetMainPatch(Option<(Version, JadeitePatchStatusVariant)>), /// Supposed to be called automatically on app's run when the launcher state was chosen SetLauncherState(Option), @@ -359,32 +356,36 @@ impl SimpleComponent for App { #[watch] set_icon_name: match &model.state { Some(LauncherState::Launch) | + Some(LauncherState::PatchNotVerified) | Some(LauncherState::PredownloadAvailable { .. }) => "media-playback-start-symbolic", + Some(LauncherState::PatchNotInstalled) | + Some(LauncherState::PatchUpdateAvailable) => "document-save-symbolic", + Some(LauncherState::WineNotInstalled) | Some(LauncherState::PrefixNotExists) => "document-save-symbolic", Some(LauncherState::GameUpdateAvailable(_)) | Some(LauncherState::GameNotInstalled(_)) => "document-save-symbolic", - Some(LauncherState::MainPatchAvailable(MainPatch { status, .. })) => match status { - PatchStatus::NotAvailable | - PatchStatus::Outdated { .. } => "window-close-symbolic", - - PatchStatus::Testing { .. } | - PatchStatus::Available { .. } => "document-save-symbolic" - } - Some(LauncherState::GameOutdated(_)) | + Some(LauncherState::PatchBroken) | + Some(LauncherState::PatchUnsafe) | None => "window-close-symbolic" }, #[watch] set_label: &match &model.state { Some(LauncherState::Launch) | + Some(LauncherState::PatchNotVerified) | Some(LauncherState::PredownloadAvailable { .. }) => tr("launch"), - Some(LauncherState::MainPatchAvailable(_)) => tr("apply-patch"), + // TODO: add localization + Some(LauncherState::PatchNotInstalled) | + Some(LauncherState::PatchUpdateAvailable) => String::from("Download patch"), + + Some(LauncherState::PatchBroken) => String::from("Patch is broken"), + Some(LauncherState::PatchUnsafe) => String::from("Patch is unsafe"), Some(LauncherState::WineNotInstalled) => tr("download-wine"), Some(LauncherState::PrefixNotExists) => tr("create-prefix"), @@ -416,15 +417,9 @@ impl SimpleComponent for App { #[watch] set_sensitive: !model.disabled_buttons && match &model.state { - Some(LauncherState::GameOutdated { .. }) => false, - - Some(LauncherState::MainPatchAvailable(MainPatch { status, .. })) => match status { - PatchStatus::NotAvailable | - PatchStatus::Outdated { .. } => false, - - PatchStatus::Testing { .. } | - PatchStatus::Available { .. } => true - }, + Some(LauncherState::GameOutdated { .. }) | + Some(LauncherState::PatchBroken) | + Some(LauncherState::PatchUnsafe) => false, Some(_) => true, @@ -433,15 +428,11 @@ impl SimpleComponent for App { #[watch] set_css_classes: match &model.state { - Some(LauncherState::GameOutdated { .. }) => &["warning", "pill"], + Some(LauncherState::GameOutdated { .. }) | + Some(LauncherState::PatchNotVerified) => &["warning", "pill"], - Some(LauncherState::MainPatchAvailable(MainPatch { status, .. })) => match status { - PatchStatus::NotAvailable | - PatchStatus::Outdated { .. } => &["error", "pill"], - - PatchStatus::Testing { .. } => &["warning", "pill"], - PatchStatus::Available { .. } => &["suggested-action", "pill"] - }, + Some(LauncherState::PatchBroken) | + Some(LauncherState::PatchUnsafe) => &["error", "pill"], Some(_) => &["suggested-action", "pill"], @@ -452,12 +443,9 @@ impl SimpleComponent for App { set_tooltip_text: Some(&match &model.state { Some(LauncherState::GameOutdated { .. }) => tr("main-window--version-outdated-tooltip"), - Some(LauncherState::MainPatchAvailable(MainPatch { status, .. })) => match status { - PatchStatus::NotAvailable => tr("main-window--patch-unavailable-tooltip"), - PatchStatus::Outdated { .. } => tr("main-window--patch-outdated-tooltip"), - - _ => String::new() - }, + // TODO: add localization + Some(LauncherState::PatchBroken) => String::from("Current patch version is broken and doesn't work properly"), + Some(LauncherState::PatchUnsafe) => String::from("Current patch version is unsafe and should not be used"), _ => String::new() }), @@ -751,45 +739,31 @@ impl SimpleComponent for App { // Update initial patch status tasks.push(std::thread::spawn(clone!(@strong sender => move || { - // Sync local patch repo - let patch = Patch::new(&CONFIG.patch.path, CONFIG.launcher.edition); - - match patch.is_sync(&CONFIG.patch.servers) { - Ok(Some(_)) => (), - - Ok(None) => { - for server in &CONFIG.patch.servers { - match patch.sync(server) { - Ok(_) => break, - - Err(err) => { - tracing::error!("Failed to sync patch folder with remote: {server}: {err}"); - - sender.input(AppMsg::Toast { - title: tr("patch-sync-failed"), - description: Some(err.to_string()) - }); - } - } - } - } - - Err(err) => { - tracing::error!("Failed to compare local patch folder with remote: {err}"); - - sender.input(AppMsg::Toast { - title: tr("patch-state-check-failed"), - description: Some(err.to_string()) - }); - } - } - // Get main patch status - sender.input(AppMsg::SetMainPatch(match patch.main_patch() { - Ok(patch) => Some(patch), + sender.input(AppMsg::SetMainPatch(match jadeite::get_latest() { + Ok(latest) => match jadeite::get_metadata() { + Ok(metadata) => { + let status = GAME.get_version() + .map(|version| metadata.hsr.global.get_status(version)) + .unwrap_or(metadata.hsr.global.status); + + Some((latest.version, status)) + } + + Err(err) => { + tracing::error!("Failed to fetch patch metadata: {err}"); + + sender.input(AppMsg::Toast { + title: tr("patch-info-fetching-error"), + description: Some(err.to_string()) + }); + + None + } + }, Err(err) => { - tracing::error!("Failed to fetch main patch info: {err}"); + tracing::error!("Failed to fetch latest patch version: {err}"); sender.input(AppMsg::Toast { title: tr("patch-info-fetching-error"), @@ -831,7 +805,6 @@ impl SimpleComponent for App { // Update launcher state sender.input(AppMsg::UpdateLauncherState { perform_on_download_needed: false, - apply_patch_if_needed: false, show_status_page: true }); @@ -851,7 +824,7 @@ impl SimpleComponent for App { match msg { // TODO: make function from this message like with toast - AppMsg::UpdateLauncherState { perform_on_download_needed, apply_patch_if_needed, show_status_page } => { + AppMsg::UpdateLauncherState { perform_on_download_needed, show_status_page } => { if show_status_page { sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("loading-launcher-state"))))); } else { @@ -898,10 +871,6 @@ impl SimpleComponent for App { sender.input(AppMsg::PerformAction); } - LauncherState::MainPatchAvailable(_) if apply_patch_if_needed => { - sender.input(AppMsg::PerformAction); - } - _ => () } } @@ -969,7 +938,6 @@ impl SimpleComponent for App { sender.input(AppMsg::SetDownloading(false)); sender.input(AppMsg::UpdateLauncherState { perform_on_download_needed: false, - apply_patch_if_needed: false, show_status_page: true }); }); @@ -978,11 +946,13 @@ impl SimpleComponent for App { AppMsg::PerformAction => unsafe { match self.state.as_ref().unwrap_unchecked() { - LauncherState::MainPatchAvailable(MainPatch { status: PatchStatus::NotAvailable, .. }) | + LauncherState::PatchNotVerified | LauncherState::PredownloadAvailable { .. } | LauncherState::Launch => launch::launch(sender), - LauncherState::MainPatchAvailable(patch) => apply_patch::apply_patch(sender, patch.to_owned()), + LauncherState::PatchNotInstalled | + LauncherState::PatchUpdateAvailable => update_patch::update_patch(sender, self.progress_bar.sender().to_owned()), + LauncherState::WineNotInstalled => download_wine::download_wine(sender, self.progress_bar.sender().to_owned()), LauncherState::PrefixNotExists => create_prefix::create_prefix(sender), @@ -990,7 +960,7 @@ impl SimpleComponent for App { LauncherState::GameNotInstalled(diff) => download_diff::download_diff(sender, self.progress_bar.sender().to_owned(), diff.to_owned()), - LauncherState::GameOutdated(_) => () + _ => () } } diff --git a/src/ui/main/update_patch.rs b/src/ui/main/update_patch.rs new file mode 100644 index 0000000..8d9d812 --- /dev/null +++ b/src/ui/main/update_patch.rs @@ -0,0 +1,64 @@ +use relm4::{ + prelude::*, + Sender +}; + +use gtk::glib::clone; + +use crate::*; +use crate::i18n::*; +use crate::ui::components::*; + +use super::{App, AppMsg}; + +pub fn update_patch(sender: ComponentSender, progress_bar_input: Sender) { + sender.input(AppMsg::SetDownloading(true)); + + let config = Config::get().unwrap(); + + std::thread::spawn(move || { + let result = jadeite::get_latest() + .and_then(|patch| patch.install(config.patch.path, clone!(@strong sender => move |state| { + match &state { + InstallerUpdate::DownloadingError(err) => { + tracing::error!("Downloading failed: {err}"); + + sender.input(AppMsg::Toast { + title: tr("downloading-failed"), + description: Some(err.to_string()) + }); + } + + InstallerUpdate::UnpackingError(err) => { + tracing::error!("Unpacking failed: {err}"); + + sender.input(AppMsg::Toast { + title: tr("unpacking-failed"), + description: Some(err.clone()) + }); + } + + _ => () + } + + #[allow(unused_must_use)] { + progress_bar_input.send(ProgressBarMsg::UpdateFromState(state.into())); + } + }))); + + if let Err(err) = result { + tracing::error!("Failed to download latest patch"); + + sender.input(AppMsg::Toast { + title: String::from("Failed to download latest patch"), // TODO: add localization + description: Some(err.to_string()) + }); + } + + sender.input(AppMsg::SetDownloading(false)); + sender.input(AppMsg::UpdateLauncherState { + perform_on_download_needed: false, + show_status_page: true + }); + }); +} diff --git a/src/ui/preferences/general/mod.rs b/src/ui/preferences/general/mod.rs index 4ed998d..7513205 100644 --- a/src/ui/preferences/general/mod.rs +++ b/src/ui/preferences/general/mod.rs @@ -4,10 +4,11 @@ use relm4::component::*; use gtk::prelude::*; use adw::prelude::*; -use anime_launcher_sdk::anime_game_core::star_rail::consts::GameEdition; - use anime_launcher_sdk::wincompatlib::prelude::*; +use anime_launcher_sdk::anime_game_core::prelude::*; +use anime_launcher_sdk::anime_game_core::star_rail::consts::GameEdition; + use anime_launcher_sdk::config::ConfigExt; use anime_launcher_sdk::star_rail::config::Config; use anime_launcher_sdk::star_rail::config::schema::launcher::LauncherStyle; @@ -27,7 +28,7 @@ pub struct GeneralApp { components_page: AsyncController, game_diff: Option, - main_patch: Option, + main_patch: Option<(Version, JadeitePatchStatusVariant)>, style: LauncherStyle, @@ -42,7 +43,7 @@ pub enum GeneralAppMsg { /// Supposed to be called automatically on app's run when the latest UnityPlayer patch version /// was retrieved from remote repos - SetMainPatch(Option), + SetMainPatch(Option<(Version, JadeitePatchStatusVariant)>), UpdateDownloadedWine, UpdateDownloadedDxvk, @@ -301,36 +302,17 @@ impl SimpleAsyncComponent for GeneralApp { add_suffix = >k::Label { #[watch] set_text: &match model.main_patch.as_ref() { - Some(patch) => match patch.status() { - PatchStatus::NotAvailable => tr("patch-not-available"), - PatchStatus::Outdated { current, .. } => tr_args("patch-outdated", [("current", current.to_string().into())]), - PatchStatus::Testing { version, .. } | - PatchStatus::Available { version, .. } => version.to_string() - } - + Some((version, _)) => version.to_string(), None => String::from("?") }, #[watch] set_css_classes: match model.main_patch.as_ref() { - Some(patch) => match patch.status() { - PatchStatus::NotAvailable => &["error"], - - PatchStatus::Outdated { .. } | - PatchStatus::Testing { .. } => &["warning"], - - PatchStatus::Available { .. } => unsafe { - let path = match Config::get() { - Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), - Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), - }; - - if let Ok(true) = model.main_patch.as_ref().unwrap_unchecked().is_applied(path) { - &["success"] - } else { - &["warning"] - } - } + Some((_, status)) => match status { + JadeitePatchStatusVariant::Verified => &["success"], + JadeitePatchStatusVariant::Unverified => &["warning"], + JadeitePatchStatusVariant::Broken => &["error"], + JadeitePatchStatusVariant::Unsafe => &["error"] } None => &[] @@ -338,25 +320,14 @@ impl SimpleAsyncComponent for GeneralApp { #[watch] set_tooltip_text: Some(&match model.main_patch.as_ref() { - Some(patch) => match patch.status() { - PatchStatus::NotAvailable => tr("patch-not-available-tooltip"), - PatchStatus::Outdated { current, latest, .. } => tr_args("patch-outdated-tooltip", [ - ("current", current.to_string().into()), - ("latest", latest.to_string().into()) - ]), - PatchStatus::Testing { .. } => tr("patch-testing-tooltip"), - PatchStatus::Available { .. } => unsafe { - let path = match Config::get() { - Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), - Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), - }; + Some((_, status)) => match status { + JadeitePatchStatusVariant::Unverified => tr("patch-testing-tooltip"), - if let Ok(true) = model.main_patch.as_ref().unwrap_unchecked().is_applied(path) { - String::new() - } else { - tr("patch-not-applied-tooltip") - } - } + // TODO: add localizaion + JadeitePatchStatusVariant::Broken => String::from("Current patch version doesn't work"), + JadeitePatchStatusVariant::Unsafe => String::from("Current patch version is unsafe to use"), + + _ => String::new() } None => String::new() @@ -365,29 +336,6 @@ impl SimpleAsyncComponent for GeneralApp { } }, - add = &adw::PreferencesGroup { - adw::ActionRow { - set_title: &tr("ask-superuser-permissions"), - set_subtitle: &tr("ask-superuser-permissions-description"), - - add_suffix = >k::Switch { - set_valign: gtk::Align::Center, - - set_state: CONFIG.patch.root, - - connect_state_notify => |switch| { - if is_ready() { - if let Ok(mut config) = Config::get() { - config.patch.root = switch.state(); - - Config::update(config); - } - } - } - } - } - }, - add = &adw::PreferencesGroup { set_title: &tr("options"), diff --git a/src/ui/preferences/main.rs b/src/ui/preferences/main.rs index 457d466..e58ed22 100644 --- a/src/ui/preferences/main.rs +++ b/src/ui/preferences/main.rs @@ -4,6 +4,7 @@ use relm4::component::*; use gtk::prelude::*; use adw::prelude::*; +use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::anime_game_core::star_rail::prelude::*; use anime_launcher_sdk::config::ConfigExt; @@ -30,7 +31,7 @@ pub enum PreferencesAppMsg { /// Supposed to be called automatically on app's run when the latest main patch version /// was retrieved from remote repos - SetMainPatch(Option), + SetMainPatch(Option<(Version, JadeitePatchStatusVariant)>), SetLauncherStyle(LauncherStyle), @@ -131,7 +132,6 @@ impl SimpleAsyncComponent for PreferencesApp { PreferencesAppMsg::UpdateLauncherState => { sender.output(Self::Output::UpdateLauncherState { perform_on_download_needed: false, - apply_patch_if_needed: false, show_status_page: false }); }