[09/04/2024] Added Discord Presence Integration. Discord displays "In ShadPS4" along with elapsed time when a user is executing shadps4.exe in their computer.

This commit is contained in:
Kevin Mora 2024-09-04 11:54:52 -07:00
parent b7e0df34a7
commit 0920b12d6c
3 changed files with 331 additions and 0 deletions

151
presence/discord.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "discord.h"
#include "discord_rpc.h"
#include <iostream>
#include <chrono>
#include <thread>
#include <filesystem>
#include <fstream>
/**
* @brief Constructor for the Discord class.
* @author Kevin Mora (morkev)
*
* Initializes the Discord class with the provided launch code (Discord application ID)
* and sets the application_running_ flag to false.
*
* @param launch_code The Discord application client ID.
*/
Discord::Discord(const std::string &launch_code)
: launch_code_(launch_code), application_running_(false), time_of_start_(0) {}
/**
* @brief Initializes the Discord RPC connection.
*
* Sets up the event handlers for Discord Rich Presence and connects to Discord
* using the provided launch code.
*/
void Discord::Initialize() {
DiscordEventHandlers handlers{};
Discord_Initialize(launch_code_.c_str(), &handlers, 1, nullptr);
}
/**
* @brief Shuts down the Discord RPC connection.
*
* Terminates the connection to Discord and cleans up any active presence.
*/
void Discord::Shutdown() {
Discord_Shutdown();
}
/**
* @brief Checks if a process with the given name is running on the system.
*
* This function iterates over all running processes to check if a process
* named `process_name` (e.g., `shadps4.exe`) is running.
*
* @param process_name The name of the process to check for.
* @return true if the process is running, false otherwise.
*/
bool Discord::IsProcessRunning(const std::string &process_name) {
for (const auto &entry : std::filesystem::directory_iterator("/proc")) {
try {
std::string pid = entry.path().filename();
std::string cmd_path = "/proc/" + pid + "/comm";
std::ifstream cmd_file(cmd_path);
std::string cmd;
std::getline(cmd_file, cmd);
if (cmd == process_name) {
return true;
}
} catch (...) {
continue;
}
}
return false;
}
/**
* @brief Retrieves the current presence data for Discord.
*
* Determines the state, large image, and large image text based on whether
* `shadps4.exe` is running. If the process is found, it updates the status
* to indicate the application is running.
*
* @param state Reference to the string where the current state will be stored (e.g., "In ShadPS4").
* @param large_image Reference to the string where the large image key will be stored.
* @param large_text Reference to the string where the large image's tooltip text will be stored.
*/
void Discord::GetData(std::string &state, std::string &large_image, std::string &large_text) {
if (IsProcessRunning("shadps4.exe")) {
state = "In ShadPS4";
large_image = "shadps4";
large_text = "ShadPS4";
} else {
state = "Unknown";
large_image = "default";
large_text = "Unknown";
}
}
/**
* @brief Updates the Discord Rich Presence with the latest status.
*
* If the application `shadps4.exe` is running, this method updates the start time
* and sends the current state, large image, and text to Discord.
* If the application is closed, it clears the presence from Discord.
*/
void Discord::UpdatePresence() {
std::string state, large_image, large_text;
GetData(state, large_image, large_text);
DiscordRichPresence presence{};
presence.state = state.c_str();
presence.large_image_key = large_image.c_str();
presence.large_image_text = large_text.c_str();
presence.start_timestamp = time_of_start_;
if (state != "Unknown") {
Discord_UpdatePresence(&presence);
std::cout << "Discord Rich Presence updated.\n";
} else {
Discord_ClearPresence();
std::cout << "Application closed. Presence cleared.\n";
}
}
/**
* @brief Runs the main loop for updating Discord Rich Presence.
*
* Continuously checks if the `shadps4.exe` process is running and updates the Discord
* status accordingly. This function handles the timing of updates and stops the timer
* when the application is closed.
*/
void Discord::Run() {
Initialize();
while (true) {
try {
std::string state, large_image, large_text;
GetData(state, large_image, large_text);
if (state != "Unknown") {
if (!application_running_) {
application_running_ = true;
time_of_start_ = std::time(nullptr); // Set start time when the application starts
}
UpdatePresence();
std::this_thread::sleep_for(std::chrono::seconds(10)); // Check every 10 seconds
} else {
if (application_running_) {
application_running_ = false;
std::cout << "Application stopped.\n";
}
std::this_thread::sleep_for(std::chrono::seconds(1)); // Faster check if app is not running
}
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << ". Retrying...\n";
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
Shutdown();
}

64
presence/discord.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef DISCORD_H
#define DISCORD_H
#include <string>
#include "discord_rpc.h"
/**
* @class Discord
* @brief Manages Discord Rich Presence for the shadPS4 Emulator.
* @author Kevin Mora (morkev)
*
* The Discord class is responsible for managing Discord Rich Presence updates,
* detecting if the `shadps4.exe` process is running, and updating Discord status accordingly.
* It also handles connecting and disconnecting from the Discord RPC service.
*
* @param launch_code_ Discord application client ID.
* @param application_running_ Tracks if the application (`shadps4.exe`) is running.
* @param time_of_start_ Tracks the time when the application starts for presence timestamping.
*/
class Discord {
private:
std::string launch_code_;
bool application_running_;
time_t time_of_start_;
/**
* @brief Checks if a specific process is running on the system.
*
* This method searches for a running process by its name.
*
* @param process_name The name of the process to check (e.g., `shadps4.exe`).
* @return true if the process is running, false otherwise.
*/
bool IsProcessRunning(const std::string &process_name);
/**
* @brief Retrieves the current status and presence data for Discord.
*
* This method sets the status, large image, and text details based on whether
* `shadps4.exe` is currently running.
*
* @param state Output parameter for the current state (e.g., "In ShadPS4").
* @param large_image Output parameter for the large image to display (e.g., "shadps4").
* @param large_text Output parameter for the large image's text (e.g., "ShadPS4").
*/
void GetData(std::string &state, std::string &large_image, std::string &large_text);
public:
/**
* @brief Constructor for the Discord class.
*
* Initializes the Discord class with the specified launch code for Discord RPC.
*
* @param launch_code Discord application client ID.
*/
explicit Discord(const std::string &launch_code);
void UpdatePresence();
void Run();
void Initialize();
void Shutdown();
};
#endif

116
presence/discord_rpc.h Normal file
View File

@ -0,0 +1,116 @@
#ifndef DISCORD_RPC_H
#define DISCORD_RPC_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @struct DiscordRichPresence
* @brief Struct representing Discord Rich Presence data.
* @autor Kevin Mora (morkev)
*
* This struct holds various fields that define the Discord Rich Presence status,
* including the state, timestamps, images, party information, and secrets for joining/spectating games.
*
* @param state The main status text for the Rich Presence (e.g., "In ShadPS4").
* @param details Additional details for the Rich Presence.
* @param start_timestamp The start time for the current activity (Unix timestamp).
* @param end_timestamp The end time for the current activity (Unix timestamp).
* @param large_image_key The key for the large image shown in Discord.
* @param large_image_text The tooltip text for the large image.
* @param small_image_key The key for the small image shown in Discord.
* @param small_image_text The tooltip text for the small image.
* @param party_id Unique identifier for the player's party.
* @param party_size The number of people in the party.
* @param party_max The maximum size of the party.
* @param match_secret Secret for joining a multiplayer match.
* @param join_secret Secret for joining a friend's game.
* @param spectate_secret Secret for spectating a friend's game.
* @param instance Whether or not the game is an instance.
*/
typedef struct DiscordRichPresence {
const char* state;
const char* details;
int64_t start_timestamp;
int64_t end_timestamp;
const char* large_image_key;
const char* large_image_text;
const char* small_image_key;
const char* small_image_text;
const char* party_id;
int party_size;
int party_max;
const char* match_secret;
const char* join_secret;
const char* spectate_secret;
int8_t instance;
} DiscordRichPresence;
/**
* @struct DiscordEventHandlers
* @brief Struct representing event handlers for Discord Rich Presence.
*
* This struct defines function pointers for various Discord RPC events,
* such as when the presence is ready, when the connection is disconnected,
* and when there are errors or game join requests.
*
* @param ready Callback function when Discord RPC is ready.
* @param disconnected Callback function when Discord RPC is disconnected.
* @param errored Callback function for handling errors.
* @param join_game Callback function for when a join game request is received.
* @param spectate_game Callback function for when a spectate game request is received.
* @param join_request Callback function for handling a join request.
*/
typedef struct DiscordEventHandlers {
void (*ready)(void);
void (*disconnected)(int errcode, const char* message);
void (*errored)(int errcode, const char* message);
void (*join_game)(const char* join_secret);
void (*spectate_game)(const char* spectate_secret);
void (*join_request)(const char* join_request);
} DiscordEventHandlers;
/**
* @brief Initializes the Discord Rich Presence.
*
* This function sets up the Discord Rich Presence with the provided application ID,
* event handlers, and optional Steam ID.
*
* @param application_id The Discord application ID.
* @param handlers Pointer to the Discord event handlers.
* @param auto_register Whether or not to auto-register the presence.
* @param optional_steam_id Optional Steam ID for Steam integration.
*/
void Discord_Initialize(
const char* application_id, DiscordEventHandlers* handlers,
int auto_register, const char* optional_steam_id);
/**
* @brief Shuts down the Discord Rich Presence.
*
* This function disconnects the Rich Presence from Discord and shuts it down.
*/
void Discord_Shutdown(void);
/**
* @brief Updates the Discord Rich Presence with new information.
*
* This function updates the current Rich Presence data (e.g., state, timestamps, images).
*
* @param presence Pointer to a `DiscordRichPresence` struct containing the new data.
*/
void Discord_UpdatePresence(const DiscordRichPresence* presence);
/**
* @brief Clears the current Discord Rich Presence.
*
* This function removes any active Rich Presence information from Discord.
*/
void Discord_ClearPresence(void);
#ifdef __cplusplus
}
#endif
#endif