feat: added game and discord_rpc to honkers feature

This commit is contained in:
Observer KRypt0n_ 2023-04-14 21:13:48 +02:00
parent db0f7238f8
commit 3e7f38f210
No known key found for this signature in database
GPG key ID: 844DA47BA25FE1E2
9 changed files with 104 additions and 92 deletions

View file

@ -39,11 +39,11 @@ impl From<&JsonValue> for Fsr {
impl Fsr {
/// Get environment variables corresponding to used amd fsr options
pub fn get_env_vars(&self) -> HashMap<&str, u64> {
pub fn get_env_vars(&self) -> HashMap<&str, String> {
if self.enabled {
HashMap::from([
("WINE_FULLSCREEN_FSR", 1),
("WINE_FULLSCREEN_FSR_STRENGTH", self.strength)
("WINE_FULLSCREEN_FSR", String::from("1")),
("WINE_FULLSCREEN_FSR_STRENGTH", self.strength.to_string())
])
}

View file

@ -7,7 +7,14 @@ use discord_rich_presence::{
DiscordIpcClient
};
use super::config::prelude::DiscordRpc as DiscordRpcConfig;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiscordRpcParams {
pub app_id: u64,
pub enabled: bool,
pub title: String,
pub subtitle: String,
pub icon: String
}
#[derive(Debug, Clone)]
pub enum RpcUpdates {
@ -34,12 +41,12 @@ pub struct DiscordRpc {
}
impl DiscordRpc {
pub fn new(mut config: DiscordRpcConfig) -> Self {
pub fn new(mut params: DiscordRpcParams) -> Self {
let (sender, receiver) = mpsc::channel();
Self {
_thread: std::thread::spawn(move || {
let mut client = DiscordIpcClient::new(&config.app_id.to_string())
let mut client = DiscordIpcClient::new(&params.app_id.to_string())
.expect("Failed to register discord ipc client");
let mut connected = false;
@ -52,7 +59,7 @@ impl DiscordRpc {
client.connect().expect("Failed to connect to discord");
client.set_activity(Self::get_activity(&config))
client.set_activity(Self::get_activity(&params))
.expect("Failed to update discord rpc activity");
}
}
@ -66,12 +73,12 @@ impl DiscordRpc {
}
RpcUpdates::UpdateActivity { title, subtitle, icon } => {
config.title = title;
config.subtitle = subtitle;
config.icon = icon;
params.title = title;
params.subtitle = subtitle;
params.icon = icon;
if connected {
client.set_activity(Self::get_activity(&config))
client.set_activity(Self::get_activity(&params))
.expect("Failed to update discord rpc activity");
}
}
@ -88,7 +95,7 @@ impl DiscordRpc {
}
}
pub fn get_activity(config: &DiscordRpcConfig) -> Activity {
pub fn get_activity(config: &DiscordRpcParams) -> Activity {
Activity::new()
.details(&config.title)
.state(&config.subtitle)

View file

@ -2,6 +2,8 @@ use std::path::PathBuf;
pub mod schema;
pub use schema::Schema;
use crate::config::Config as ConfigTrait;
use crate::honkai::consts::config_file;

View file

@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use crate::discord_rpc::DiscordRpcParams;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DiscordRpc {
pub app_id: u64,
@ -10,6 +12,19 @@ pub struct DiscordRpc {
pub icon: String
}
impl From<DiscordRpc> for DiscordRpcParams {
#[inline]
fn from(config: DiscordRpc) -> Self {
Self {
app_id: config.app_id,
enabled: config.enabled,
title: config.title,
subtitle: config.subtitle,
icon: config.icon
}
}
}
// TODO: add honkers-specific discord rpc
impl Default for DiscordRpc {

View file

@ -3,15 +3,31 @@ use std::path::PathBuf;
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
use enum_ordinalize::Ordinalize;
#[cfg(feature = "discord-rpc")]
pub mod discord_rpc;
use crate::config::schema_blanks::prelude::*;
use crate::honkai::consts::launcher_dir;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ordinalize, Serialize, Deserialize)]
pub enum LauncherStyle {
Modern,
Classic
}
impl Default for LauncherStyle {
#[inline]
fn default() -> Self {
Self::Modern
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Launcher {
pub language: String,
pub style: LauncherStyle,
pub temp: Option<PathBuf>,
pub repairer: Repairer,
@ -24,6 +40,7 @@ impl Default for Launcher {
fn default() -> Self {
Self {
language: String::from("en-us"),
style: LauncherStyle::default(),
temp: launcher_dir().ok(),
repairer: Repairer::default(),
@ -43,6 +60,11 @@ impl From<&JsonValue> for Launcher {
None => default.language
},
style: match value.get("style") {
Some(value) => serde_json::from_value(value.to_owned()).unwrap_or_default(),
None => default.style
},
temp: match value.get("temp") {
Some(value) => {
if value.is_null() {

View file

@ -1,6 +1,12 @@
use serde::{Serialize, Deserialize};
use serde_json::Value as JsonValue;
#[cfg(feature = "components")]
use crate::components::wine::Version as WineVersion;
#[cfg(feature = "components")]
use crate::components::dxvk::Version as DxvkVersion;
pub mod launcher;
pub mod game;
pub mod patch;
@ -57,3 +63,23 @@ impl From<&JsonValue> for Schema {
}
}
}
impl Schema {
#[cfg(feature = "components")]
/// Get selected wine version
pub fn get_selected_wine(&self) -> anyhow::Result<Option<WineVersion>> {
match &self.game.wine.selected {
Some(selected) => WineVersion::find_in(&self.components.path, selected),
None => Ok(None)
}
}
#[cfg(feature = "components")]
/// Get selected dxvk version
pub fn get_selected_dxvk(&self) -> anyhow::Result<Option<DxvkVersion>> {
match wincompatlib::dxvk::Dxvk::get_version(&self.game.wine.prefix)? {
Some(version) => DxvkVersion::find_in(&self.components.path, version),
None => Ok(None)
}
}
}

View file

@ -1,19 +1,17 @@
use std::process::{Command, Stdio};
use anime_game_core::prelude::*;
use anime_game_core::genshin::telemetry;
use anime_game_core::genshin::game::Game;
use anime_game_core::honkai::telemetry;
use super::consts;
use super::config;
use crate::config::Config as _;
use crate::honkai::config::Config;
use crate::honkai::config::schema::Schema;
#[cfg(feature = "fps-unlocker")]
use super::fps_unlocker::FpsUnlocker;
use crate::honkai::consts;
#[cfg(feature = "discord-rpc")]
use super::discord_rpc::*;
use crate::discord_rpc::*;
fn replace_keywords<T: ToString>(command: T, config: &config::Config) -> String {
fn replace_keywords<T: ToString>(command: T, config: &Schema) -> String {
let wine_build = config.game.wine.builds.join(config.game.wine.selected.as_ref().unwrap());
command.to_string()
@ -21,7 +19,7 @@ fn replace_keywords<T: ToString>(command: T, config: &config::Config) -> String
.replace("%prefix%", &config.game.wine.prefix.to_string_lossy())
.replace("%temp%", &config.launcher.temp.as_ref().unwrap_or(&std::env::temp_dir()).to_string_lossy())
.replace("%launcher%", &consts::launcher_dir().unwrap().to_string_lossy())
.replace("%game%", &config.game.path.for_edition(config.launcher.edition).to_string_lossy())
.replace("%game%", &config.game.path.to_string_lossy())
}
/// Try to run the game
@ -31,10 +29,9 @@ fn replace_keywords<T: ToString>(command: T, config: &config::Config) -> String
pub fn run() -> anyhow::Result<()> {
tracing::info!("Preparing to run the game");
let config = config::get()?;
let game_path = config.game.path.for_edition(config.launcher.edition);
let config = Config::get()?;
if !game_path.exists() {
if !config.game.path.exists() {
return Err(anyhow::anyhow!("Game is not installed"));
}
@ -52,59 +49,6 @@ pub fn run() -> anyhow::Result<()> {
return Err(anyhow::anyhow!("Telemetry server is not disabled: {server}"));
}
// Prepare fps unlocker
// 1) Download if needed
// 2) Generate config file
// 3) Generate fpsunlocker.bat from launcher.bat
#[cfg(feature = "fps-unlocker")]
if config.game.enhancements.fps_unlocker.enabled {
tracing::info!("Preparing FPS unlocker");
let unlocker = match FpsUnlocker::from_dir(&config.game.enhancements.fps_unlocker.path) {
Ok(Some(unlocker)) => unlocker,
other => {
// Ok(None) means unknown version, so we should delete it before downloading newer one
// because otherwise downloader will try to continue downloading "partially downloaded" file
if let Ok(None) = other {
std::fs::remove_file(FpsUnlocker::get_binary_in(&config.game.enhancements.fps_unlocker.path))?;
}
tracing::info!("Unlocker is not downloaded. Downloading");
match FpsUnlocker::download(&config.game.enhancements.fps_unlocker.path) {
Ok(unlocker) => unlocker,
Err(err) => return Err(anyhow::anyhow!("Failed to download FPS unlocker: {err}"))
}
}
};
// Generate FPS unlocker config file
if let Err(err) = unlocker.update_config(config.game.enhancements.fps_unlocker.config) {
return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}"));
}
let bat_path = game_path.join("fps_unlocker.bat");
let original_bat_path = game_path.join("launcher.bat");
// Generate fpsunlocker.bat from launcher.bat
std::fs::write(bat_path, std::fs::read_to_string(original_bat_path)?
.replace("start GenshinImpact.exe %*", &format!("start GenshinImpact.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy()))
.replace("start YuanShen.exe %*", &format!("start YuanShen.exe %*\n\nZ:\ncd \"{}\"\nstart unlocker.exe", unlocker.dir().to_string_lossy())))?;
}
// Generate `config.ini` if environment emulation feature is presented
#[cfg(feature = "environment-emulation")] {
let game = Game::new(game_path);
std::fs::write(
game_path.join("config.ini"),
config.launcher.environment.generate_config(game.get_version()?.to_string())
)?;
}
// Prepare bash -c '<command>'
let mut bash_command = String::new();
@ -123,16 +67,12 @@ pub fn run() -> anyhow::Result<()> {
bash_command += &run_command;
bash_command += " ";
if let Some(virtual_desktop) = config.game.wine.virtual_desktop.get_command() {
if let Some(virtual_desktop) = config.game.wine.virtual_desktop.get_command("honkers") {
windows_command += &virtual_desktop;
windows_command += " ";
}
windows_command += if config.game.enhancements.fps_unlocker.enabled && cfg!(feature = "fps-unlocker") {
"fps_unlocker.bat "
} else {
"launcher.bat "
};
windows_command += "launcher.bat ";
if config.game.wine.borderless {
windows_command += "-screen-fullscreen 0 -popupwindow ";
@ -150,7 +90,7 @@ pub fn run() -> anyhow::Result<()> {
// Bundle all windows arguments used to run the game into a single file
if features.compact_launch {
std::fs::write(game_path.join("compact_launch.bat"), format!("start {windows_command}\nexit"))?;
std::fs::write(config.game.path.join("compact_launch.bat"), format!("start {windows_command}\nexit"))?;
windows_command = String::from("compact_launch.bat");
}
@ -185,7 +125,7 @@ pub fn run() -> anyhow::Result<()> {
}
command.envs(config.game.wine.sync.get_env_vars());
command.envs(config.game.enhancements.hud.get_env_vars(&config));
command.envs(config.game.enhancements.hud.get_env_vars(config.game.enhancements.gamescope.enabled));
command.envs(config.game.enhancements.fsr.get_env_vars());
command.envs(config.game.wine.language.get_env_vars());
@ -200,11 +140,11 @@ pub fn run() -> anyhow::Result<()> {
tracing::info!("Running the game with command: {variables} bash -c \"{bash_command}\"");
command.current_dir(game_path).spawn()?.wait_with_output()?;
command.current_dir(config.game.path).spawn()?.wait_with_output()?;
#[cfg(feature = "discord-rpc")]
let rpc = if config.launcher.discord_rpc.enabled {
Some(DiscordRpc::new(config.launcher.discord_rpc))
Some(DiscordRpc::new(config.launcher.discord_rpc.into()))
} else {
None
};

View file

@ -5,3 +5,6 @@ pub mod states;
#[cfg(feature = "config")]
pub mod config;
#[cfg(feature = "game")]
pub mod game;

View file

@ -15,11 +15,8 @@ pub mod config;
#[cfg(feature = "components")]
pub mod components;
#[cfg(feature = "game")]
// pub mod game;
#[cfg(feature = "discord-rpc")]
// pub mod discord_rpc;
pub mod discord_rpc;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");