forgive me krytpon

Working RPC, kind of, need to get icons working
This commit is contained in:
Soham Nandy 2023-01-18 20:23:29 +05:30
parent 6e831811cc
commit a4b2680a25
8 changed files with 201 additions and 95 deletions

22
Cargo.lock generated
View file

@ -58,6 +58,7 @@ dependencies = [
"anyhow",
"cached 0.41.0",
"dirs",
"discord-rich-presence",
"glib-build-tools",
"gtk4",
"lazy_static",
@ -589,6 +590,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "discord-rich-presence"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47fc4beffb85ee1461588499073a4d9c20dcc7728c4b13d6b282ab6c508947e5"
dependencies = [
"serde",
"serde_derive",
"serde_json",
"uuid",
]
[[package]]
name = "dispatch"
version = "0.2.0"
@ -2169,6 +2182,15 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
"getrandom",
]
[[package]]
name = "vcpkg"
version = "0.2.15"

View file

@ -16,11 +16,16 @@ opt-level = 3
glib-build-tools = "0.16"
[dependencies]
discord-rich-presence = "0.2.3"
gtk = { package = "gtk4", version = "0.5", features = ["v4_8"] }
adw = { package = "libadwaita", version = "0.2", features = ["v1_2"] }
rfd = { version = "0.10", features = ["xdg-portal"], default-features = false }
anime-game-core = { path = "anime-game-core", features = ["all", "static", "genshin"] }
anime-game-core = { path = "anime-game-core", features = [
"all",
"static",
"genshin",
] }
wincompatlib = { version = "0.1.3", features = ["dxvk"] }
serde = { version = "1.0", features = ["derive"] }

View file

@ -55,10 +55,10 @@ Adw.PreferencesPage page {
]
};
}
Adw.ComboRow discord_rpc_row
Adw.ActionRow discord_rpc_row
{
title: "Discord Rpc";
subtitle: "placeholder";
title: "Discord RPC";
subtitle: "Show your friends your lack of bitches";
Gtk.Switch discord_rpc_switch {
valign: center;
}

View file

@ -1,13 +1,12 @@
use std::thread::JoinHandle;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use super::prelude::Fsr;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscordRpc {
pub enabled: bool,
pub large_image_key: String,
pub app_id: u64,
pub app_id: String,
pub description: String,
pub state: String,
}
@ -17,7 +16,7 @@ impl Default for DiscordRpc {
Self {
enabled: true,
large_image_key: "gi-icon".to_string(),
app_id: 901534333360304168,
app_id: "901534333360304168".to_string(),
description: "Bullying Paimon".to_string(),
state: "In the weeb game".to_string(),
}
@ -30,44 +29,27 @@ impl From<&JsonValue> for DiscordRpc {
Self {
enabled: match value.get("enabled") {
Some(value) => value.as_bool().unwrap_or(default.enabled),
None => default.enabled
None => default.enabled,
},
description: match value.get("description") {
Some(value) => value.as_str().unwrap_or(&default.description).to_string(),
None => default.description
None => default.description,
},
state: match value.get("state") {
Some(value) => value.as_str().unwrap_or(&default.state).to_string(),
None => default.state
None => default.state,
},
large_image_key: match value.get("large_image_key"){
Some(value) => value.as_str().unwrap_or(&default.large_image_key).to_string(),
None => default.large_image_key
},
app_id: match value.get("app_id"){
Some(value) => value.as_u64().unwrap_or(default.app_id),
None => default.app_id
large_image_key: match value.get("large_image_key") {
Some(value) => value
.as_str()
.unwrap_or(&default.large_image_key)
.to_string(),
None => default.large_image_key,
},
app_id: "901534333360304168".to_string(),
}
}
}
impl DiscordRpc
{
pub fn toggle(&self)
{
println!("[Debug] RPC state changed!");
if self.enabled
{
todo!();
}
else
{
todo!();
}
}
}

View file

@ -2,8 +2,8 @@ use std::process::Command;
use anime_game_core::genshin::telemetry;
use super::consts;
use super::config;
use super::consts;
use super::fps_unlocker::FpsUnlocker;
/*#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -61,24 +61,35 @@ pub fn try_get_terminal() -> Option<Terminal> {
}*/
/// Try to run the game
///
///
/// If `debug = true`, then the game will be run in the new terminal window
pub fn run() -> anyhow::Result<()> {
let config = config::get()?;
std::thread::spawn(move || loop {
while config.game.enhancements.discord_rpc.enabled {
println!("RPC ENABLE");
std::thread::sleep(std::time::Duration::from_millis(100));
}
println!("RPC ENABLE");
std::thread::sleep(std::time::Duration::from_millis(100));
}).join().expect("Thread has panicked");
if !config.game.path.exists() {
return Err(anyhow::anyhow!("Game is not installed"));
}
let wine_executable = match config.try_get_wine_executable() {
Some(path) => path,
None => return Err(anyhow::anyhow!("Couldn't find wine executable"))
None => return Err(anyhow::anyhow!("Couldn't find wine executable")),
};
// Check telemetry servers
if let Some(server) = telemetry::is_disabled(consts::TELEMETRY_CHECK_TIMEOUT) {
return Err(anyhow::anyhow!("Telemetry server is not disabled: {server}"));
return Err(anyhow::anyhow!(
"Telemetry server is not disabled: {server}"
));
}
// Prepare fps unlocker
@ -94,28 +105,51 @@ pub fn run() -> anyhow::Result<()> {
// 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))?;
std::fs::remove_file(FpsUnlocker::get_binary_in(
&config.game.enhancements.fps_unlocker.path,
))?;
}
match FpsUnlocker::download(&config.game.enhancements.fps_unlocker.path) {
Ok(unlocker) => unlocker,
Err(err) => return Err(anyhow::anyhow!("Failed to download FPS unlocker: {err}"))
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.clone()) {
return Err(anyhow::anyhow!("Failed to update FPS unlocker config: {err}"));
if let Err(err) =
unlocker.update_config(config.game.enhancements.fps_unlocker.config.clone())
{
return Err(anyhow::anyhow!(
"Failed to update FPS unlocker config: {err}"
));
}
let bat_path = config.game.path.join("fpsunlocker.bat");
let original_bat_path = config.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())))?;
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()
),
),
)?;
}
// Prepare bash -c '<command>'
@ -132,7 +166,11 @@ pub fn run() -> anyhow::Result<()> {
bash_chain += &format!("{virtual_desktop} ");
}
bash_chain += if config.game.enhancements.fps_unlocker.enabled { "fpsunlocker.bat " } else { "launcher.bat " };
bash_chain += if config.game.enhancements.fps_unlocker.enabled {
"fpsunlocker.bat "
} else {
"launcher.bat "
};
if config.game.wine.borderless {
bash_chain += "-screen-fullscreen 0 -popupwindow ";
@ -150,7 +188,7 @@ pub fn run() -> anyhow::Result<()> {
let bash_chain = match &config.game.command {
Some(command) => command.replace("%command%", &bash_chain),
None => bash_chain
None => bash_chain,
};
let mut command = Command::new("bash");

View file

@ -1,15 +1,18 @@
use gtk::prelude::*;
use gtk::{CssProvider, StyleContext, STYLE_PROVIDER_PRIORITY_APPLICATION};
use discord_rich_presence::{
activity::{self, Activity, Party, Secrets},
DiscordIpc, DiscordIpcClient,
};
use gtk::gdk::Display;
use gtk::glib;
use gtk::glib::clone;
use gtk::prelude::*;
use gtk::{CssProvider, StyleContext, STYLE_PROVIDER_PRIORITY_APPLICATION};
use std::path::Path;
use std::fs;
use std::path::Path;
pub mod ui;
pub mod lib;
pub mod ui;
use ui::*;
@ -29,55 +32,112 @@ fn main() {
glib::set_program_name(Some("An Anime Game Launcher"));
// Create app
let application = gtk::Application::new(
Some(APP_ID),
Default::default()
);
let application = gtk::Application::new(Some(APP_ID), Default::default());
application.add_main_option(
"run-game",
"run-game",
glib::Char::from(0),
glib::OptionFlags::empty(),
glib::OptionArg::None,
"Run the game",
None
None,
);
application.add_main_option(
"just-run-game",
"just-run-game",
glib::Char::from(0),
glib::OptionFlags::empty(),
glib::OptionArg::None,
"Run the game whenever it possible, ignoring updates predownloads",
None
None,
);
let run_game = std::rc::Rc::new(std::cell::Cell::new(false));
let just_run_game = std::rc::Rc::new(std::cell::Cell::new(false));
application.connect_handle_local_options(clone!(@strong run_game, @strong just_run_game => move |_, arg| {
if arg.contains("just-run-game") {
just_run_game.set(true);
}
application.connect_handle_local_options(
clone!(@strong run_game, @strong just_run_game => move |_, arg| {
if arg.contains("just-run-game") {
just_run_game.set(true);
}
else if arg.contains("run-game") {
run_game.set(true);
}
else if arg.contains("run-game") {
run_game.set(true);
}
-1
}));
-1
}),
);
// Init app window and show it
application.connect_activate(move |app| {
let config = lib::config::get().expect("Failed to load config");
let mut client =
DiscordIpcClient::new(config.game.enhancements.discord_rpc.app_id.as_str())
.expect("Failed to create client");
match client.connect() {
Ok(_) => {
println!("Client connected to Discord successfully.");
}
Err(_) => {
println!(
"Client failed to connect to Discord, Please try again or relaunch Discord."
);
}
};
let _thread = std::thread::spawn(move || loop {
let conf = lib::config::get().expect("Failed to load config");
if conf.game.enhancements.discord_rpc.enabled {
let act = activity::Activity::new();
let activity_state: Activity = if config.game.enhancements.discord_rpc.state != "" {
act.state(config.game.enhancements.discord_rpc.state.as_str())
.clone()
} else {
act
};
let activity_details: Activity =
if config.game.enhancements.discord_rpc.description != "" {
activity_state
.state(config.game.enhancements.discord_rpc.description.as_str())
.clone()
} else {
activity_state
};
let activity_li: Activity =
if config.game.enhancements.discord_rpc.large_image_key != "" {
activity_details
.state(
config.game.enhancements.discord_rpc.large_image_key.as_str(),
)
.clone()
} else {
activity_details
};
match client.set_activity(activity_li) {
Ok(_) => {println!("Client set activity successfully.");}
Err(_) => {println!("Client failed to set activity, Please try again or relaunch Discord.");}
};
std::thread::sleep(std::time::Duration::from_millis(10));
} else {
match client.clear_activity(){
Ok(_) => {println!("Client activity cleared successfully.");}
Err(_) => {println!("Failed to clear.");}
}
}
std::thread::sleep(std::time::Duration::from_millis(10));
});
// Apply CSS styles to the application
let provider = CssProvider::new();
provider.load_from_data(include_bytes!("../assets/styles.css"));
StyleContext::add_provider_for_display(
&Display::default().expect("Could not connect to a display"),
&provider,
STYLE_PROVIDER_PRIORITY_APPLICATION
STYLE_PROVIDER_PRIORITY_APPLICATION,
);
// Create default launcher folder if needed
@ -85,16 +145,13 @@ fn main() {
if !launcher_dir.exists() || launcher_dir.join(".first-run").exists() {
fs::create_dir_all(&launcher_dir).expect("Failed to create default launcher dir");
fs::write(launcher_dir.join(".first-run"), "").expect("Failed to create .first-run file");
fs::write(launcher_dir.join(".first-run"), "")
.expect("Failed to create .first-run file");
let first_run = FirstRunApp::new(app).expect("Failed to init FirstRunApp");
first_run.show();
}
else {
let config = lib::config::get().expect("Failed to load config");
} else {
// Create wine builds folder
if !Path::new(&config.game.wine.builds).exists() {
fs::create_dir_all(config.game.wine.builds)
@ -118,9 +175,7 @@ fn main() {
if !run_game.get() && !just_run_game.get() {
main.show();
}
else {
} else {
use lib::launcher::states::LauncherState;
let just_run_game = just_run_game.get();
@ -132,20 +187,22 @@ fn main() {
if let LauncherState::PredownloadAvailable { game, voices } = state {
if just_run_game {
state = &LauncherState::Launch;
}
else if let Ok(config) = lib::config::get() {
} else if let Ok(config) = lib::config::get() {
let mut predownloaded = true;
let temp = config.launcher.temp.unwrap_or("/tmp".into());
if !temp.join(game.file_name().unwrap_or(String::from("\0"))).exists() {
if !temp
.join(game.file_name().unwrap_or(String::from("\0")))
.exists()
{
predownloaded = false;
}
else {
} else {
for voice in voices {
if !temp.join(voice.file_name().unwrap_or(String::from("\0"))).exists() {
if !temp
.join(voice.file_name().unwrap_or(String::from("\0")))
.exists()
{
predownloaded = false;
break;
@ -167,7 +224,7 @@ fn main() {
std::process::exit(0);
}
_ => main.show()
_ => main.show(),
}
});
}

View file

@ -275,6 +275,7 @@ impl App {
///
/// Changes will happen in the main thread so you can call `update` method from separate thread
pub fn init_actions(self) -> Self {
let (sender, receiver) = glib::MainContext::channel::<Actions>(glib::PRIORITY_DEFAULT);
// I prefer to avoid using clone! here because it breaks my code autocompletion

View file

@ -49,7 +49,7 @@ pub struct AppWidgets {
pub fps_unlocker_window_mode_combo: adw::ComboRow,
pub fps_unlocker_priority_combo: adw::ComboRow,
pub discord_rpc_row: adw::ComboRow,
pub discord_rpc_row: adw::ActionRow,
pub discord_rpc: gtk::Switch,
}
@ -109,6 +109,7 @@ impl AppWidgets {
result.gamescope_row.set_sensitive(false);
result.gamescope_row.set_tooltip_text(Some("Gamescope is not installed"));
}
result.discord_rpc_row.set_sensitive(true);
result.discord_rpc.set_sensitive(true);
Ok(result)
@ -166,8 +167,8 @@ impl App {
if let Ok(mut config) = config::get() {
config.game.wine.borderless = switch.state();
config::update(config);
config::game::enhancements::discordrpc::rpc_start();
}
});
// Virtual desktop resolution selection
@ -232,7 +233,7 @@ impl App {
if let Ok(mut config) = config::get()
{
config.game.enhancements.discord_rpc.enabled = switch.state();
config.game.enhancements.discord_rpc.toggle();
// config.game.enhancements.discord_rpc.toggle();
config::update(config);
}
});