Made some progress on adding game/voiceovers downloading

This commit is contained in:
Observer KRypt0n_ 2022-07-17 16:59:57 +02:00
parent a4df021a2d
commit 9d5b3b27ed
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
3 changed files with 114 additions and 32 deletions

View file

@ -38,7 +38,7 @@ impl Default for LauncherState {
} }
impl LauncherState { impl LauncherState {
pub fn get(status_page: Option<adw::StatusPage>) -> std::io::Result<Self> { pub fn get(status_page: Option<&adw::StatusPage>) -> std::io::Result<Self> {
let config = config::get()?; let config = config::get()?;
let game = Game::new(&config.game.path); let game = Game::new(&config.game.path);

View file

@ -7,11 +7,14 @@ use gtk4::glib::clone;
use std::rc::Rc; use std::rc::Rc;
use std::cell::Cell; use std::cell::Cell;
use anime_game_core::prelude::*;
use crate::ui::*; use crate::ui::*;
use super::preferences::PreferencesStack; use super::preferences::PreferencesStack;
use super::traits::toast_error::ToastError; use super::traits::toast_error::ToastError;
use crate::lib::config;
use crate::lib::game; use crate::lib::game;
use crate::lib::tasks; use crate::lib::tasks;
use crate::lib::launcher::states::LauncherState; use crate::lib::launcher::states::LauncherState;
@ -80,7 +83,7 @@ pub enum Actions {
OpenPreferencesPage, OpenPreferencesPage,
PreferencesGoBack, PreferencesGoBack,
PerformButtonEvent, PerformButtonEvent,
LaunchGame, DownloadDiff(Rc<VersionDiff>),
ShowProgressBar, ShowProgressBar,
UpdateProgress { fraction: Rc<f64>, title: Rc<String> }, UpdateProgress { fraction: Rc<f64>, title: Rc<String> },
HideProgressBar HideProgressBar
@ -136,12 +139,12 @@ impl App {
// Load initial launcher state // Load initial launcher state
std::thread::spawn(clone!(@strong result => move || { 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) => { Ok(state) => {
result.set_state(state); result.set_state(state);
result.widgets.status_page.set_visible(false); result.widgets.status_page.hide();
result.widgets.launcher_content.set_visible(true); result.widgets.launcher_content.show();
}, },
Err(err) => { Err(err) => {
glib::MainContext::default().invoke(move || { glib::MainContext::default().invoke(move || {
@ -207,28 +210,111 @@ impl App {
match state { match state {
LauncherState::Launch => { LauncherState::Launch => {
this.update(Actions::LaunchGame);
},
LauncherState::PatchAvailable(_) => todo!(),
LauncherState::VoiceUpdateAvailable(_) => todo!(),
LauncherState::VoiceOutdated(_) => todo!(),
LauncherState::VoiceNotInstalled(_) => todo!(),
LauncherState::GameUpdateAvailable(_) => todo!(),
LauncherState::GameOutdated(_) => todo!(),
LauncherState::GameNotInstalled(_) => todo!()
}
}
Actions::LaunchGame => {
// Display toast message if the game is failed to run // Display toast message if the game is failed to run
if let Err(err) = game::run(false) { if let Err(err) = game::run(false) {
this.toast_error("Failed to run game", err); this.toast_error("Failed to run game", err);
} }
},
LauncherState::PatchAvailable(_) => 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
}
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 => { Actions::ShowProgressBar => {
this.widgets.launch_game_group.set_visible(false); this.widgets.progress_bar.set_text(None);
this.widgets.progress_bar_group.set_visible(true); this.widgets.progress_bar.set_fraction(0.0);
this.widgets.launch_game_group.hide();
this.widgets.progress_bar_group.show();
} }
Actions::UpdateProgress { fraction, title } => { Actions::UpdateProgress { fraction, title } => {
@ -237,8 +323,8 @@ impl App {
} }
Actions::HideProgressBar => { Actions::HideProgressBar => {
this.widgets.launch_game_group.set_visible(true); this.widgets.launch_game_group.show();
this.widgets.progress_bar_group.set_visible(false); this.widgets.progress_bar_group.hide();
} }
} }
@ -281,24 +367,22 @@ impl App {
self.widgets.launch_game.set_label("Apply patch"); self.widgets.launch_game.set_label("Apply patch");
} }
LauncherState::GameUpdateAvailable(_) => { LauncherState::GameUpdateAvailable(_) |
self.widgets.launch_game.set_label("Update");
}
LauncherState::VoiceUpdateAvailable(_) => { LauncherState::VoiceUpdateAvailable(_) => {
self.widgets.launch_game.set_label("Update"); self.widgets.launch_game.set_label("Update");
} }
LauncherState::GameNotInstalled(_) => { LauncherState::GameNotInstalled(_) |
self.widgets.launch_game.set_label("Download");
}
LauncherState::VoiceNotInstalled(_) => { LauncherState::VoiceNotInstalled(_) => {
self.widgets.launch_game.set_label("Download"); self.widgets.launch_game.set_label("Download");
} }
LauncherState::VoiceOutdated(_) => todo!(), LauncherState::VoiceOutdated(_) |
LauncherState::GameOutdated(_) => todo!() 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(); let mut values = self.values.take();

View file

@ -62,8 +62,6 @@ impl PreferencesStack {
/// Update page info before opening it /// Update page info before opening it
/// ///
/// Being called from the `MainApp` struct /// 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> { pub fn update(&self) -> Result<(), Error> {
self.status_page.set_visible(true); self.status_page.set_visible(true);
self.status_page.set_description(None); self.status_page.set_description(None);