mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-02 23:42:43 +00:00
[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:
parent
b7e0df34a7
commit
0920b12d6c
151
presence/discord.cpp
Normal file
151
presence/discord.cpp
Normal 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
64
presence/discord.h
Normal 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
116
presence/discord_rpc.h
Normal 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
|
Loading…
Reference in New Issue
Block a user