core: done dynamic heavy tasks initialization

- now launcher is able to load some heavy stuff in background
  so they will not freeze it during start
- patch status and latest game data is loaded in background from now
- renamed some components and their messages types for consistency
This commit is contained in:
Observer KRypt0n_ 2023-02-22 20:46:47 +02:00
parent ba75180811
commit 6525fbb0d9
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
6 changed files with 179 additions and 102 deletions

View file

@ -37,29 +37,6 @@ lazy_static::lazy_static! {
pub static ref CONFIG: config::Config = config::get().expect("Failed to load config");
pub static ref GAME: Game = Game::new(&CONFIG.game.path);
// TODO: add loading screen for heavy tasks like this
// UPD: tried once. The problem is that I use this variable, as well as ones above,
// in the view! macro, which makes it times harder to make the main window load
// faster than this variable calculates its value to show StatusPage with loader.
// As for now I have no idea how to fix this
pub static ref GAME_DIFF: Option<VersionDiff> = match GAME.try_get_diff() {
Ok(diff) => Some(diff),
Err(err) => {
tracing::error!("Failed to get game diff {err}");
None
}
};
pub static ref PATCH: Option<Patch> = match Patch::try_fetch(&CONFIG.patch.servers, None) {
Ok(patch) => Some(patch),
Err(err) => {
tracing::error!("Failed to fetch patch info {err}");
None
}
};
}
fn main() {

View file

@ -13,7 +13,7 @@ pub struct AboutDialog {
}
#[derive(Debug)]
pub enum AppMsg {
pub enum AboutDialogMsg {
Show,
Hide
}
@ -21,7 +21,7 @@ pub enum AppMsg {
#[relm4::component(pub)]
impl SimpleComponent for AboutDialog {
type Init = ();
type Input = AppMsg;
type Input = AboutDialogMsg;
type Output = ();
view! {
@ -77,7 +77,7 @@ impl SimpleComponent for AboutDialog {
set_visible: model.visible,
connect_close_request[sender] => move |_| {
sender.input(AppMsg::Hide);
sender.input(AboutDialogMsg::Hide);
gtk::Inhibit(false)
}
@ -104,11 +104,11 @@ impl SimpleComponent for AboutDialog {
tracing::debug!("Called about dialog event: {:?}", msg);
match msg {
AppMsg::Show => {
AboutDialogMsg::Show => {
self.visible = true;
}
AppMsg::Hide => {
AboutDialogMsg::Hide => {
self.visible = false;
}
}

View file

@ -12,8 +12,8 @@ use anime_launcher_sdk::config::launcher::LauncherStyle;
use crate::*;
use crate::i18n::tr;
use super::preferences::main::App as PreferencesApp;
use super::about::{AboutDialog, AppMsg as AboutDialogMsg};
use super::preferences::main::*;
use super::about::*;
relm4::new_action_group!(WindowActionGroup, "win");
@ -33,6 +33,14 @@ pub struct App {
#[derive(Debug)]
pub enum AppMsg {
/// Supposed to be called automatically on app's run when the latest game version
/// was retrieved from the API
UpdateGameDiff(Option<VersionDiff>),
/// Supposed to be called automatically on app's run when the latest patch version
/// was retrieved from remote repos
UpdatePatch(Option<Patch>),
PerformAction,
OpenPreferences,
ClosePreferences,
@ -261,11 +269,42 @@ impl SimpleComponent for App {
widgets.main_window.insert_action_group("win", Some(&group.into_action_group()));
unsafe {
crate::READY = true;
}
tracing::info!("Main window initialized");
tracing::info!("Main window initialized. App is ready");
// Initialize some heavy tasks
std::thread::spawn(move || {
tracing::info!("Initializing heavy tasks");
// Update initial game version status
sender.input(AppMsg::UpdateGameDiff(match GAME.try_get_diff() {
Ok(diff) => Some(diff),
Err(err) => {
tracing::error!("Failed to get game diff {err}");
None
}
}));
tracing::info!("Updated game version status");
// Update initial patch status
sender.input(AppMsg::UpdatePatch(match Patch::try_fetch(&CONFIG.patch.servers, None) {
Ok(patch) => Some(patch),
Err(err) => {
tracing::error!("Failed to fetch patch info {err}");
None
}
}));
tracing::info!("Updated patch status");
unsafe {
crate::READY = true;
}
tracing::info!("App is ready");
});
ComponentParts { model, widgets }
}
@ -274,6 +313,16 @@ 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));
},
#[allow(unused_must_use)]
AppMsg::UpdatePatch(patch) => unsafe {
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().sender().send(PreferencesAppMsg::UpdatePatch(patch));
},
AppMsg::PerformAction => {
anime_launcher_sdk::game::run().expect("Failed to run the game");
}

View file

@ -8,10 +8,10 @@ use anime_launcher_sdk::config::prelude::*;
use crate::i18n::tr;
use crate::*;
pub struct Enhancements;
pub struct EnhancementsApp;
#[relm4::component(pub)]
impl SimpleComponent for Enhancements {
impl SimpleComponent for EnhancementsApp {
type Init = ();
type Input = ();
type Output = ();
@ -403,7 +403,7 @@ impl SimpleComponent for Enhancements {
root: &Self::Root,
_sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
tracing::info!("Initializing about dialog");
tracing::info!("Initializing enhancements settings");
let model = Self;
let widgets = view_output!();

View file

@ -15,9 +15,12 @@ use crate::ui::components::*;
use crate::i18n::*;
use crate::*;
pub struct General {
wine_components: AsyncController<ComponentsList<AppMsg>>,
dxvk_components: AsyncController<ComponentsList<AppMsg>>,
pub struct GeneralApp {
wine_components: AsyncController<ComponentsList<GeneralAppMsg>>,
dxvk_components: AsyncController<ComponentsList<GeneralAppMsg>>,
game_diff: Option<VersionDiff>,
patch: Option<Patch>,
style: LauncherStyle,
@ -32,7 +35,15 @@ pub struct General {
}
#[derive(Debug, Clone)]
pub enum AppMsg {
pub enum GeneralAppMsg {
/// Supposed to be called automatically on app's run when the latest game version
/// was retrieved from the API
UpdateGameDiff(Option<VersionDiff>),
/// Supposed to be called automatically on app's run when the latest patch version
/// was retrieved from remote repos
UpdatePatch(Option<Patch>),
Toast {
title: String,
description: Option<String>
@ -49,10 +60,10 @@ pub enum AppMsg {
}
#[relm4::component(pub)]
impl SimpleComponent for General {
impl SimpleComponent for GeneralApp {
type Init = ();
type Input = AppMsg;
type Output = super::main::AppMsg;
type Input = GeneralAppMsg;
type Output = super::main::PreferencesAppMsg;
view! {
adw::PreferencesPage {
@ -84,7 +95,7 @@ impl SimpleComponent for General {
set_from_resource: Some("/org/app/images/modern.svg")
},
connect_clicked => AppMsg::UpdateLauncherStyle(LauncherStyle::Modern)
connect_clicked => GeneralAppMsg::UpdateLauncherStyle(LauncherStyle::Modern)
},
gtk::Label {
@ -110,7 +121,7 @@ impl SimpleComponent for General {
set_from_resource: Some("/org/app/images/classic.svg")
},
connect_clicked => AppMsg::UpdateLauncherStyle(LauncherStyle::Classic)
connect_clicked => GeneralAppMsg::UpdateLauncherStyle(LauncherStyle::Classic)
},
gtk::Label {
@ -233,7 +244,8 @@ impl SimpleComponent for General {
set_title: &tr("game-version"),
add_suffix = &gtk::Label {
set_text: &match GAME_DIFF.as_ref() {
#[watch]
set_text: &match model.game_diff.as_ref() {
Some(diff) => match diff {
VersionDiff::Latest(current) |
VersionDiff::Predownload { current, .. } |
@ -246,19 +258,21 @@ impl SimpleComponent for General {
None => String::from("?")
},
add_css_class: match GAME_DIFF.as_ref() {
#[watch]
set_css_classes: match model.game_diff.as_ref() {
Some(diff) => match diff {
VersionDiff::Latest(_) => "success",
VersionDiff::Predownload { .. } => "accent",
VersionDiff::Diff { .. } => "warning",
VersionDiff::Outdated { .. } => "error",
VersionDiff::NotInstalled { .. } => ""
VersionDiff::Latest(_) => &["success"],
VersionDiff::Predownload { .. } => &["accent"],
VersionDiff::Diff { .. } => &["warning"],
VersionDiff::Outdated { .. } => &["error"],
VersionDiff::NotInstalled { .. } => &[]
}
None => "success"
None => &["success"]
},
set_tooltip_text: Some(&match GAME_DIFF.as_ref() {
#[watch]
set_tooltip_text: Some(&match model.game_diff.as_ref() {
Some(diff) => match diff {
VersionDiff::Latest(_) => String::new(),
VersionDiff::Predownload { current, latest, .. } => tr_args("game-predownload-available", [
@ -284,7 +298,8 @@ impl SimpleComponent for General {
set_title: &tr("patch-version"),
add_suffix = &gtk::Label {
set_text: &match PATCH.as_ref() {
#[watch]
set_text: &match model.patch.as_ref() {
Some(patch) => match patch {
Patch::NotAvailable => tr("patch-not-available"),
Patch::Outdated { current, .. } => tr_args("patch-outdated", [("current", current.to_string().into())]),
@ -296,25 +311,27 @@ impl SimpleComponent for General {
None => String::from("?")
},
add_css_class: match PATCH.as_ref() {
#[watch]
set_css_classes: match model.patch.as_ref() {
Some(patch) => match patch {
Patch::NotAvailable => "error",
Patch::NotAvailable => &["error"],
Patch::Outdated { .. } |
Patch::Preparation { .. } |
Patch::Testing { .. } => "warning",
Patch::Testing { .. } => &["warning"],
Patch::Available { .. } => unsafe {
if let Ok(true) = PATCH.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) {
"success"
if let Ok(true) = model.patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) {
&["success"]
} else {
"warning"
&["warning"]
}
}
}
None => ""
None => &[]
},
set_tooltip_text: Some(&match PATCH.as_ref() {
#[watch]
set_tooltip_text: Some(&match model.patch.as_ref() {
Some(patch) => match patch {
Patch::NotAvailable => tr("patch-not-available-tooltip"),
Patch::Outdated { current, latest, .. } => tr_args("patch-outdated-tooltip", [
@ -324,7 +341,7 @@ impl SimpleComponent for General {
Patch::Preparation { .. } => tr("patch-preparation-tooltip"),
Patch::Testing { .. } => tr("patch-testing-tooltip"),
Patch::Available { .. } => unsafe {
if let Ok(true) = PATCH.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) {
if let Ok(true) = model.patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) {
String::new()
} else {
tr("patch-testing-tooltip")
@ -364,7 +381,7 @@ impl SimpleComponent for General {
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(AppMsg::SelectWine(row.selected() as usize));
sender.input(GeneralAppMsg::SelectWine(row.selected() as usize));
}
} @wine_selected_notify
},
@ -378,7 +395,7 @@ impl SimpleComponent for General {
set_state: true,
connect_state_notify[sender] => move |switch| {
sender.input(AppMsg::WineRecommendedOnly(switch.state()));
sender.input(GeneralAppMsg::WineRecommendedOnly(switch.state()));
}
}
}
@ -414,7 +431,7 @@ impl SimpleComponent for General {
connect_selected_notify[sender] => move |row| {
if is_ready() {
sender.input(AppMsg::SelectDxvk(row.selected() as usize));
sender.input(GeneralAppMsg::SelectDxvk(row.selected() as usize));
}
} @dxvk_selected_notify
},
@ -428,7 +445,7 @@ impl SimpleComponent for General {
set_state: true,
connect_state_notify[sender] => move |switch| {
sender.input(AppMsg::DxvkRecommendedOnly(switch.state()));
sender.input(GeneralAppMsg::DxvkRecommendedOnly(switch.state()));
}
}
}
@ -445,7 +462,7 @@ impl SimpleComponent for General {
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
tracing::info!("Initializing about dialog");
tracing::info!("Initializing general settings");
let model = Self {
wine_components: ComponentsList::builder()
@ -454,8 +471,8 @@ impl SimpleComponent for General {
download_folder: CONFIG.game.wine.builds.clone(),
groups: wine::get_groups().into_iter().map(|group| group.into()).collect()
},
on_downloaded: Some(AppMsg::UpdateDownloadedWine),
on_deleted: Some(AppMsg::UpdateDownloadedWine)
on_downloaded: Some(GeneralAppMsg::UpdateDownloadedWine),
on_deleted: Some(GeneralAppMsg::UpdateDownloadedWine)
})
.forward(sender.input_sender(), std::convert::identity),
@ -465,11 +482,14 @@ impl SimpleComponent for General {
download_folder: CONFIG.game.dxvk.builds.clone(),
groups: dxvk::get_groups().into_iter().map(|group| group.into()).collect()
},
on_downloaded: Some(AppMsg::UpdateDownloadedDxvk),
on_deleted: Some(AppMsg::UpdateDownloadedDxvk)
on_downloaded: Some(GeneralAppMsg::UpdateDownloadedDxvk),
on_deleted: Some(GeneralAppMsg::UpdateDownloadedDxvk)
})
.forward(sender.input_sender(), std::convert::identity),
game_diff: None,
patch: None,
style: CONFIG.launcher.style,
downloaded_wine_versions: vec![],
@ -488,11 +508,19 @@ impl SimpleComponent for General {
}
fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
tracing::debug!("Called enhancements settings event: {:?}", msg);
tracing::debug!("Called general settings event: {:?}", msg);
match msg {
GeneralAppMsg::UpdateGameDiff(diff) => {
self.game_diff = diff;
},
GeneralAppMsg::UpdatePatch(patch) => {
self.patch = patch;
},
#[allow(unused_must_use)]
AppMsg::UpdateLauncherStyle(style) => {
GeneralAppMsg::UpdateLauncherStyle(style) => {
if let Ok(mut config) = config::get() {
config.launcher.style = style;
@ -505,21 +533,21 @@ impl SimpleComponent for General {
}
#[allow(unused_must_use)]
AppMsg::Toast { title, description } => {
GeneralAppMsg::Toast { title, description } => {
sender.output(Self::Output::Toast { title, description });
}
AppMsg::WineRecommendedOnly(state) => {
GeneralAppMsg::WineRecommendedOnly(state) => {
// todo
self.wine_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
AppMsg::DxvkRecommendedOnly(state) => {
GeneralAppMsg::DxvkRecommendedOnly(state) => {
// todo
self.dxvk_components.sender().send(components::list::AppMsg::ShowRecommendedOnly(state)).unwrap();
}
AppMsg::UpdateDownloadedWine => {
GeneralAppMsg::UpdateDownloadedWine => {
self.downloaded_wine_versions = wine::get_downloaded(&CONFIG.game.wine.builds).unwrap_or_default();
self.selected_wine_version = if let Some(selected) = &CONFIG.game.wine.selected {
@ -541,7 +569,7 @@ impl SimpleComponent for General {
};
}
AppMsg::UpdateDownloadedDxvk => {
GeneralAppMsg::UpdateDownloadedDxvk => {
self.downloaded_dxvk_versions = dxvk::get_downloaded(&CONFIG.game.dxvk.builds).unwrap_or_default();
self.selected_dxvk_version = if let Ok(Some(selected)) = CONFIG.try_get_selected_dxvk_info() {
@ -563,7 +591,7 @@ impl SimpleComponent for General {
};
}
AppMsg::SelectWine(index) => {
GeneralAppMsg::SelectWine(index) => {
if let Ok(mut config) = config::get() {
if let Some(version) = self.downloaded_wine_versions.get(index) {
if config.game.wine.selected.as_ref().unwrap_or(&String::new()) != &version.title {
@ -581,26 +609,26 @@ impl SimpleComponent for General {
}
Err(err) => {
sender.input(AppMsg::Toast {
sender.input(GeneralAppMsg::Toast {
title: tr("wine-prefix-update-failed"),
description: Some(err.to_string())
});
}
}
sender.input(AppMsg::ResetWineSelection(index));
sender.input(GeneralAppMsg::ResetWineSelection(index));
});
}
}
}
}
AppMsg::ResetWineSelection(index) => {
GeneralAppMsg::ResetWineSelection(index) => {
self.selecting_wine_version = false;
self.selected_wine_version = index as u32;
}
AppMsg::SelectDxvk(index) => {
GeneralAppMsg::SelectDxvk(index) => {
if let Ok(config) = config::get() {
if let Some(version) = self.downloaded_dxvk_versions.get(index) {
if let Ok(selected) = config.try_get_selected_dxvk_info() {
@ -618,13 +646,13 @@ impl SimpleComponent for General {
std::thread::spawn(move || {
if let Err(err) = Dxvk::install(&wine, dxvk_folder, InstallParams::default()) {
sender.input(AppMsg::Toast {
sender.input(GeneralAppMsg::Toast {
title: tr("dxvk-install-failed"),
description: Some(err.to_string())
});
}
sender.input(AppMsg::ResetDxvkSelection(index));
sender.input(GeneralAppMsg::ResetDxvkSelection(index));
});
}
}
@ -632,7 +660,7 @@ impl SimpleComponent for General {
}
}
AppMsg::ResetDxvkSelection(index) => {
GeneralAppMsg::ResetDxvkSelection(index) => {
self.selecting_dxvk_version = false;
self.selected_dxvk_version = index as u32;
}

View file

@ -3,19 +3,32 @@ use relm4::prelude::*;
use gtk::prelude::*;
use adw::prelude::*;
use anime_launcher_sdk::anime_game_core::prelude::*;
use anime_launcher_sdk::anime_game_core::genshin::prelude::*;
use anime_launcher_sdk::config::launcher::LauncherStyle;
use crate::i18n::tr;
use super::general::*;
use super::enhancements::*;
pub static mut PREFERENCES_WINDOW: Option<adw::PreferencesWindow> = None;
pub struct App {
general: Controller<super::general::General>,
enhancements: Controller<super::enhancements::Enhancements>
pub struct PreferencesApp {
general: Controller<GeneralApp>,
enhancements: Controller<EnhancementsApp>
}
#[derive(Debug, Clone)]
pub enum AppMsg {
pub enum PreferencesAppMsg {
/// Supposed to be called automatically on app's run when the latest game version
/// was retrieved from the API
UpdateGameDiff(Option<VersionDiff>),
/// Supposed to be called automatically on app's run when the latest patch version
/// was retrieved from remote repos
UpdatePatch(Option<Patch>),
Toast {
title: String,
description: Option<String>
@ -24,9 +37,9 @@ pub enum AppMsg {
}
#[relm4::component(pub)]
impl SimpleComponent for App {
impl SimpleComponent for PreferencesApp {
type Init = gtk::Window;
type Input = AppMsg;
type Input = PreferencesAppMsg;
type Output = crate::ui::main::AppMsg;
view! {
@ -41,7 +54,7 @@ impl SimpleComponent for App {
connect_close_request[sender] => move |_| {
if let Err(err) = anime_launcher_sdk::config::flush() {
sender.input(AppMsg::Toast {
sender.input(PreferencesAppMsg::Toast {
title: tr("config-flush-error"),
description: Some(err.to_string())
});
@ -60,11 +73,11 @@ impl SimpleComponent for App {
tracing::info!("Initializing preferences window");
let model = Self {
general: super::general::General::builder()
general: GeneralApp::builder()
.launch(())
.forward(sender.input_sender(), std::convert::identity),
enhancements: super::enhancements::Enhancements::builder()
enhancements: EnhancementsApp::builder()
.launch(())
.detach()
};
@ -78,8 +91,8 @@ impl SimpleComponent for App {
}
#[allow(unused_must_use)] {
model.general.sender().send(super::general::AppMsg::UpdateDownloadedWine);
model.general.sender().send(super::general::AppMsg::UpdateDownloadedDxvk);
model.general.sender().send(GeneralAppMsg::UpdateDownloadedWine);
model.general.sender().send(GeneralAppMsg::UpdateDownloadedDxvk);
}
ComponentParts { model, widgets }
@ -89,7 +102,17 @@ impl SimpleComponent for App {
tracing::debug!("Called preferences window event: {:?}", msg);
match msg {
AppMsg::Toast { title, description } => unsafe {
#[allow(unused_must_use)]
PreferencesAppMsg::UpdateGameDiff(diff) => {
self.general.sender().send(GeneralAppMsg::UpdateGameDiff(diff));
},
#[allow(unused_must_use)]
PreferencesAppMsg::UpdatePatch(patch) => {
self.general.sender().send(GeneralAppMsg::UpdatePatch(patch));
},
PreferencesAppMsg::Toast { title, description } => unsafe {
let toast = adw::Toast::new(&title);
toast.set_timeout(5);
@ -124,7 +147,7 @@ impl SimpleComponent for App {
}
#[allow(unused_must_use)]
AppMsg::UpdateLauncherStyle(style) => {
PreferencesAppMsg::UpdateLauncherStyle(style) => {
sender.output(Self::Output::UpdateLauncherStyle(style));
}
}