From 1e40bd09ba37da5307d007c4811d3c8f9c0bcc99 Mon Sep 17 00:00:00 2001 From: Songtronix Date: Tue, 4 Jun 2019 22:12:32 +0200 Subject: [PATCH] working discord presence --- voxygen/src/discord.rs | 104 +++++++++++++++++++++++++++++++++++++++++ voxygen/src/main.rs | 60 +++++++++++++++++++++--- 2 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 voxygen/src/discord.rs diff --git a/voxygen/src/discord.rs b/voxygen/src/discord.rs new file mode 100644 index 0000000000..ca4ef32a51 --- /dev/null +++ b/voxygen/src/discord.rs @@ -0,0 +1,104 @@ +use discord_game_sdk::{Activity, Discord}; +use std::ffi::CString; +use std::sync::mpsc::Sender; +use std::sync::{mpsc, Mutex, MutexGuard}; +use std::thread; +use std::thread::JoinHandle; + +use crate::DEFAULT_PUBLIC_SERVER; +use chrono::Utc; + +const DISCORD_APPLICATION_ID: i64 = 583662036194689035; + +/// Represents an update of the game which should be reflected in Discord +pub enum DiscordUpdate { + Details(String), + State(String), + SmallImg(String), + SmallImgDesc(String), + LargeImg(String), + LargeImgDesc(String), + Shutdown, +} + +pub struct DiscordState { + pub tx: Sender, + pub thread: Option>, +} + +pub fn run() -> Mutex { + let (tx, rx) = mpsc::channel(); + + Mutex::new(DiscordState { + tx, + thread: Some(thread::spawn(move || { + let mut discord = + Discord::new(DISCORD_APPLICATION_ID).expect("failed to initiate discord_game_sdk"); + + //Set initial Status + let mut current_activity = Activity::empty(); + current_activity.with_details(CString::new("Menu").expect("failed to create CString")); + current_activity.with_state(CString::new("Idling").expect("failed to create CString")); + current_activity.with_small_image_key( + CString::new("veloren_logo_squareicon_2000").expect("failed to create CString"), + ); + current_activity.with_small_image_tooltip( + CString::new("Veloren").expect("failed to create CString"), + ); + current_activity + .with_large_image_key(CString::new("bg_main").expect("failed to create CString")); + current_activity.with_large_image_tooltip( + CString::new("veloren.net").expect("failed to create CString"), + ); + + current_activity.with_start_time(Utc::now()); + + discord.update_activity(¤t_activity, |_, _| {}); + + 'outer: loop { + discord.empty_event_receivers(); + discord.run_callbacks(); + + let msg = rx.try_recv(); + match msg { + Ok(update) => { + match update { + DiscordUpdate::Details(x) => current_activity + .with_details(CString::new(x).expect("failed to create CString")), + DiscordUpdate::State(x) => current_activity + .with_state(CString::new(x).expect("failed to create CString")), + DiscordUpdate::SmallImg(x) => current_activity.with_small_image_key( + CString::new(x).expect("failed to create CString"), + ), + DiscordUpdate::SmallImgDesc(x) => current_activity + .with_small_image_tooltip( + CString::new(x).expect("failed to create CString"), + ), + DiscordUpdate::LargeImg(x) => current_activity.with_large_image_key( + CString::new(x).expect("failed to create CString"), + ), + DiscordUpdate::LargeImgDesc(x) => current_activity + .with_large_image_tooltip( + CString::new(x).expect("failed to create CString"), + ), + DiscordUpdate::Shutdown => break 'outer, + }; + discord.update_activity(¤t_activity, |_, _| {}); + } + Err(_) => {} + } + } + })), + }) +} + +/* Some helpers */ +pub fn send_menu(disc: &mut MutexGuard) { + disc.tx.send(DiscordUpdate::Details("Menu".into())); + disc.tx.send(DiscordUpdate::State("Idling".into())); + disc.tx.send(DiscordUpdate::LargeImg("bg_main".into())); +} +pub fn send_singleplayer(disc: &mut MutexGuard) { + disc.tx.send(DiscordUpdate::Details("Singleplayer".into())); + disc.tx.send(DiscordUpdate::State("Playing...".into())); +} \ No newline at end of file diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 185dc7d2cd..8bde637e8c 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -1,6 +1,10 @@ #![feature(drain_filter)] #![recursion_limit = "2048"] +#[cfg(feature = "discord")] +#[macro_use] +extern crate lazy_static; + #[macro_use] pub mod ui; pub mod anim; @@ -17,16 +21,21 @@ pub mod settings; pub mod singleplayer; pub mod window; +#[cfg(feature = "discord")] +pub mod discord; + // Reexports pub use crate::error::Error; -use crate::{ - audio::{base::Genre, AudioFrontend}, - menu::main::MainMenuState, - settings::Settings, - window::Window, +use crate::{audio::AudioFrontend, menu::main::MainMenuState, settings::Settings, window::Window}; +use log::{self, debug, error, info, warn}; + +#[cfg(feature = "discord")] +use std::sync::{ + mpsc::Sender, + Mutex, }; -use log::{debug, error, info, warn}; + use simplelog::{CombinedLogger, Config, TermLogger, WriteLogger}; use std::{fs::File, mem, panic, str::FromStr}; @@ -80,6 +89,14 @@ pub trait PlayState { fn name(&self) -> &'static str; } +#[cfg(feature = "discord")] +lazy_static! { + //Set up discord rich presence + static ref discord_instance: Mutex = { + discord::run() + }; +} + fn main() { // Set up the global state. let settings = Settings::load(); @@ -109,6 +126,17 @@ fn main() { ), ]) .unwrap(); + + // Initialize discord. (lazy_static intialise lazily...) + #[cfg(feature = "discord")] + { + match discord_instance.lock() { + Ok(disc) => { + //great + } + Err(e) => {} + } + } // Set up panic handler to relay swish panic messages to the user let settings_clone = global_state.settings.clone(); @@ -238,6 +266,26 @@ fn main() { } } } + + //Properly shutdown discord thread + #[cfg(feature = "discord")] + { + match discord_instance.lock() { + Ok(mut disc) => { + disc.tx.send(discord::DiscordUpdate::Shutdown); + match disc.thread.take() { + Some(th) => { + th.join(); + } + None => { + error!("couldn't gracefully shutdown discord thread"); + } + } + } + Err(e) => error!("couldn't gracefully shutdown discord thread: {}", e), + } + } + // Save settings to add new fields or create the file if it is not already there if let Err(err) = global_state.settings.save_to_file() { warn!("Failed to save settings: {:?}", err);