Epic safe launch (#85)

This feature lets users force some Epic Games to be launched through the Epic Launcher
This commit is contained in:
Philip Kristoffersen
2022-04-18 14:00:06 +02:00
committed by GitHub
parent 03aab2e088
commit e9d10499f2
7 changed files with 212 additions and 118 deletions
+1
View File
@@ -16,6 +16,7 @@ create_symlinks = true
[epic_games] [epic_games]
enabled = true enabled = true
create_symlinks = true create_symlinks = true
safe_launch = []
[uplay] [uplay]
enabled = true enabled = true
+2 -2
View File
@@ -10,8 +10,8 @@ pub struct EpicPlatform {
} }
impl EpicPlatform { impl EpicPlatform {
pub fn new(settings: EpicGamesLauncherSettings) -> Self { pub fn new(settings: &EpicGamesLauncherSettings) -> Self {
EpicPlatform { settings } EpicPlatform { settings: settings.clone() }
} }
} }
+5
View File
@@ -44,6 +44,11 @@ pub(crate) fn get_egs_manifests(
let mut manifests : Vec<ManifestItem> = manifests.collect(); let mut manifests : Vec<ManifestItem> = manifests.collect();
manifests.sort_by_key(|m| format!("{}-{}-{}",m.install_location,m.launch_executable,m.is_managed)); 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)); 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) Ok(manifests)
} }
Err(err) => Err(ReadDirError { Err(err) => Err(ReadDirError {
+14
View File
@@ -31,6 +31,9 @@ pub(crate) struct ManifestItem {
#[serde(alias = "ExpectingDLCInstalled")] #[serde(alias = "ExpectingDLCInstalled")]
pub expected_dlc: Option<HashMap<String, bool>>, pub expected_dlc: Option<HashMap<String, bool>>,
#[serde(default)]
pub safe_launch: bool,
} }
fn exe_shortcut(manifest: ManifestItem) -> ShortcutOwned { fn exe_shortcut(manifest: ManifestItem) -> ShortcutOwned {
@@ -96,7 +99,18 @@ impl ManifestItem {
self.catalog_namespace, self.catalog_item_id, self.app_name 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 { fn needs_launcher(&self) -> bool {
if self.safe_launch{
return true;
}
match (&self.is_managed, &self.expected_dlc) { match (&self.is_managed, &self.expected_dlc) {
(true, _) => true, (true, _) => true,
(false, Some(map)) => !map.is_empty(), (false, Some(map)) => !map.is_empty(),
+2
View File
@@ -7,4 +7,6 @@ pub struct EpicGamesLauncherSettings {
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub create_symlinks: bool, pub create_symlinks: bool,
pub safe_launch : Vec<String>
} }
+1 -1
View File
@@ -162,7 +162,7 @@ fn write_shortcut_collections<S: AsRef<str>>(
pub fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec<ShortcutOwned>)> { pub fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec<ShortcutOwned>)> {
let mut platform_results = 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(&LegendaryPlatform::new(settings.legendary.clone())),
update_platform_shortcuts(&ItchPlatform::new(settings.itch.clone())), update_platform_shortcuts(&ItchPlatform::new(settings.itch.clone())),
update_platform_shortcuts(&OriginPlatform { update_platform_shortcuts(&OriginPlatform {
+154 -82
View File
@@ -5,7 +5,7 @@ use steam_shortcuts_util::shortcut::ShortcutOwned;
use std::{error::Error}; use std::{error::Error};
use tokio::{runtime::Runtime, sync::watch::{Receiver, self}}; 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}}; 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}};
@@ -49,6 +49,7 @@ struct MyEguiApp {
ui_images: UiImages, ui_images: UiImages,
games_to_sync: Receiver<FetchGameStatus>, games_to_sync: Receiver<FetchGameStatus>,
status_reciever: Receiver<SyncProgress>, status_reciever: Receiver<SyncProgress>,
epic_manifests: Option<Vec<ManifestItem>>,
} }
@@ -63,6 +64,7 @@ impl MyEguiApp {
games_to_sync:watch::channel(FetchGameStatus::NeedsFetched).1, games_to_sync:watch::channel(FetchGameStatus::NeedsFetched).1,
ui_images: UiImages::default(), ui_images: UiImages::default(),
status_reciever: watch::channel(SyncProgress::NotStarted).1, status_reciever: watch::channel(SyncProgress::NotStarted).1,
epic_manifests : None,
} }
} }
pub fn run_sync(&mut self) { pub fn run_sync(&mut self) {
@@ -148,32 +150,35 @@ impl epi::App for MyEguiApp {
egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "Bottom Panel") egui::TopBottomPanel::new(egui::panel::TopBottomSide::Bottom, "Bottom Panel")
.frame(frame) .frame(frame)
.show(ctx,|ui|{ .show(ctx,|ui|{
{ let (status_string,syncing) = match &*self.status_reciever.borrow(){
let status = &*self.status_reciever.borrow(); SyncProgress::NotStarted => {
match status{ ("".to_string(),false)
SyncProgress::NotStarted => {}, },
SyncProgress::Starting => { SyncProgress::Starting => {
ui.label("Starting Import"); ("Starting Import".to_string(),true)
}, },
SyncProgress::FoundGames { games_found } => { SyncProgress::FoundGames { games_found } => {
ui.label(format!("Found {} games to import",games_found)); (format!("Found {} games to import",games_found),true)
}, },
SyncProgress::FindingImages => { SyncProgress::FindingImages => {
ui.label(format!("Searching for images")); (format!("Searching for images"),true)
}, },
SyncProgress::DownloadingImages { to_download } => { SyncProgress::DownloadingImages { to_download } => {
ui.label(format!("Downloading {} images ",to_download)); (format!("Downloading {} images ",to_download),true)
}, },
SyncProgress::Done => { SyncProgress::Done => {
ui.label(format!("Done importing games")); (format!("Done importing games"),false)
}, },
}; };
if status_string != "" {
ui.label(status_string);
} }
let texture = self.get_import_image(ui); let texture = self.get_import_image(ui);
let size = texture.size_vec2(); let size = texture.size_vec2();
let image_button = ImageButton::new(texture, size * 0.5); let image_button = ImageButton::new(texture, size * 0.5);
if ui.add(image_button).on_hover_text("Import your games into steam").clicked() { if ui.add(image_button).on_hover_text("Import your games into steam")
.clicked() && !syncing{
self.run_sync(); self.run_sync();
} }
}); });
@@ -246,8 +251,6 @@ impl MyEguiApp{
fn render_settings(&mut self, ui: &mut egui::Ui){ fn render_settings(&mut self, ui: &mut egui::Ui){
ui.heading("Settings"); ui.heading("Settings");
ui.label("Here you can change your settings");
let mut scroll_style = ui.style_mut(); let mut scroll_style = ui.style_mut();
scroll_style.visuals.extreme_bg_color = BACKGROUND_COLOR; scroll_style.visuals.extreme_bg_color = BACKGROUND_COLOR;
@@ -262,24 +265,91 @@ impl MyEguiApp{
.show(ui,|ui| { .show(ui,|ui| {
ui.reset_style(); ui.reset_style();
ui.heading("SteamGridDB"); self.render_steamgriddb_settings(ui);
ui.checkbox(&mut self.settings.steamgrid_db.enabled, "Download images");
ui.horizontal(|ui| { self.render_steam_settings(ui);
let mut empty_string ="".to_string();
let auth_key = self.settings.steamgrid_db.auth_key.as_mut().unwrap_or(&mut empty_string); self.render_epic_settings(ui);
ui.label("Authentication key: ");
if ui.text_edit_singleline(auth_key).changed(){
self.settings.steamgrid_db.auth_key = Some(auth_key.to_string()); #[cfg(target_family = "unix")]
} {
}); ui.heading("Heroic");
ui.horizontal(|ui| { ui.checkbox(&mut self.settings.heroic.enabled, "Import form Heroic");
ui.label("To download images you need an API Key from SteamGridDB, you can find yours");
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);
}
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.heading("Steam");
ui.horizontal(|ui| { ui.horizontal(|ui| {
let mut empty_string ="".to_string(); let mut empty_string ="".to_string();
@@ -293,32 +363,34 @@ impl MyEguiApp{
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.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.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.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.add_space(SECTION_SPACING);
}
ui.heading("Epic Games"); fn render_steamgriddb_settings(&mut self, ui: &mut egui::Ui) {
ui.checkbox(&mut self.settings.epic_games.enabled, "Import form Epic Games"); ui.heading("SteamGridDB");
ui.checkbox(&mut self.settings.steamgrid_db.enabled, "Download images");
if self.settings.steamgrid_db.enabled{
ui.horizontal(|ui| { ui.horizontal(|ui| {
let mut empty_string ="".to_string(); let mut empty_string ="".to_string();
let epic_location = self.settings.epic_games.location.as_mut().unwrap_or(&mut empty_string); let auth_key = self.settings.steamgrid_db.auth_key.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"); ui.label("Authentication key: ");
if ui.text_edit_singleline(epic_location).changed(){ if ui.text_edit_singleline(auth_key).changed(){
self.settings.epic_games.location = Some(epic_location.to_string()); self.settings.steamgrid_db.auth_key = Some(auth_key.to_string());
} }
}); });
ui.horizontal(|ui| {
ui.add_space(SECTION_SPACING); ui.label("To download images you need an API Key from SteamGridDB, you can find yours");
ui.hyperlink_to("here", "https://www.steamgriddb.com/profile/preferences/api")
#[cfg(target_family = "unix")] });
{ 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.heading("Heroic"); }
ui.checkbox(&mut self.settings.heroic.enabled, "Import form Heroic");
ui.add_space(SECTION_SPACING); ui.add_space(SECTION_SPACING);
} }
fn render_legendary_settings(&mut self, ui: &mut egui::Ui) {
ui.heading("Legendary & Rare"); ui.heading("Legendary & Rare");
ui.checkbox(&mut self.settings.legendary.enabled, "Import form Legendary & Rare"); ui.checkbox(&mut self.settings.legendary.enabled, "Import form Legendary & Rare");
if self.settings.legendary.enabled{
ui.horizontal(|ui| { ui.horizontal(|ui| {
let mut empty_string ="".to_string(); let mut empty_string ="".to_string();
let legendary_location = self.settings.legendary.executable.as_mut().unwrap_or(&mut empty_string); let legendary_location = self.settings.legendary.executable.as_mut().unwrap_or(&mut empty_string);
@@ -327,57 +399,57 @@ impl MyEguiApp{
self.settings.legendary.executable = Some(legendary_location.to_string()); self.settings.legendary.executable = Some(legendary_location.to_string());
} }
}); });
}
ui.add_space(SECTION_SPACING); ui.add_space(SECTION_SPACING);
}
ui.heading("Itch.io"); fn render_epic_settings(&mut self, ui: &mut egui::Ui) {
ui.checkbox(&mut self.settings.itch.enabled, "Import form Itch.io"); 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| { ui.horizontal(|ui| {
let mut empty_string ="".to_string(); let mut empty_string ="".to_string();
let itch_location = self.settings.itch.location.as_mut().unwrap_or(&mut empty_string); let epic_location = epic_settings.location.as_mut().unwrap_or(&mut empty_string);
ui.label("Itch.io Folder: "); 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(itch_location).changed(){ if ui.text_edit_singleline(epic_location).changed(){
self.settings.itch.location = Some(itch_location.to_string()); epic_settings.location = Some(epic_location.to_string());
} }
}); });
ui.add_space(SECTION_SPACING); 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)
};
ui.heading("Origin"); egui::CollapsingHeader::new(safe_mode_header)
ui.checkbox(&mut self.settings.origin.enabled, "Import from Origin"); .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()
});
ui.add_space(SECTION_SPACING); let mut safe_open_games = epic_settings.safe_launch.clone();
for manifest in manifests{
ui.heading("GoG Galaxy"); let key = manifest.get_key();
ui.checkbox(&mut self.settings.gog.enabled, "Import form GoG Galaxy"); let display_name = &manifest.display_name;
ui.horizontal(|ui| { let mut safe_open = safe_open_games.contains(display_name) || safe_open_games.contains(&key);
let mut empty_string ="".to_string(); if ui.checkbox(&mut safe_open, display_name).clicked(){
let itch_location = self.settings.gog.location.as_mut().unwrap_or(&mut empty_string); if safe_open{
ui.label("GoG Galaxy Folder: "); safe_open_games.push(key);
if ui.text_edit_singleline(itch_location).changed(){ }else{
self.settings.gog.location = Some(itch_location.to_string()); safe_open_games.retain(|m| m!= display_name && m!= &key);
} }
});
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());
} }
}
epic_settings.safe_launch = safe_open_games;
}) ; }) ;
ui.add_space(SECTION_SPACING);
}); }
} }
fn render_import_games(&mut self, ui: &mut egui::Ui){ fn render_import_games(&mut self, ui: &mut egui::Ui){