feat: added game
and discord_rpc
to honkers feature
This commit is contained in:
parent
db0f7238f8
commit
3e7f38f210
9 changed files with 104 additions and 92 deletions
|
@ -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())
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -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(¶ms.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(¶ms))
|
||||
.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(¶ms))
|
||||
.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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
|
@ -5,3 +5,6 @@ pub mod states;
|
|||
|
||||
#[cfg(feature = "config")]
|
||||
pub mod config;
|
||||
|
||||
#[cfg(feature = "game")]
|
||||
pub mod game;
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue