From b2cda7b7f71288d1408a79bf25909fb97e7bfe47 Mon Sep 17 00:00:00 2001 From: Evan Pratten <ewpratten@gmail.com> Date: Sat, 2 Oct 2021 17:31:17 -0400 Subject: [PATCH] Add discord presence updating --- game/Cargo.toml | 1 + game/src/context.rs | 7 ++- game/src/lib.rs | 40 ++++++++++++++-- game/src/scenes/fsm_error_screen.rs | 17 ++++++- game/src/scenes/ingame_scene/mod.rs | 16 +++++-- game/src/scenes/loading_screen.rs | 14 +++++- game/src/scenes/main_menu_screen.rs | 30 +++++++++--- game/src/scenes/pause_screen.rs | 74 ++++++++++++++++++----------- 8 files changed, 151 insertions(+), 48 deletions(-) diff --git a/game/Cargo.toml b/game/Cargo.toml index 27126cb..d20822c 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -32,6 +32,7 @@ cfg-if = "1.0" num-derive = "0.3" num = "0.4" tiled = { version ="0.9.5", default-features = false } +async-trait = "0.1.51" [dev-dependencies] puffin_viewer = "0.6" diff --git a/game/src/context.rs b/game/src/context.rs index 27fe726..a3e0ac3 100644 --- a/game/src/context.rs +++ b/game/src/context.rs @@ -1,4 +1,6 @@ -use std::cell::RefCell; +use std::{cell::RefCell, sync::mpsc::Sender}; + +use discord_sdk::activity::ActivityBuilder; use crate::{GameConfig, utilities::non_ref_raylib::HackedRaylibHandle}; @@ -6,7 +8,8 @@ use crate::{GameConfig, utilities::non_ref_raylib::HackedRaylibHandle}; #[derive(Debug)] pub struct GameContext { pub renderer: RefCell<HackedRaylibHandle>, - pub config: GameConfig + pub config: GameConfig, + pub discord_rpc_send: Sender<Option<ActivityBuilder>> } // impl GameContext { diff --git a/game/src/lib.rs b/game/src/lib.rs index ca21477..d1ab32c 100644 --- a/game/src/lib.rs +++ b/game/src/lib.rs @@ -1,6 +1,7 @@ #![feature(derive_default_enum)] #![feature(custom_inner_attributes)] #![feature(stmt_expr_attributes)] +#![feature(async_await)] #![feature(c_variadic)] #![deny(unsafe_code)] #![warn( @@ -69,7 +70,7 @@ )] #![clippy::msrv = "1.57.0"] -use std::cell::RefCell; +use std::{cell::RefCell, sync::mpsc::TryRecvError}; use discord_sdk::activity::ActivityBuilder; use raylib::prelude::*; @@ -97,6 +98,8 @@ extern crate serde; extern crate approx; #[macro_use] extern crate num_derive; +#[macro_use] +extern crate async_trait; mod context; mod discord_rpc; @@ -133,11 +136,14 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std: }; maybe_set_discord_presence( &discord_rpc, - ActivityBuilder::default().details("Testing..."), + ActivityBuilder::default().details("Game starting"), ) .await .unwrap(); + // Build an MPSC for the game to send rich presence data to discord + let (send_discord_rpc, recv_discord_rpc) = std::sync::mpsc::channel(); + let context; let raylib_thread; { @@ -161,6 +167,7 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std: context = Box::new(GameContext { renderer: RefCell::new(rl.into()), config: game_config.clone(), + discord_rpc_send: send_discord_rpc, }); } @@ -198,7 +205,6 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std: &raylib_thread, )?; - while !context.renderer.borrow().window_should_close() { // Profile the main game loop puffin::profile_scope!("main_loop"); @@ -212,13 +218,21 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std: // If in dev mode, allow a debug key #[cfg(debug_assertions)] { - if context.renderer.borrow().is_key_pressed(KeyboardKey::KEY_F3) { + if context + .renderer + .borrow() + .is_key_pressed(KeyboardKey::KEY_F3) + { game_config.debug_view = !game_config.debug_view; } } // Handle fullscreen shortcut - if context.renderer.borrow().is_key_pressed(KeyboardKey::KEY_F11) { + if context + .renderer + .borrow() + .is_key_pressed(KeyboardKey::KEY_F11) + { context.renderer.borrow_mut().toggle_fullscreen(); } @@ -273,6 +287,22 @@ pub async fn game_begin(game_config: &mut GameConfig) -> Result<(), Box<dyn std: unsafe { raylib::ffi::EndDrawing(); } + + // Try to update discord + match recv_discord_rpc.try_recv() { + Ok(activity) => { + if let Some(activity) = activity { + if let Err(e) = maybe_set_discord_presence(&discord_rpc, activity).await { + error!("Failed to update discord presence: {:?}", e); + } + } + } + Err(TryRecvError::Empty) => {} + Err(TryRecvError::Disconnected) => { + error!("Discord RPC channel disconnected"); + continue; + } + } } Ok(()) } diff --git a/game/src/scenes/fsm_error_screen.rs b/game/src/scenes/fsm_error_screen.rs index 7a57148..d1fee04 100644 --- a/game/src/scenes/fsm_error_screen.rs +++ b/game/src/scenes/fsm_error_screen.rs @@ -1,6 +1,7 @@ use dirty_fsm::{Action, ActionFlag}; +use discord_sdk::activity::{ActivityBuilder, Assets}; use raylib::{color::Color, prelude::RaylibDraw}; -use tracing::{debug, trace}; +use tracing::{debug, error, trace}; use crate::{ context::GameContext, @@ -26,8 +27,20 @@ impl Action<Scenes, ScreenError, GameContext> for FsmErrorScreen { Ok(()) } - fn on_first_run(&mut self, _context: &GameContext) -> Result<(), ScreenError> { + fn on_first_run(&mut self, context: &GameContext) -> Result<(), ScreenError> { debug!("Running FsmErrorScreen for the first time"); + + // Update discord + if let Err(e) = context.discord_rpc_send.send(Some( + ActivityBuilder::default() + .details("IT FUCKING DIED") + .assets( + Assets::default().large("game-logo-small", Some(context.config.name.clone())), + ), + )) { + error!("Failed to update discord: {}", e); + } + Ok(()) } diff --git a/game/src/scenes/ingame_scene/mod.rs b/game/src/scenes/ingame_scene/mod.rs index 1509484..bfe505e 100644 --- a/game/src/scenes/ingame_scene/mod.rs +++ b/game/src/scenes/ingame_scene/mod.rs @@ -1,4 +1,5 @@ use dirty_fsm::{Action, ActionFlag}; +use discord_sdk::activity::{ActivityBuilder, Assets}; use raylib::prelude::*; use crate::{ @@ -13,7 +14,7 @@ use crate::{ use self::level::Level; use super::{Scenes, ScreenError}; -use tracing::{debug, trace}; +use tracing::{debug, error, trace}; mod hud; pub mod level; @@ -57,7 +58,7 @@ impl Action<Scenes, ScreenError, GameContext> for InGameScreen { Ok(()) } - fn on_first_run(&mut self, _context: &GameContext) -> Result<(), ScreenError> { + fn on_first_run(&mut self, context: &GameContext) -> Result<(), ScreenError> { debug!("Running InGameScreen for the first time"); // Set the player to running @@ -68,6 +69,15 @@ impl Action<Scenes, ScreenError, GameContext> for InGameScreen { -cur_level.platform_tex.height as f32, ); + // Update discord + if let Err(e) = context.discord_rpc_send.send(Some( + ActivityBuilder::default().details("in game").assets( + Assets::default().large("game-logo-small", Some(context.config.name.clone())), + ), + )) { + error!("Failed to update discord: {}", e); + } + Ok(()) } @@ -105,8 +115,6 @@ impl Action<Scenes, ScreenError, GameContext> for InGameScreen { } else { Ok(ActionFlag::Continue) } - - } fn on_finish(&mut self, _interrupted: bool) -> Result<(), ScreenError> { diff --git a/game/src/scenes/loading_screen.rs b/game/src/scenes/loading_screen.rs index 0ccc70b..01b31be 100644 --- a/game/src/scenes/loading_screen.rs +++ b/game/src/scenes/loading_screen.rs @@ -3,6 +3,7 @@ use std::ops::{Div, Sub}; use cfg_if::cfg_if; use chrono::{DateTime, Utc}; use dirty_fsm::{Action, ActionFlag}; +use discord_sdk::activity::{ActivityBuilder, Assets}; use raylib::prelude::*; use crate::{GameConfig, context::GameContext, utilities::{ @@ -13,7 +14,7 @@ use crate::{GameConfig, context::GameContext, utilities::{ }}; use super::{Scenes, ScreenError}; -use tracing::{debug, info, trace}; +use tracing::{debug, info, error, trace}; /// Defines how long the loading screen should be displayed. const LOADING_SCREEN_DURATION_SECONDS: u8 = 3; @@ -49,9 +50,18 @@ impl Action<Scenes, ScreenError, GameContext> for LoadingScreen { Ok(()) } - fn on_first_run(&mut self, _context: &GameContext) -> Result<(), ScreenError> { + fn on_first_run(&mut self, context: &GameContext) -> Result<(), ScreenError> { debug!("Running LoadingScreen for the first time"); + // Update discord + if let Err(e) = context.discord_rpc_send.send(Some( + ActivityBuilder::default().details("loading...").assets( + Assets::default().large("game-logo-small", Some(context.config.name.clone())), + ), + )) { + error!("Failed to update discord: {}", e); + } + // Keep track of when this screen is opened self.start_timestamp = Some(Utc::now()); diff --git a/game/src/scenes/main_menu_screen.rs b/game/src/scenes/main_menu_screen.rs index cc0f68b..13c8295 100644 --- a/game/src/scenes/main_menu_screen.rs +++ b/game/src/scenes/main_menu_screen.rs @@ -2,19 +2,24 @@ use std::ops::{Div, Sub}; use chrono::{DateTime, Utc}; use dirty_fsm::{Action, ActionFlag}; +use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{GameConfig, context::GameContext, utilities::{ +use crate::{ + context::GameContext, + utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }}; + }, + GameConfig, +}; use super::{Scenes, ScreenError}; -use tracing::{debug, info, trace}; +use tracing::{debug, error, info, trace}; #[derive(Debug)] pub struct MainMenuScreen {} @@ -32,9 +37,18 @@ impl Action<Scenes, ScreenError, GameContext> for MainMenuScreen { Ok(()) } - fn on_first_run(&mut self, _context: &GameContext) -> Result<(), ScreenError> { + fn on_first_run(&mut self, context: &GameContext) -> Result<(), ScreenError> { debug!("Running MainMenuScreen for the first time"); + // Update discord + if let Err(e) = context.discord_rpc_send.send(Some( + ActivityBuilder::default().details("main menu").assets( + Assets::default().large("game-logo-small", Some(context.config.name.clone())), + ), + )) { + error!("Failed to update discord: {}", e); + } + Ok(()) } @@ -47,7 +61,11 @@ impl Action<Scenes, ScreenError, GameContext> for MainMenuScreen { self.render_screen_space(&mut context.renderer.borrow_mut(), &context.config); // TODO: TEMP - if context.renderer.borrow_mut().is_key_pressed(KeyboardKey::KEY_SPACE) { + if context + .renderer + .borrow_mut() + .is_key_pressed(KeyboardKey::KEY_SPACE) + { Ok(ActionFlag::SwitchState(Scenes::InGameScene)) } else { Ok(ActionFlag::Continue) @@ -64,7 +82,7 @@ impl ScreenSpaceRender for MainMenuScreen { fn render_screen_space( &self, raylib: &mut crate::utilities::non_ref_raylib::HackedRaylibHandle, - config: &GameConfig + config: &GameConfig, ) { // Render the background raylib.clear_background(Color::BLACK); diff --git a/game/src/scenes/pause_screen.rs b/game/src/scenes/pause_screen.rs index e960b71..a496c5a 100644 --- a/game/src/scenes/pause_screen.rs +++ b/game/src/scenes/pause_screen.rs @@ -2,19 +2,24 @@ use std::ops::{Div, Sub}; use chrono::{DateTime, Utc}; use dirty_fsm::{Action, ActionFlag}; +use discord_sdk::activity::{ActivityBuilder, Assets}; use pkg_version::pkg_version_major; use raylib::prelude::*; -use crate::{GameConfig, context::GameContext, utilities::{ +use crate::{ + context::GameContext, + utilities::{ datastore::{load_texture_from_internal_data, ResourceLoadError}, game_version::get_version_string, math::interpolate_exp, non_ref_raylib::HackedRaylibHandle, render_layer::ScreenSpaceRender, - }}; + }, + GameConfig, +}; use super::{Scenes, ScreenError}; -use tracing::{debug, info, trace}; +use tracing::{debug, error, info, trace}; #[derive(Debug)] pub struct PauseScreen {} @@ -32,9 +37,18 @@ impl Action<Scenes, ScreenError, GameContext> for PauseScreen { Ok(()) } - fn on_first_run(&mut self, _context: &GameContext) -> Result<(), ScreenError> { + fn on_first_run(&mut self, context: &GameContext) -> Result<(), ScreenError> { debug!("Running PauseScreen for the first time"); + // Update discord + if let Err(e) = context.discord_rpc_send.send(Some( + ActivityBuilder::default().details("paused").assets( + Assets::default().large("game-logo-small", Some(context.config.name.clone())), + ), + )) { + error!("Failed to update discord: {}", e); + } + Ok(()) } @@ -49,26 +63,35 @@ impl Action<Scenes, ScreenError, GameContext> for PauseScreen { //Mouse Position let mouse_position: Vector2 = context.renderer.borrow_mut().get_mouse_position(); //Mouse Input - let is_left_click = context.renderer.borrow_mut().is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON); - + let is_left_click = context + .renderer + .borrow_mut() + .is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON); //"Hitboxes" for the resume and Main menu buttons - if is_left_click && Rectangle::new(322.0,321.0,435.0,80.0).check_collision_point_rec(mouse_position) { + if is_left_click + && Rectangle::new(322.0, 321.0, 435.0, 80.0).check_collision_point_rec(mouse_position) + { return Ok(ActionFlag::SwitchState(Scenes::InGameScene)); } - if is_left_click && Rectangle::new(390.0,464.0,200.0,50.0).check_collision_point_rec(mouse_position) { + if is_left_click + && Rectangle::new(390.0, 464.0, 200.0, 50.0).check_collision_point_rec(mouse_position) + { return Ok(ActionFlag::SwitchState(Scenes::MainMenuScreen)); } - if context.renderer.borrow_mut().is_key_pressed(KeyboardKey::KEY_ESCAPE) { + if context + .renderer + .borrow_mut() + .is_key_pressed(KeyboardKey::KEY_ESCAPE) + { Ok(ActionFlag::SwitchState(Scenes::InGameScene)) } else { Ok(ActionFlag::Continue) } } - fn on_finish(&mut self, _interrupted: bool) -> Result<(), ScreenError> { debug!("Finished PauseScreen"); Ok(()) @@ -76,11 +99,10 @@ impl Action<Scenes, ScreenError, GameContext> for PauseScreen { } impl ScreenSpaceRender for PauseScreen { - fn render_screen_space( &self, raylib: &mut crate::utilities::non_ref_raylib::HackedRaylibHandle, - config: &GameConfig + config: &GameConfig, ) { let screen_size = raylib.get_screen_size(); @@ -98,85 +120,83 @@ impl ScreenSpaceRender for PauseScreen { (screen_size.x as i32 / 2) - 223, (screen_size.y as i32 / 2) - 40, 120, - Color::RED + Color::RED, ); raylib.draw_text( "Paused", (screen_size.x as i32 / 2) - 217, (screen_size.y as i32 / 2) - 40, 120, - Color::BLUE + Color::BLUE, ); raylib.draw_text( "Paused", (screen_size.x as i32 / 2) - 220, (screen_size.y as i32 / 2) - 40, 120, - Color::WHITE + Color::WHITE, ); raylib.draw_text( "Click To Resume", (screen_size.x as i32 / 2) - 80, (screen_size.y as i32 / 2) + 60, 20, - Color::RED + Color::RED, ); raylib.draw_text( "Click To Resume", (screen_size.x as i32 / 2) - 80, (screen_size.y as i32 / 2) + 60, 20, - Color::BLUE + Color::BLUE, ); raylib.draw_text( "Click To Resume", (screen_size.x as i32 / 2) - 80, (screen_size.y as i32 / 2) + 60, 20, - Color::WHITE + Color::WHITE, ); raylib.draw_text( "Main Menu", (screen_size.x as i32 / 2) - 123, (screen_size.y as i32 / 2) + 100, 50, - Color::RED + Color::RED, ); raylib.draw_text( "Main Menu", (screen_size.x as i32 / 2) - 117, (screen_size.y as i32 / 2) + 100, 50, - Color::BLUE + Color::BLUE, ); raylib.draw_text( "Main Menu", (screen_size.x as i32 / 2) - 120, (screen_size.y as i32 / 2) + 100, 50, - Color::WHITE + Color::WHITE, ); - if Rectangle::new(390.0,464.0,200.0,50.0).check_collision_point_rec(mouse_position) { + if Rectangle::new(390.0, 464.0, 200.0, 50.0).check_collision_point_rec(mouse_position) { raylib.draw_text( "Main Menu", (screen_size.x as i32 / 2) - 120, (screen_size.y as i32 / 2) + 100, 50, - Color::YELLOW + Color::YELLOW, ); } - if Rectangle::new(322.0,321.0,435.0,80.0).check_collision_point_rec(mouse_position) { + if Rectangle::new(322.0, 321.0, 435.0, 80.0).check_collision_point_rec(mouse_position) { raylib.draw_text( "Paused", (screen_size.x as i32 / 2) - 220, (screen_size.y as i32 / 2) - 40, 120, - Color::DARKBLUE + Color::DARKBLUE, ); } - - } }