mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Launch games through heroic launcher (#151)
* Add option to launch heroic games through launcher * Add ui * Let users choose which game to launch through Heroic
This commit is contained in:
@@ -38,6 +38,7 @@ flatpak = false
|
||||
|
||||
[heroic]
|
||||
enabled = true
|
||||
launch_games_through_heroic =[]
|
||||
|
||||
[amazon]
|
||||
enabled = true
|
||||
|
||||
+58
-31
@@ -1,9 +1,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::heroic_platform::InstallationMode;
|
||||
use serde::Deserialize;
|
||||
use steam_shortcuts_util::{shortcut::ShortcutOwned, Shortcut};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct HeroicGame {
|
||||
pub app_name: String,
|
||||
pub title: String,
|
||||
@@ -11,6 +12,10 @@ pub struct HeroicGame {
|
||||
pub install_path: String,
|
||||
pub executable: String,
|
||||
pub launch_parameters: String,
|
||||
#[serde(skip_deserializing)]
|
||||
pub install_mode: Option<InstallationMode>,
|
||||
#[serde(skip_deserializing)]
|
||||
pub launch_through_heroic: bool,
|
||||
}
|
||||
|
||||
impl HeroicGame {
|
||||
@@ -23,41 +28,63 @@ impl HeroicGame {
|
||||
|
||||
impl From<HeroicGame> for ShortcutOwned {
|
||||
fn from(game: HeroicGame) -> Self {
|
||||
let target_path = Path::new(&game.install_path).join(game.executable);
|
||||
let mut owned_shortcut = if game.launch_through_heroic && game.install_mode.is_some() {
|
||||
let launch_parameter = format!("heroic://launch/{}", game.app_name);
|
||||
let (exe, parameter) = match game.install_mode.unwrap() {
|
||||
InstallationMode::FlatPak => (
|
||||
"flatpak",
|
||||
format!("run com.heroicgameslauncher.hgl {}", launch_parameter),
|
||||
),
|
||||
InstallationMode::UserBin => ("heroic", launch_parameter),
|
||||
};
|
||||
Shortcut::new(
|
||||
"0",
|
||||
game.title.as_str(),
|
||||
exe,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
parameter.as_str(),
|
||||
)
|
||||
.to_owned()
|
||||
} else {
|
||||
let target_path = Path::new(&game.install_path).join(game.executable);
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
let mut target = target_path.to_string_lossy().to_string();
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
if !target.starts_with('\"') && !target.ends_with('\"') {
|
||||
target = format!("\"{}\"", target);
|
||||
#[cfg(target_family = "unix")]
|
||||
let mut target = target_path.to_string_lossy().to_string();
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
if !target.starts_with('\"') && !target.ends_with('\"') {
|
||||
target = format!("\"{}\"", target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
let mut install_path = game.install_path.to_string();
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
if !install_path.starts_with('\"') && !install_path.ends_with('\"') {
|
||||
install_path = format!("\"{}\"", install_path);
|
||||
#[cfg(target_family = "unix")]
|
||||
let mut install_path = game.install_path.to_string();
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
if !install_path.starts_with('\"') && !install_path.ends_with('\"') {
|
||||
install_path = format!("\"{}\"", install_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
let install_path = game.install_path.to_string();
|
||||
#[cfg(target_os = "windows")]
|
||||
let install_path = game.install_path.to_string();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let target = target_path.to_string_lossy().to_string();
|
||||
#[cfg(target_os = "windows")]
|
||||
let target = target_path.to_string_lossy().to_string();
|
||||
|
||||
let shortcut = Shortcut::new(
|
||||
"0",
|
||||
game.title.as_str(),
|
||||
&target,
|
||||
&install_path,
|
||||
&target,
|
||||
"",
|
||||
game.launch_parameters.as_str(),
|
||||
);
|
||||
let mut owned_shortcut = shortcut.to_owned();
|
||||
let shortcut = Shortcut::new(
|
||||
"0",
|
||||
game.title.as_str(),
|
||||
&target,
|
||||
&install_path,
|
||||
&target,
|
||||
"",
|
||||
game.launch_parameters.as_str(),
|
||||
);
|
||||
|
||||
shortcut.to_owned()
|
||||
};
|
||||
owned_shortcut.tags.push("Heroic".to_owned());
|
||||
owned_shortcut.tags.push("Ready TO Play".to_owned());
|
||||
owned_shortcut.tags.push("Installed".to_owned());
|
||||
|
||||
@@ -13,7 +13,8 @@ pub struct HeroicPlatform {
|
||||
pub settings: HeroicSettings,
|
||||
}
|
||||
|
||||
enum InstallationMode {
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub enum InstallationMode {
|
||||
FlatPak,
|
||||
UserBin,
|
||||
}
|
||||
@@ -84,7 +85,7 @@ impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
||||
fn get_shortcuts(&self) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||
let install_modes = vec![InstallationMode::FlatPak, InstallationMode::UserBin];
|
||||
|
||||
let mut heroic_games = get_epic_games(&install_modes)?;
|
||||
let mut heroic_games = self.get_epic_games(&install_modes)?;
|
||||
if let Ok(gog_games) = get_gog_games(&install_modes) {
|
||||
heroic_games.extend(gog_games);
|
||||
} else {
|
||||
@@ -110,31 +111,58 @@ impl Platform<HeroicGameType, Box<dyn Error>> for HeroicPlatform {
|
||||
|
||||
fn needs_proton(&self, input: &HeroicGameType) -> bool {
|
||||
match input {
|
||||
HeroicGameType::Epic(_) => true,
|
||||
HeroicGameType::Epic(game) => !game.launch_through_heroic,
|
||||
HeroicGameType::Gog(_, is_windows) => *is_windows,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_epic_games(
|
||||
install_modes: &[InstallationMode],
|
||||
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||
let mut shortcuts: Vec<HeroicGame> = install_modes
|
||||
.iter()
|
||||
.filter_map(|install_mode| get_shortcuts_from_install_mode(install_mode).ok())
|
||||
.flatten()
|
||||
.filter(|s| s.is_installed())
|
||||
.collect();
|
||||
|
||||
shortcuts.sort_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name));
|
||||
shortcuts.dedup_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name));
|
||||
let mut epic_shortcuts = vec![];
|
||||
for shortcut in shortcuts {
|
||||
epic_shortcuts.push(HeroicGameType::Epic(shortcut));
|
||||
impl HeroicPlatform {
|
||||
pub fn get_epic_games(
|
||||
&self,
|
||||
install_modes: &[InstallationMode],
|
||||
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||
let shortcuts = self.get_heroic_games(install_modes);
|
||||
let mut epic_shortcuts = vec![];
|
||||
for shortcut in shortcuts {
|
||||
epic_shortcuts.push(HeroicGameType::Epic(shortcut));
|
||||
}
|
||||
Ok(epic_shortcuts)
|
||||
}
|
||||
Ok(epic_shortcuts)
|
||||
}
|
||||
|
||||
pub fn get_heroic_games(&self, install_modes: &[InstallationMode]) -> Vec<HeroicGame> {
|
||||
let mut shortcuts: Vec<HeroicGame> = install_modes
|
||||
.iter()
|
||||
.filter_map(|install_mode| {
|
||||
let mut shortcuts = get_shortcuts_from_install_mode(install_mode).ok();
|
||||
if let Some(shortcuts) = shortcuts.as_mut() {
|
||||
for shortcut in shortcuts {
|
||||
shortcut.install_mode = Some(install_mode.clone());
|
||||
if self
|
||||
.settings
|
||||
.launch_games_through_heroic
|
||||
.contains(&shortcut.app_name)
|
||||
|| self
|
||||
.settings
|
||||
.launch_games_through_heroic
|
||||
.contains(&shortcut.title)
|
||||
{
|
||||
shortcut.launch_through_heroic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
shortcuts
|
||||
})
|
||||
.flatten()
|
||||
.filter(|s| s.is_installed())
|
||||
.collect();
|
||||
shortcuts
|
||||
.sort_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name));
|
||||
shortcuts
|
||||
.dedup_by_key(|m| format!("{}-{}-{}", m.launch_parameters, m.executable, &m.app_name));
|
||||
shortcuts
|
||||
}
|
||||
}
|
||||
fn get_gog_games(
|
||||
install_modes: &[InstallationMode],
|
||||
) -> Result<Vec<HeroicGameType>, Box<dyn Error>> {
|
||||
|
||||
@@ -3,4 +3,5 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct HeroicSettings {
|
||||
pub enabled: bool,
|
||||
pub launch_games_through_heroic: Vec<String>,
|
||||
}
|
||||
|
||||
+48
-9
@@ -2,7 +2,7 @@ use copypasta::ClipboardProvider;
|
||||
use eframe::egui;
|
||||
use egui::ScrollArea;
|
||||
|
||||
use crate::egs::EpicPlatform;
|
||||
use crate::{egs::EpicPlatform, heroic::HeroicPlatform};
|
||||
|
||||
use super::{
|
||||
ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR},
|
||||
@@ -39,6 +39,43 @@ impl MyEguiApp {
|
||||
ui.heading("Heroic");
|
||||
ui.checkbox(&mut self.settings.heroic.enabled, "Import from Heroic");
|
||||
|
||||
|
||||
let safe_mode_header = match self.settings.heroic.launch_games_through_heroic.len() {
|
||||
0 => "Force games to launch through Heroic Launcher".to_string(),
|
||||
1 => "One game forced to launch through Heroic Launcher".to_string(),
|
||||
x => format!("{} games forced to launch through Heroic Launcher", x),
|
||||
};
|
||||
|
||||
egui::CollapsingHeader::new(safe_mode_header)
|
||||
.id_source("Heroic_Launcher_safe_launch")
|
||||
.show(ui, |ui| {
|
||||
ui.label("Some games must be started from the Heroic Launcher, select those games below and BoilR will create shortcuts that opens the games through the Heroic Launcher.");
|
||||
let manifests =self.heroic_games.get_or_insert_with(||{
|
||||
let heroic_setting = self.settings.heroic.clone();
|
||||
|
||||
let install_modes = vec![crate::heroic::InstallationMode::FlatPak, crate::heroic::InstallationMode::UserBin];
|
||||
let heroic_platform =HeroicPlatform{
|
||||
settings:heroic_setting
|
||||
};
|
||||
let heroic_games = heroic_platform.get_heroic_games(&install_modes);
|
||||
heroic_games
|
||||
});
|
||||
|
||||
let safe_open_games = &mut self.settings.heroic.launch_games_through_heroic;
|
||||
for manifest in manifests{
|
||||
let key = &manifest.app_name;
|
||||
let display_name = &manifest.title;
|
||||
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.clone());
|
||||
}else{
|
||||
safe_open_games.retain(|m| m!= display_name && m!= key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}) ;
|
||||
|
||||
ui.add_space(SECTION_SPACING);
|
||||
}
|
||||
|
||||
@@ -187,9 +224,9 @@ impl MyEguiApp {
|
||||
.unwrap_or_default();
|
||||
ui.label("Authentication key: ");
|
||||
if ui.text_edit_singleline(&mut auth_key).changed() {
|
||||
if auth_key.is_empty(){
|
||||
if auth_key.is_empty() {
|
||||
self.settings.steamgrid_db.auth_key = None;
|
||||
}else{
|
||||
} else {
|
||||
self.settings.steamgrid_db.auth_key = Some(auth_key.to_string());
|
||||
}
|
||||
}
|
||||
@@ -263,14 +300,16 @@ impl MyEguiApp {
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
let mut empty_string = "".to_string();
|
||||
let epic_location = epic_settings.launcher_exe.as_mut().unwrap_or(&mut empty_string);
|
||||
ui.label("Epic Launcher Location: ").on_hover_text(
|
||||
"The location of the Epic launcher exe",
|
||||
);
|
||||
let epic_location = epic_settings
|
||||
.launcher_exe
|
||||
.as_mut()
|
||||
.unwrap_or(&mut empty_string);
|
||||
ui.label("Epic Launcher Location: ")
|
||||
.on_hover_text("The location of the Epic launcher exe");
|
||||
if ui.text_edit_singleline(epic_location).changed() {
|
||||
if epic_location.is_empty(){
|
||||
if epic_location.is_empty() {
|
||||
epic_settings.launcher_exe = None;
|
||||
}else{
|
||||
} else {
|
||||
epic_settings.launcher_exe = Some(epic_location.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -8,7 +8,7 @@ use tokio::{
|
||||
sync::watch::{self, Receiver},
|
||||
};
|
||||
|
||||
use crate::{egs::ManifestItem, settings::Settings, sync::SyncProgress};
|
||||
use crate::{egs::ManifestItem, heroic::HeroicGame, settings::Settings, sync::SyncProgress};
|
||||
|
||||
use super::{
|
||||
ui_colors::{
|
||||
@@ -36,6 +36,7 @@ pub struct MyEguiApp {
|
||||
pub(crate) games_to_sync: Receiver<FetcStatus<Vec<(String, Vec<ShortcutOwned>)>>>,
|
||||
pub(crate) status_reciever: Receiver<SyncProgress>,
|
||||
pub(crate) epic_manifests: Option<Vec<ManifestItem>>,
|
||||
pub(crate) heroic_games: Option<Vec<HeroicGame>>,
|
||||
pub(crate) image_selected_state: ImageSelectState,
|
||||
pub(crate) backup_state: BackupState,
|
||||
}
|
||||
@@ -51,6 +52,7 @@ impl MyEguiApp {
|
||||
ui_images: UiImages::default(),
|
||||
status_reciever: watch::channel(SyncProgress::NotStarted).1,
|
||||
epic_manifests: None,
|
||||
heroic_games: None,
|
||||
image_selected_state: ImageSelectState::default(),
|
||||
backup_state: BackupState::default(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user