From ba1110908259fa893036f9b37ffef2576d0c2f92 Mon Sep 17 00:00:00 2001 From: Observer KRypt0n_ Date: Wed, 8 Mar 2023 18:36:01 +0200 Subject: [PATCH] 0.5.0 - improved wine/dxvk features, added command used to run the game --- Cargo.toml | 2 +- src/components/dxvk.rs | 13 +++++++++-- src/components/loader.rs | 25 ++++++++++++++------ src/components/wine.rs | 49 +++++++++++++++++++++++++++++++--------- src/game.rs | 49 +++++++++++++++++++++++++++++++++------- 5 files changed, 109 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cef3569..4c48420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "anime-launcher-sdk" -version = "0.4.0" +version = "0.5.0" authors = ["Nikita Podvirnyy "] license = "GPL-3.0" readme = "README.md" diff --git a/src/components/dxvk.rs b/src/components/dxvk.rs index ecd8bc0..a44d12b 100644 --- a/src/components/dxvk.rs +++ b/src/components/dxvk.rs @@ -34,6 +34,14 @@ impl Group { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Features { + /// Standard environment variables that are applied when you launch the game + /// + /// Available keywords: + /// - `%build%` - path to wine build + /// - `%prefix%` - path to wine prefix + /// - `%temp%` - path to temp folder specified in config file + /// - `%launcher%` - path to launcher folder + /// - `%game%` - path to the game pub env: HashMap } @@ -70,11 +78,12 @@ impl From<&JsonValue> for Features { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Version { pub name: String, pub version: String, - pub uri: String + pub uri: String, + pub features: Option } impl Version { diff --git a/src/components/loader.rs b/src/components/loader.rs index f818576..95d4a54 100644 --- a/src/components/loader.rs +++ b/src/components/loader.rs @@ -36,6 +36,11 @@ pub fn get_wine_versions(index: &Path) -> anyhow::Result> { None => anyhow::bail!("Wrong components index structure: wine group's title not found") }; + let features = match group.get("features") { + Some(features) => features.into(), + None => wine::Features::default() + }; + let versions = serde_json::from_str::(&std::fs::read_to_string(index.join("wine").join(format!("{name}.json")))?)?; let mut wine_versions = Vec::new(); @@ -43,18 +48,19 @@ pub fn get_wine_versions(index: &Path) -> anyhow::Result> { match versions.as_array() { Some(versions) => { for version in versions { - wine_versions.push(serde_json::from_value::(version.to_owned())?); + wine_versions.push(wine::Version { + name: version["name"].as_str().unwrap().to_string(), + title: version["title"].as_str().unwrap().to_string(), + uri: version["uri"].as_str().unwrap().to_string(), + files: serde_json::from_value::(version["files"].to_owned())?, + features: version.get("features").map(|v| v.into()) + }); } } None => anyhow::bail!("Wrong components index structure: wine versions must be a list") } - let features = match group.get("features") { - Some(features) => features.into(), - None => wine::Features::default() - }; - wine_groups.push(wine::Group { name, title, @@ -112,7 +118,12 @@ pub fn get_dxvk_versions(index: &Path) -> anyhow::Result> { match versions.as_array() { Some(versions) => { for version in versions { - dxvk_versions.push(serde_json::from_value::(version.to_owned())?); + dxvk_versions.push(dxvk::Version { + name: version["name"].as_str().unwrap().to_string(), + version: version["version"].as_str().unwrap().to_string(), + uri: version["uri"].as_str().unwrap().to_string(), + features: version.get("features").map(|v| v.into()) + }); } } diff --git a/src/components/wine.rs b/src/components/wine.rs index d92b76f..b03c558 100644 --- a/src/components/wine.rs +++ b/src/components/wine.rs @@ -34,7 +34,27 @@ impl Group { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Features { + /// Whether this wine group needs DXVK pub need_dxvk: bool, + + /// Command used to launch the game + /// + /// Available keywords: + /// - `%build%` - path to wine build + /// - `%prefix%` - path to wine prefix + /// - `%temp%` - path to temp folder specified in config file + /// - `%launcher%` - path to launcher folder + /// - `%game%` - path to the game + pub command: Option, + + /// Standard environment variables that are applied when you launch the game + /// + /// Available keywords: + /// - `%build%` - path to wine build + /// - `%prefix%` - path to wine prefix + /// - `%temp%` - path to temp folder specified in config file + /// - `%launcher%` - path to launcher folder + /// - `%game%` - path to the game pub env: HashMap } @@ -42,6 +62,7 @@ impl Default for Features { fn default() -> Self { Self { need_dxvk: true, + command: None, env: HashMap::new() } } @@ -57,6 +78,11 @@ impl From<&JsonValue> for Features { None => default.need_dxvk }, + command: match value.get("command") { + Some(value) => value.as_str().map(|value| value.to_string()), + None => default.command + }, + env: match value.get("env") { Some(value) => { if let Some(object) = value.as_object() { @@ -77,21 +103,13 @@ impl From<&JsonValue> for Features { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Version { pub name: String, pub title: String, pub uri: String, - pub files: Files -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Files { - pub wine: String, - pub wine64: Option, - pub wineserver: Option, - pub wineboot: Option, - pub winecfg: Option + pub files: Files, + pub features: Option } impl Version { @@ -158,6 +176,15 @@ impl Version { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Files { + pub wine: String, + pub wine64: Option, + pub wineserver: Option, + pub wineboot: Option, + pub winecfg: Option +} + pub fn get_groups>(components: T) -> anyhow::Result> { ComponentsLoader::new(components).get_wine_versions() } diff --git a/src/game.rs b/src/game.rs index abc609b..d988b74 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,5 @@ use std::process::{Command, Stdio}; +use std::path::PathBuf; use anime_game_core::genshin::telemetry; @@ -11,6 +12,17 @@ use super::fps_unlocker::FpsUnlocker; #[cfg(feature = "discord-rpc")] use super::discord_rpc::*; +fn replace_keywords(command: T, config: &config::Config) -> String { + let wine_build = config.game.wine.builds.join(config.game.wine.selected.as_ref().unwrap()); + + command.to_string() + .replace("%build%", &wine_build.to_string_lossy()) + .replace("%prefix%", &config.game.wine.prefix.to_string_lossy()) + .replace("%temp%", &config.launcher.temp.as_ref().unwrap_or(&PathBuf::from("/tmp")).to_string_lossy()) + .replace("%launcher%", &consts::launcher_dir().unwrap().to_string_lossy()) + .replace("%game%", &config.game.path.to_string_lossy()) +} + /// Try to run the game /// /// This function will freeze thread it was called from while the game is running @@ -28,6 +40,14 @@ pub fn run() -> anyhow::Result<()> { anyhow::bail!("Couldn't find wine executable"); }; + let features = match wine.features { + Some(features) => features, + None => match wine.find_group(&config.components.path)? { + Some(group) => group.features, + None => anyhow::bail!("Couldn't find wine group") + } + }; + // Check telemetry servers tracing::info!("Checking telemetry"); @@ -86,7 +106,14 @@ pub fn run() -> anyhow::Result<()> { bash_chain += "gamemoderun "; } - bash_chain += &format!("'{}' ", config.game.wine.builds.join(wine.name).join(wine.files.wine64.unwrap_or(wine.files.wine)).to_string_lossy()); + let wine_build = config.game.wine.builds.join(&wine.name); + + let run_command = features.command + .map(|command| replace_keywords(command, &config)) + .unwrap_or(format!("'{}'", wine_build.join(wine.files.wine64.unwrap_or(wine.files.wine)).to_string_lossy())); + + bash_chain += &run_command; + bash_chain += " "; if let Some(virtual_desktop) = config.game.wine.virtual_desktop.get_command() { bash_chain += &format!("{virtual_desktop} "); @@ -113,7 +140,7 @@ pub fn run() -> anyhow::Result<()> { } let bash_chain = match &config.game.command { - Some(command) => command.replace("%command%", &bash_chain), + Some(command) => replace_keywords(command, &config).replace("%command%", &bash_chain), None => bash_chain }; @@ -128,16 +155,22 @@ pub fn run() -> anyhow::Result<()> { command.env("WINEPREFIX", &config.game.wine.prefix); // Add environment flags for selected wine - if let Ok(Some(wine )) = config.get_selected_wine() { - if let Ok(Some(group)) = wine.find_group(&config.components.path) { - command.envs(group.features.env); - } + for (key, value) in features.env.into_iter() { + command.env(key, replace_keywords(value, &config)); } // Add environment flags for selected dxvk if let Ok(Some(dxvk )) = config.get_selected_dxvk() { - if let Ok(Some(group)) = dxvk.find_group(&config.components.path) { - command.envs(group.features.env); + if let Some(features) = &dxvk.features { + for (key, value) in features.env.iter() { + command.env(key, replace_keywords(value, &config)); + } + } + + else if let Ok(Some(group)) = dxvk.find_group(&config.components.path) { + for (key, value) in group.features.env.into_iter() { + command.env(key, replace_keywords(value, &config)); + } } }