From e9d10499f285fb1c741b2957fea92224e9ae552e Mon Sep 17 00:00:00 2001 From: Philip Kristoffersen Date: Mon, 18 Apr 2022 14:00:06 +0200 Subject: [PATCH] Epic safe launch (#85) This feature lets users force some Epic Games to be launched through the Epic Launcher --- src/defaultconfig.toml | 1 + src/egs/epic_platform.rs | 4 +- src/egs/get_manifests.rs | 5 + src/egs/manifest_item.rs | 14 ++ src/egs/settings.rs | 2 + src/sync/synchronization.rs | 2 +- src/ui/uiapp.rs | 302 ++++++++++++++++++++++-------------- 7 files changed, 212 insertions(+), 118 deletions(-) diff --git a/src/defaultconfig.toml b/src/defaultconfig.toml index a2cbd69..ac698d8 100644 --- a/src/defaultconfig.toml +++ b/src/defaultconfig.toml @@ -16,6 +16,7 @@ create_symlinks = true [epic_games] enabled = true create_symlinks = true +safe_launch = [] [uplay] enabled = true diff --git a/src/egs/epic_platform.rs b/src/egs/epic_platform.rs index 0c70ecc..af763f4 100644 --- a/src/egs/epic_platform.rs +++ b/src/egs/epic_platform.rs @@ -10,8 +10,8 @@ pub struct EpicPlatform { } impl EpicPlatform { - pub fn new(settings: EpicGamesLauncherSettings) -> Self { - EpicPlatform { settings } + pub fn new(settings: &EpicGamesLauncherSettings) -> Self { + EpicPlatform { settings: settings.clone() } } } diff --git a/src/egs/get_manifests.rs b/src/egs/get_manifests.rs index 2af4845..f8833db 100644 --- a/src/egs/get_manifests.rs +++ b/src/egs/get_manifests.rs @@ -44,6 +44,11 @@ pub(crate) fn get_egs_manifests( let mut manifests : Vec = manifests.collect(); manifests.sort_by_key(|m| format!("{}-{}-{}",m.install_location,m.launch_executable,m.is_managed)); manifests.dedup_by_key(|m| format!("{}-{}-{}",m.install_location,m.launch_executable,m.is_managed)); + for mut manifest in &mut manifests{ + if settings.safe_launch.contains(&manifest.display_name) || settings.safe_launch.contains(&manifest.get_key()){ + manifest.safe_launch = true; + } + } Ok(manifests) } Err(err) => Err(ReadDirError { diff --git a/src/egs/manifest_item.rs b/src/egs/manifest_item.rs index b8144fd..8a3f7b0 100644 --- a/src/egs/manifest_item.rs +++ b/src/egs/manifest_item.rs @@ -31,6 +31,9 @@ pub(crate) struct ManifestItem { #[serde(alias = "ExpectingDLCInstalled")] pub expected_dlc: Option>, + + #[serde(default)] + pub safe_launch: bool, } fn exe_shortcut(manifest: ManifestItem) -> ShortcutOwned { @@ -96,7 +99,18 @@ impl ManifestItem { self.catalog_namespace, self.catalog_item_id, self.app_name ) } + + pub fn get_key(&self) -> String{ + format!( + "{}-{}-{}", + self.catalog_namespace, self.catalog_item_id, self.app_name + ) + } + fn needs_launcher(&self) -> bool { + if self.safe_launch{ + return true; + } match (&self.is_managed, &self.expected_dlc) { (true, _) => true, (false, Some(map)) => !map.is_empty(), diff --git a/src/egs/settings.rs b/src/egs/settings.rs index 9d04c72..b097d44 100644 --- a/src/egs/settings.rs +++ b/src/egs/settings.rs @@ -7,4 +7,6 @@ pub struct EpicGamesLauncherSettings { #[cfg(target_family = "unix")] pub create_symlinks: bool, + + pub safe_launch : Vec } diff --git a/src/sync/synchronization.rs b/src/sync/synchronization.rs index 57e8c7a..8143274 100644 --- a/src/sync/synchronization.rs +++ b/src/sync/synchronization.rs @@ -162,7 +162,7 @@ fn write_shortcut_collections>( pub fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec)> { let mut platform_results = vec![ - update_platform_shortcuts(&EpicPlatform::new(settings.epic_games.clone())), + update_platform_shortcuts(&EpicPlatform::new(&settings.epic_games)), update_platform_shortcuts(&LegendaryPlatform::new(settings.legendary.clone())), update_platform_shortcuts(&ItchPlatform::new(settings.itch.clone())), update_platform_shortcuts(&OriginPlatform { diff --git a/src/ui/uiapp.rs b/src/ui/uiapp.rs index 668bc82..633523e 100644 --- a/src/ui/uiapp.rs +++ b/src/ui/uiapp.rs @@ -5,7 +5,7 @@ use steam_shortcuts_util::shortcut::ShortcutOwned; use std::{error::Error}; use tokio::{runtime::Runtime, sync::watch::{Receiver, self}}; -use crate::{settings::Settings, sync::{download_images, self, SyncProgress}}; +use crate::{settings::Settings, sync::{download_images, self, SyncProgress}, egs::{EpicPlatform, ManifestItem}}; use super::{ui_images::{get_import_image, get_logo, get_logo_icon}, ui_colors::{TEXT_COLOR, BACKGROUND_COLOR, BG_STROKE_COLOR, ORANGE, PURLPLE, LIGHT_ORANGE, EXTRA_BACKGROUND_COLOR}}; @@ -48,7 +48,8 @@ struct MyEguiApp { rt: Runtime, ui_images: UiImages, games_to_sync: Receiver, - status_reciever: Receiver, + status_reciever: Receiver, + epic_manifests: Option>, } @@ -63,6 +64,7 @@ impl MyEguiApp { games_to_sync:watch::channel(FetchGameStatus::NeedsFetched).1, ui_images: UiImages::default(), status_reciever: watch::channel(SyncProgress::NotStarted).1, + epic_manifests : None, } } pub fn run_sync(&mut self) { @@ -148,34 +150,37 @@ impl epi::App for MyEguiApp { egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "Bottom Panel") .frame(frame) .show(ctx,|ui|{ - { - let status = &*self.status_reciever.borrow(); - match status{ - SyncProgress::NotStarted => {}, + let (status_string,syncing) = match &*self.status_reciever.borrow(){ + SyncProgress::NotStarted => { + ("".to_string(),false) + }, SyncProgress::Starting => { - ui.label("Starting Import"); + ("Starting Import".to_string(),true) }, SyncProgress::FoundGames { games_found } => { - ui.label(format!("Found {} games to import",games_found)); + (format!("Found {} games to import",games_found),true) }, SyncProgress::FindingImages => { - ui.label(format!("Searching for images")); + (format!("Searching for images"),true) }, SyncProgress::DownloadingImages { to_download } => { - ui.label(format!("Downloading {} images ",to_download)); + (format!("Downloading {} images ",to_download),true) }, - SyncProgress::Done =>{ - ui.label(format!("Done importing games")); + SyncProgress::Done => { + (format!("Done importing games"),false) }, - }; - } + }; + if status_string != "" { + ui.label(status_string); + } - let texture = self.get_import_image(ui); - let size = texture.size_vec2(); - let image_button = ImageButton::new(texture, size * 0.5); - if ui.add(image_button).on_hover_text("Import your games into steam").clicked() { + let texture = self.get_import_image(ui); + let size = texture.size_vec2(); + let image_button = ImageButton::new(texture, size * 0.5); + if ui.add(image_button).on_hover_text("Import your games into steam") + .clicked() && !syncing{ self.run_sync(); - } + } }); } @@ -246,8 +251,6 @@ impl MyEguiApp{ fn render_settings(&mut self, ui: &mut egui::Ui){ ui.heading("Settings"); - ui.label("Here you can change your settings"); - let mut scroll_style = ui.style_mut(); scroll_style.visuals.extreme_bg_color = BACKGROUND_COLOR; @@ -262,8 +265,111 @@ impl MyEguiApp{ .show(ui,|ui| { ui.reset_style(); - ui.heading("SteamGridDB"); - ui.checkbox(&mut self.settings.steamgrid_db.enabled, "Download images"); + self.render_steamgriddb_settings(ui); + + self.render_steam_settings(ui); + + self.render_epic_settings(ui); + + + #[cfg(target_family = "unix")] + { + ui.heading("Heroic"); + ui.checkbox(&mut self.settings.heroic.enabled, "Import form Heroic"); + + ui.add_space(SECTION_SPACING); + } + + self.render_legendary_settings(ui); + self.render_itch_settings(ui); + self.render_origin_settings(ui); + self.render_gog_settings(ui); + self.render_uplay_settings(ui); + self.render_lutris_settings(ui); + + }); + } + + fn render_lutris_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Lutris"); + ui.checkbox(&mut self.settings.lutris.enabled, "Import form Lutris"); + if self.settings.lutris.enabled{ + ui.horizontal(|ui| { + let mut empty_string ="".to_string(); + let lutris_location = self.settings.lutris.executable.as_mut().unwrap_or(&mut empty_string); + ui.label("Lutris Location: "); + if ui.text_edit_singleline(lutris_location).changed(){ + self.settings.lutris.executable = Some(lutris_location.to_string()); + } + }); + } + } + + fn render_uplay_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Uplay"); + ui.checkbox(&mut self.settings.uplay.enabled, "Import form Uplay"); + ui.add_space(SECTION_SPACING); + } + + fn render_gog_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("GoG Galaxy"); + ui.checkbox(&mut self.settings.gog.enabled, "Import form GoG Galaxy"); + if self.settings.gog.enabled { + ui.horizontal(|ui| { + let mut empty_string ="".to_string(); + let itch_location = self.settings.gog.location.as_mut().unwrap_or(&mut empty_string); + ui.label("GoG Galaxy Folder: "); + if ui.text_edit_singleline(itch_location).changed(){ + self.settings.gog.location = Some(itch_location.to_string()); + } + }); + } + ui.add_space(SECTION_SPACING); + } + + fn render_origin_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Origin"); + ui.checkbox(&mut self.settings.origin.enabled, "Import from Origin"); + ui.add_space(SECTION_SPACING); + } + + fn render_itch_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Itch.io"); + ui.checkbox(&mut self.settings.itch.enabled, "Import form Itch.io"); + if self.settings.itch.enabled { + ui.horizontal(|ui| { + let mut empty_string ="".to_string(); + let itch_location = self.settings.itch.location.as_mut().unwrap_or(&mut empty_string); + ui.label("Itch.io Folder: "); + if ui.text_edit_singleline(itch_location).changed(){ + self.settings.itch.location = Some(itch_location.to_string()); + } + }); + } + ui.add_space(SECTION_SPACING); + } + + fn render_steam_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Steam"); + ui.horizontal(|ui| { + let mut empty_string ="".to_string(); + let steam_location = self.settings.steam.location.as_mut().unwrap_or(&mut empty_string); + ui.label("Steam Location: "); + if ui.text_edit_singleline(steam_location).changed(){ + self.settings.steam.location = Some(steam_location.to_string()); + } + }); + ui.checkbox(&mut self.settings.steam.create_collections, "Create collections").on_hover_text("Tries to create a games collection for each platform"); + ui.checkbox(&mut self.settings.steam.optimize_for_big_picture, "Optimize for big picture").on_hover_text("Set icons to be larger horizontal images, this looks nice in steam big picture mode, but a bit off in desktop mode"); + ui.checkbox(&mut self.settings.steam.stop_steam, "Stop Steam before import").on_hover_text("Stops Steam if it is running when import starts"); + ui.checkbox(&mut self.settings.steam.start_steam, "Start Steam after import").on_hover_text("Starts Steam is it is not running after the import"); + ui.add_space(SECTION_SPACING); + } + + fn render_steamgriddb_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("SteamGridDB"); + ui.checkbox(&mut self.settings.steamgrid_db.enabled, "Download images"); + if self.settings.steamgrid_db.enabled{ ui.horizontal(|ui| { let mut empty_string ="".to_string(); let auth_key = self.settings.steamgrid_db.auth_key.as_mut().unwrap_or(&mut empty_string); @@ -277,48 +383,14 @@ impl MyEguiApp{ ui.hyperlink_to("here", "https://www.steamgriddb.com/profile/preferences/api") }); ui.checkbox(&mut self.settings.steamgrid_db.prefer_animated, "Prefer animated images").on_hover_text("Prefer downloading animated images over static images (this can slow Steam down but looks neat)"); + } + ui.add_space(SECTION_SPACING); + } - ui.add_space(SECTION_SPACING); - - ui.heading("Steam"); - ui.horizontal(|ui| { - let mut empty_string ="".to_string(); - let steam_location = self.settings.steam.location.as_mut().unwrap_or(&mut empty_string); - ui.label("Steam Location: "); - if ui.text_edit_singleline(steam_location).changed(){ - self.settings.steam.location = Some(steam_location.to_string()); - } - }); - ui.checkbox(&mut self.settings.steam.create_collections, "Create collections").on_hover_text("Tries to create a games collection for each platform"); - ui.checkbox(&mut self.settings.steam.optimize_for_big_picture, "Optimize for big picture").on_hover_text("Set icons to be larger horizontal images, this looks nice in steam big picture mode, but a bit off in desktop mode"); - ui.checkbox(&mut self.settings.steam.stop_steam, "Stop Steam before import").on_hover_text("Stops Steam if it is running when import starts"); - ui.checkbox(&mut self.settings.steam.start_steam, "Start Steam after import").on_hover_text("Starts Steam is it is not running after the import"); - - ui.add_space(SECTION_SPACING); - - ui.heading("Epic Games"); - ui.checkbox(&mut self.settings.epic_games.enabled, "Import form Epic Games"); - ui.horizontal(|ui| { - let mut empty_string ="".to_string(); - let epic_location = self.settings.epic_games.location.as_mut().unwrap_or(&mut empty_string); - ui.label("Epic Manifests Location: ").on_hover_text("The location where Epic stores its manifest files that BoilR needs to read"); - if ui.text_edit_singleline(epic_location).changed(){ - self.settings.epic_games.location = Some(epic_location.to_string()); - } - }); - - ui.add_space(SECTION_SPACING); - - #[cfg(target_family = "unix")] - { - ui.heading("Heroic"); - ui.checkbox(&mut self.settings.heroic.enabled, "Import form Heroic"); - - ui.add_space(SECTION_SPACING); - } - - ui.heading("Legendary & Rare"); - ui.checkbox(&mut self.settings.legendary.enabled, "Import form Legendary & Rare"); + fn render_legendary_settings(&mut self, ui: &mut egui::Ui) { + ui.heading("Legendary & Rare"); + ui.checkbox(&mut self.settings.legendary.enabled, "Import form Legendary & Rare"); + if self.settings.legendary.enabled{ ui.horizontal(|ui| { let mut empty_string ="".to_string(); let legendary_location = self.settings.legendary.executable.as_mut().unwrap_or(&mut empty_string); @@ -327,59 +399,59 @@ impl MyEguiApp{ self.settings.legendary.executable = Some(legendary_location.to_string()); } }); - - ui.add_space(SECTION_SPACING); - - ui.heading("Itch.io"); - ui.checkbox(&mut self.settings.itch.enabled, "Import form Itch.io"); - ui.horizontal(|ui| { - let mut empty_string ="".to_string(); - let itch_location = self.settings.itch.location.as_mut().unwrap_or(&mut empty_string); - ui.label("Itch.io Folder: "); - if ui.text_edit_singleline(itch_location).changed(){ - self.settings.itch.location = Some(itch_location.to_string()); - } - }); - - ui.add_space(SECTION_SPACING); - - ui.heading("Origin"); - ui.checkbox(&mut self.settings.origin.enabled, "Import from Origin"); - - ui.add_space(SECTION_SPACING); - - ui.heading("GoG Galaxy"); - ui.checkbox(&mut self.settings.gog.enabled, "Import form GoG Galaxy"); - ui.horizontal(|ui| { - let mut empty_string ="".to_string(); - let itch_location = self.settings.gog.location.as_mut().unwrap_or(&mut empty_string); - ui.label("GoG Galaxy Folder: "); - if ui.text_edit_singleline(itch_location).changed(){ - self.settings.gog.location = Some(itch_location.to_string()); - } - }); - - ui.add_space(SECTION_SPACING); - - ui.heading("Uplay"); - ui.checkbox(&mut self.settings.uplay.enabled, "Import form Uplay"); - - ui.add_space(SECTION_SPACING); - - ui.heading("Lutris"); - ui.checkbox(&mut self.settings.lutris.enabled, "Import form Lutris"); - ui.horizontal(|ui| { - let mut empty_string ="".to_string(); - let lutris_location = self.settings.lutris.executable.as_mut().unwrap_or(&mut empty_string); - ui.label("Lutris Location: "); - if ui.text_edit_singleline(lutris_location).changed(){ - self.settings.lutris.executable = Some(lutris_location.to_string()); - } - }); - - }); + } + ui.add_space(SECTION_SPACING); } + fn render_epic_settings(&mut self, ui: &mut egui::Ui) { + let epic_settings = &mut self.settings.epic_games; + ui.heading("Epic Games"); + ui.checkbox(&mut epic_settings.enabled, "Import form Epic Games"); + if epic_settings.enabled{ + ui.horizontal(|ui| { + let mut empty_string ="".to_string(); + let epic_location = epic_settings.location.as_mut().unwrap_or(&mut empty_string); + ui.label("Epic Manifests Location: ").on_hover_text("The location where Epic stores its manifest files that BoilR needs to read"); + if ui.text_edit_singleline(epic_location).changed(){ + epic_settings.location = Some(epic_location.to_string()); + } + }); + + let safe_mode_header = match epic_settings.safe_launch.len(){ + 0 => "Force games to launch through Epic Launcher".to_string(), + 1 => "One game forced to launch through Epic Launcher".to_string(), + x => format!("{} games forced to launch through Epic Launcher",x) + }; + + egui::CollapsingHeader::new(safe_mode_header) + .id_source("Epic_Launcher_safe_launch") + .show(ui, |ui| { + ui.label("Some games must be started from the Epic Launcher, select those games below and BoilR will create shortcuts that opens the games through the Epic Launcher."); + let manifests =self.epic_manifests.get_or_insert_with(||{ + let epic_platform = EpicPlatform::new(epic_settings); + let manifests = crate::platform::Platform::get_shortcuts(&epic_platform); + manifests.unwrap_or_default() + }); + + let mut safe_open_games = epic_settings.safe_launch.clone(); + for manifest in manifests{ + let key = manifest.get_key(); + let display_name = &manifest.display_name; + let mut safe_open = safe_open_games.contains(display_name) || safe_open_games.contains(&key); + if ui.checkbox(&mut safe_open, display_name).clicked(){ + if safe_open{ + safe_open_games.push(key); + }else{ + safe_open_games.retain(|m| m!= display_name && m!= &key); + } + } + } + epic_settings.safe_launch = safe_open_games; + }) ; + ui.add_space(SECTION_SPACING); + } +} + fn render_import_games(&mut self, ui: &mut egui::Ui){ ui.heading("Import Games");