0.2.0
- added experimental discord rpc support
This commit is contained in:
parent
f1f8eb6a2c
commit
420326a8a0
7 changed files with 229 additions and 8 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "anime-launcher-sdk"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
authors = ["Nikita Podvirnyy <suimin.tu.mu.ga.mi@gmail.com>"]
|
||||
license = "GPL-3.0"
|
||||
readme = "README.md"
|
||||
|
@ -20,6 +20,7 @@ enum-ordinalize = { version = "3.1", optional = true }
|
|||
wincompatlib = { version = "0.2", optional = true }
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
md-5 = { version = "0.10", features = ["asm"], optional = true }
|
||||
discord-rich-presence = { version = "0.2.3", optional = true }
|
||||
|
||||
[features]
|
||||
states = []
|
||||
|
@ -27,6 +28,7 @@ config = ["dep:serde", "dep:serde_json", "dep:enum-ordinalize"]
|
|||
components = ["dep:wincompatlib", "dep:lazy_static"]
|
||||
game = ["components", "config"]
|
||||
fps-unlocker = ["dep:md-5"]
|
||||
discord-rpc = ["dep:discord-rich-presence"]
|
||||
|
||||
default = ["all"]
|
||||
all = ["states", "config", "components", "game", "fps-unlocker"]
|
||||
all = ["states", "config", "components", "game", "fps-unlocker", "discord-rpc"]
|
||||
|
|
|
@ -3,16 +3,22 @@ use serde_json::Value as JsonValue;
|
|||
|
||||
pub mod fsr;
|
||||
pub mod hud;
|
||||
pub mod fps_unlocker;
|
||||
pub mod gamescope;
|
||||
|
||||
#[cfg(feature = "fps-unlocker")]
|
||||
pub mod fps_unlocker;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::gamescope::prelude::*;
|
||||
|
||||
#[cfg(feature = "fps-unlocker")]
|
||||
pub use super::fps_unlocker::prelude::*;
|
||||
|
||||
pub use super::Enhancements;
|
||||
pub use super::fsr::Fsr;
|
||||
pub use super::hud::HUD;
|
||||
|
||||
#[cfg(feature = "fps-unlocker")]
|
||||
pub use super::fps_unlocker::FpsUnlocker;
|
||||
}
|
||||
|
||||
|
@ -23,7 +29,10 @@ pub struct Enhancements {
|
|||
pub fsr: Fsr,
|
||||
pub gamemode: bool,
|
||||
pub hud: HUD,
|
||||
|
||||
#[cfg(feature = "fps-unlocker")]
|
||||
pub fps_unlocker: FpsUnlocker,
|
||||
|
||||
pub gamescope: Gamescope
|
||||
}
|
||||
|
||||
|
@ -47,6 +56,7 @@ impl From<&JsonValue> for Enhancements {
|
|||
None => default.hud
|
||||
},
|
||||
|
||||
#[cfg(feature = "fps-unlocker")]
|
||||
fps_unlocker: match value.get("fps_unlocker") {
|
||||
Some(value) => FpsUnlocker::from(value),
|
||||
None => default.fps_unlocker
|
||||
|
|
57
src/config/launcher/discord_rpc.rs
Normal file
57
src/config/launcher/discord_rpc.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DiscordRpc {
|
||||
pub app_id: u64,
|
||||
pub enabled: bool,
|
||||
pub title: String,
|
||||
pub subtitle: String,
|
||||
pub icon: String
|
||||
}
|
||||
|
||||
impl Default for DiscordRpc {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
app_id: 901534333360304168,
|
||||
enabled: false,
|
||||
|
||||
title: String::from("Researching the world"),
|
||||
subtitle: String::from("of Teyvat"),
|
||||
icon: String::from("launcher")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&JsonValue> for DiscordRpc {
|
||||
fn from(value: &JsonValue) -> Self {
|
||||
let default = Self::default();
|
||||
|
||||
Self {
|
||||
app_id: match value.get("app_id") {
|
||||
Some(value) => value.as_u64().unwrap_or(default.app_id),
|
||||
None => default.app_id
|
||||
},
|
||||
|
||||
enabled: match value.get("enabled") {
|
||||
Some(value) => value.as_bool().unwrap_or(default.enabled),
|
||||
None => default.enabled
|
||||
},
|
||||
|
||||
title: match value.get("title") {
|
||||
Some(value) => value.as_str().unwrap_or(&default.title).to_string(),
|
||||
None => default.title
|
||||
},
|
||||
|
||||
subtitle: match value.get("subtitle") {
|
||||
Some(value) => value.as_str().unwrap_or(&default.subtitle).to_string(),
|
||||
None => default.subtitle
|
||||
},
|
||||
|
||||
icon: match value.get("icon") {
|
||||
Some(value) => value.as_str().unwrap_or(&default.icon).to_string(),
|
||||
None => default.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,9 +11,15 @@ use crate::consts::launcher_dir;
|
|||
|
||||
pub mod repairer;
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub mod discord_rpc;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::Launcher;
|
||||
pub use super::repairer::Repairer;
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub use super::discord_rpc::DiscordRpc;
|
||||
}
|
||||
|
||||
use prelude::*;
|
||||
|
@ -33,7 +39,7 @@ impl Default for GameEdition {
|
|||
})
|
||||
});
|
||||
|
||||
if locale.len() > 4 && &locale[..5].to_lowercase() == "zh_cn" {
|
||||
if locale.len() > 4 && &locale[..5].to_ascii_lowercase() == "zh_cn" {
|
||||
Self::China
|
||||
} else {
|
||||
Self::Global
|
||||
|
@ -80,7 +86,10 @@ pub struct Launcher {
|
|||
pub speed_limit: u64,
|
||||
pub repairer: Repairer,
|
||||
pub edition: GameEdition,
|
||||
pub style: LauncherStyle
|
||||
pub style: LauncherStyle,
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub discord_rpc: DiscordRpc
|
||||
}
|
||||
|
||||
impl Default for Launcher {
|
||||
|
@ -91,7 +100,10 @@ impl Default for Launcher {
|
|||
speed_limit: 0,
|
||||
repairer: Repairer::default(),
|
||||
edition: GameEdition::default(),
|
||||
style: LauncherStyle::default()
|
||||
style: LauncherStyle::default(),
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
discord_rpc: DiscordRpc::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +150,12 @@ impl From<&JsonValue> for Launcher {
|
|||
style: match value.get("style") {
|
||||
Some(value) => serde_json::from_value(value.clone()).unwrap_or(default.style),
|
||||
None => default.style
|
||||
},
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
discord_rpc: match value.get("discord_rpc") {
|
||||
Some(value) => DiscordRpc::from(value),
|
||||
None => default.discord_rpc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
108
src/discord_rpc.rs
Normal file
108
src/discord_rpc.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
use std::thread::JoinHandle;
|
||||
use std::sync::mpsc::{self, Sender, SendError};
|
||||
|
||||
use discord_rich_presence::{
|
||||
activity::*,
|
||||
DiscordIpc,
|
||||
DiscordIpcClient
|
||||
};
|
||||
|
||||
use super::config::prelude::DiscordRpc as DiscordRpcConfig;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RpcUpdates {
|
||||
/// Establish RPC connection
|
||||
Connect,
|
||||
|
||||
/// Terminate RPC connection. Panics if not connected
|
||||
Disconnect,
|
||||
|
||||
/// Update RPC activity
|
||||
UpdateActivity {
|
||||
title: String,
|
||||
subtitle: String,
|
||||
icon: String
|
||||
},
|
||||
|
||||
/// Clear RPC activity
|
||||
ClearActivity
|
||||
}
|
||||
|
||||
pub struct DiscordRpc {
|
||||
_thread: JoinHandle<()>,
|
||||
sender: Sender<RpcUpdates>
|
||||
}
|
||||
|
||||
impl DiscordRpc {
|
||||
pub fn new(mut config: DiscordRpcConfig) -> Self {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
Self {
|
||||
_thread: std::thread::spawn(move || {
|
||||
let mut client = DiscordIpcClient::new(&config.app_id.to_string())
|
||||
.expect("Failed to register discord ipc client");
|
||||
|
||||
let mut connected = false;
|
||||
|
||||
while let Ok(update) = receiver.recv() {
|
||||
match update {
|
||||
RpcUpdates::Connect => {
|
||||
if !connected {
|
||||
connected = true;
|
||||
|
||||
client.connect().expect("Failed to connect to discord");
|
||||
|
||||
client.set_activity(Self::get_activity(&config))
|
||||
.expect("Failed to update discord rpc activity");
|
||||
}
|
||||
}
|
||||
|
||||
RpcUpdates::Disconnect => {
|
||||
if connected {
|
||||
connected = false;
|
||||
|
||||
client.close().expect("Failed to disconnect from discord");
|
||||
}
|
||||
}
|
||||
|
||||
RpcUpdates::UpdateActivity { title, subtitle, icon } => {
|
||||
config.title = title;
|
||||
config.subtitle = subtitle;
|
||||
config.icon = icon;
|
||||
|
||||
if connected {
|
||||
client.set_activity(Self::get_activity(&config))
|
||||
.expect("Failed to update discord rpc activity");
|
||||
}
|
||||
}
|
||||
|
||||
RpcUpdates::ClearActivity => {
|
||||
if connected {
|
||||
client.clear_activity().expect("Failed to clear discord rpc activity");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
sender
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_activity(config: &DiscordRpcConfig) -> Activity {
|
||||
Activity::new()
|
||||
.details(&config.title)
|
||||
.state(&config.subtitle)
|
||||
.assets(Assets::new().large_image(&config.icon))
|
||||
}
|
||||
|
||||
pub fn update(&self, update: RpcUpdates) -> Result<(), SendError<RpcUpdates>> {
|
||||
self.sender.send(update)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DiscordRpc {
|
||||
#[allow(unused_must_use)]
|
||||
fn drop(&mut self) {
|
||||
self.update(RpcUpdates::Disconnect);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use std::process::Command;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use anime_game_core::genshin::telemetry;
|
||||
|
||||
|
@ -8,6 +8,9 @@ use super::config;
|
|||
#[cfg(feature = "fps-unlocker")]
|
||||
use super::fps_unlocker::FpsUnlocker;
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use super::discord_rpc::*;
|
||||
|
||||
/// Try to run the game
|
||||
///
|
||||
/// If `debug = true`, then the game will be run in the new terminal window
|
||||
|
@ -63,7 +66,7 @@ pub fn run() -> anyhow::Result<()> {
|
|||
};
|
||||
|
||||
// Generate FPS unlocker config file
|
||||
if let Err(err) = unlocker.update_config(config.game.enhancements.fps_unlocker.config.clone()) {
|
||||
if let Err(err) = unlocker.update_config(config.game.enhancements.fps_unlocker.config) {
|
||||
return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}"));
|
||||
}
|
||||
|
||||
|
@ -150,5 +153,25 @@ pub fn run() -> anyhow::Result<()> {
|
|||
|
||||
command.current_dir(config.game.path).spawn()?;
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
if config.launcher.discord_rpc.enabled {
|
||||
let rpc = DiscordRpc::new(config.launcher.discord_rpc);
|
||||
|
||||
rpc.update(RpcUpdates::Connect)?;
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
std::thread::spawn(move || {
|
||||
while let Ok(output) = Command::new("ps").arg("-A").stdout(Stdio::piped()).output() {
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
if !output.contains("GenshinImpact.e") && !output.contains("unlocker.exe") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rpc.update(RpcUpdates::Disconnect);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -20,6 +20,9 @@ pub mod game;
|
|||
#[cfg(feature = "fps-unlocker")]
|
||||
pub mod fps_unlocker;
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub mod discord_rpc;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// Check if specified binary is available
|
||||
|
|
Loading…
Reference in a new issue