From 9d5b3b27ed1f4f837883d80da03abcf0e288f4b7 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Sun, 17 Jul 2022 16:59:57 +0200 Subject: [PATCH] Made some progress on adding game/voiceovers downloading --- src/lib/launcher/states.rs | 2 +- src/ui/main.rs | 142 +++++++++++++++++++++++++++++-------- src/ui/preferences/mod.rs | 2 - 3 files changed, 114 insertions(+), 32 deletions(-) diff --git a/src/lib/launcher/states.rs b/src/lib/launcher/states.rs index 1da0389..b1b1f42 100644 --- a/src/lib/launcher/states.rs +++ b/src/lib/launcher/states.rs @@ -38,7 +38,7 @@ impl Default for LauncherState { } impl LauncherState { - pub fn get(status_page: Option) -> std::io::Result { + pub fn get(status_page: Option<&adw::StatusPage>) -> std::io::Result { let config = config::get()?; let game = Game::new(&config.game.path); diff --git a/src/ui/main.rs b/src/ui/main.rs index c9034a2..c852ff4 100644 --- a/src/ui/main.rs +++ b/src/ui/main.rs @@ -7,11 +7,14 @@ use gtk4::glib::clone; use std::rc::Rc; use std::cell::Cell; +use anime_game_core::prelude::*; + use crate::ui::*; use super::preferences::PreferencesStack; use super::traits::toast_error::ToastError; +use crate::lib::config; use crate::lib::game; use crate::lib::tasks; use crate::lib::launcher::states::LauncherState; @@ -80,7 +83,7 @@ pub enum Actions { OpenPreferencesPage, PreferencesGoBack, PerformButtonEvent, - LaunchGame, + DownloadDiff(Rc), ShowProgressBar, UpdateProgress { fraction: Rc, title: Rc }, HideProgressBar @@ -136,12 +139,12 @@ impl App { // Load initial launcher state std::thread::spawn(clone!(@strong result => move || { - match LauncherState::get(Some(result.widgets.status_page.clone())) { + match LauncherState::get(Some(&result.widgets.status_page)) { Ok(state) => { result.set_state(state); - result.widgets.status_page.set_visible(false); - result.widgets.launcher_content.set_visible(true); + result.widgets.status_page.hide(); + result.widgets.launcher_content.show(); }, Err(err) => { glib::MainContext::default().invoke(move || { @@ -207,28 +210,111 @@ impl App { match state { LauncherState::Launch => { - this.update(Actions::LaunchGame); + // Display toast message if the game is failed to run + if let Err(err) = game::run(false) { + this.toast_error("Failed to run game", err); + } }, + LauncherState::PatchAvailable(_) => todo!(), - LauncherState::VoiceUpdateAvailable(_) => todo!(), - LauncherState::VoiceOutdated(_) => todo!(), - LauncherState::VoiceNotInstalled(_) => todo!(), - LauncherState::GameUpdateAvailable(_) => todo!(), - LauncherState::GameOutdated(_) => todo!(), - LauncherState::GameNotInstalled(_) => todo!() + + LauncherState::VoiceUpdateAvailable(diff) | + LauncherState::VoiceNotInstalled(diff) | + LauncherState::GameUpdateAvailable(diff) | + LauncherState::GameNotInstalled(diff) => { + this.update(Actions::DownloadDiff(Rc::new(diff))); + }, + + LauncherState::GameOutdated(_) => (), + LauncherState::VoiceOutdated(_) => () } } + + Actions::DownloadDiff(diff) => { + match config::get() { + Ok(config) => { + fn to_gb(bytes: u64) -> f64 { + (bytes as f64 / 1024.0 / 1024.0 / 1024.0 * 100.0).ceil() / 100.0 + } - Actions::LaunchGame => { - // Display toast message if the game is failed to run - if let Err(err) = game::run(false) { - this.toast_error("Failed to run game", err); + let diff = (*diff).clone(); + + std::thread::spawn(clone!(@strong this => move || { + diff.install_to(config.game.path, clone!(@strong this => move |state| { + match state { + InstallerUpdate::DownloadingStarted(_) => { + this.update(Actions::ShowProgressBar); + + this.update(Actions::UpdateProgress { + fraction: Rc::new(0.0), + title: Rc::new(String::from("Downloading...")) + }); + } + + InstallerUpdate::DownloadingProgress(curr, total) => { + // To reduce amount of action requests + if curr % 10000 < 200 { + let progress = curr as f64 / total as f64; + + this.update(Actions::UpdateProgress { + fraction: Rc::new(progress), + title: Rc::new(format!( + "Downloading: {:.2}% ({} of {} GB)", + progress * 100.0, + to_gb(curr), + to_gb(total) + )) + }); + } + } + + InstallerUpdate::UnpackingStarted(_) => { + this.update(Actions::UpdateProgress { + fraction: Rc::new(0.0), + title: Rc::new(String::from("Unpacking...")) + }); + } + + InstallerUpdate::UnpackingProgress(curr, total) => { + let progress = curr as f64 / total as f64; + + this.update(Actions::UpdateProgress { + fraction: Rc::new(progress), + title: Rc::new(format!( + "Unpacking: {:.2}% ({} of {} GB)", + progress * 100.0, + to_gb(curr), + to_gb(total) + )) + }); + } + + InstallerUpdate::DownloadingFinished => (), + + InstallerUpdate::UnpackingFinished => { + this.update(Actions::HideProgressBar); + } + + InstallerUpdate::DownloadingError(err) => this.toast_error("Failed to download game", err), + InstallerUpdate::UnpackingError => this.toast_error("Failed to unpack game", "?") + } + })).unwrap(); + })); + }, + Err(err) => { + glib::MainContext::default().invoke(clone!(@strong this => move || { + this.toast_error("Failed to load config", err); + })); + } } } Actions::ShowProgressBar => { - this.widgets.launch_game_group.set_visible(false); - this.widgets.progress_bar_group.set_visible(true); + this.widgets.progress_bar.set_text(None); + this.widgets.progress_bar.set_fraction(0.0); + + this.widgets.launch_game_group.hide(); + this.widgets.progress_bar_group.show(); } Actions::UpdateProgress { fraction, title } => { @@ -237,8 +323,8 @@ impl App { } Actions::HideProgressBar => { - this.widgets.launch_game_group.set_visible(true); - this.widgets.progress_bar_group.set_visible(false); + this.widgets.launch_game_group.show(); + this.widgets.progress_bar_group.hide(); } } @@ -281,24 +367,22 @@ impl App { self.widgets.launch_game.set_label("Apply patch"); } - LauncherState::GameUpdateAvailable(_) => { - self.widgets.launch_game.set_label("Update"); - } - + LauncherState::GameUpdateAvailable(_) | LauncherState::VoiceUpdateAvailable(_) => { self.widgets.launch_game.set_label("Update"); } - LauncherState::GameNotInstalled(_) => { - self.widgets.launch_game.set_label("Download"); - } - + LauncherState::GameNotInstalled(_) | LauncherState::VoiceNotInstalled(_) => { self.widgets.launch_game.set_label("Download"); } - LauncherState::VoiceOutdated(_) => todo!(), - LauncherState::GameOutdated(_) => todo!() + LauncherState::VoiceOutdated(_) | + LauncherState::GameOutdated(_) => { + self.widgets.launch_game.set_label("Update"); + self.widgets.launch_game.set_tooltip_text(Some("Version is too outdated and can't be updated")); + self.widgets.launch_game.set_sensitive(false); + } } let mut values = self.values.take(); diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs index a84ab39..012860e 100644 --- a/src/ui/preferences/mod.rs +++ b/src/ui/preferences/mod.rs @@ -62,8 +62,6 @@ impl PreferencesStack { /// Update page info before opening it /// /// Being called from the `MainApp` struct - /// - /// TODO: do it asynchronously. The problem is that I somehow need to handle this function's error to display it as a toast pub fn update(&self) -> Result<(), Error> { self.status_page.set_visible(true); self.status_page.set_description(None);