diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 5a871ec041..49bf596571 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -131,6 +131,21 @@ void Host_UpdateTitle(const std::string& title) __android_log_write(ANDROID_LOG_INFO, DOLPHIN_TAG, title.c_str()); } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +} + +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ + return false; +} + void Host_UpdateDisasmDialog() { } diff --git a/Source/Core/Core/Host.h b/Source/Core/Core/Host.h index dad850a5b2..9d360d7ec4 100644 --- a/Source/Core/Core/Host.h +++ b/Source/Core/Core/Host.h @@ -64,4 +64,14 @@ void Host_UpdateTitle(const std::string& title); void Host_YieldToUI(); void Host_TitleChanged(); +void Host_UpdateDiscordClientID(const std::string& client_id = {}); +bool Host_UpdateDiscordPresenceRaw(const std::string& details = {}, const std::string& state = {}, + const std::string& large_image_key = {}, + const std::string& large_image_text = {}, + const std::string& small_image_key = {}, + const std::string& small_image_text = {}, + const int64_t start_timestamp = 0, + const int64_t end_timestamp = 0, const int party_size = 0, + const int party_max = 0); + std::unique_ptr Host_CreateGBAHost(std::weak_ptr core); diff --git a/Source/Core/Core/IOS/DolphinDevice.cpp b/Source/Core/Core/IOS/DolphinDevice.cpp index db399c97a9..a9074b3d11 100644 --- a/Source/Core/Core/IOS/DolphinDevice.cpp +++ b/Source/Core/Core/IOS/DolphinDevice.cpp @@ -15,8 +15,10 @@ #include "Common/Timer.h" #include "Common/Version.h" #include "Core/Config/MainSettings.h" +#include "Core/Config/UISettings.h" #include "Core/Core.h" #include "Core/HW/Memmap.h" +#include "Core/Host.h" namespace IOS::HLE { @@ -30,6 +32,9 @@ enum IOCTL_DOLPHIN_SET_SPEED_LIMIT = 0x04, IOCTL_DOLPHIN_GET_CPU_SPEED = 0x05, IOCTL_DOLPHIN_GET_REAL_PRODUCTCODE = 0x06, + IOCTL_DOLPHIN_DISCORD_SET_CLIENT = 0x07, + IOCTL_DOLPHIN_DISCORD_SET_PRESENCE = 0x08, + IOCTL_DOLPHIN_DISCORD_RESET = 0x09 }; @@ -157,6 +162,67 @@ IPCReply GetRealProductCode(const IOCtlVRequest& request) return IPCReply(IPC_SUCCESS); } +IPCReply SetDiscordClient(const IOCtlVRequest& request) +{ + if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) + return IPCReply(IPC_EACCES); + + if (!request.HasNumberOfValidVectors(1, 0)) + return IPCReply(IPC_EINVAL); + + std::string new_client_id = + Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); + + Host_UpdateDiscordClientID(new_client_id); + + return IPCReply(IPC_SUCCESS); +} + +IPCReply SetDiscordPresence(const IOCtlVRequest& request) +{ + if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) + return IPCReply(IPC_EACCES); + + if (!request.HasNumberOfValidVectors(10, 0)) + return IPCReply(IPC_EINVAL); + + std::string details = + Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); + std::string state = Memory::GetString(request.in_vectors[1].address, request.in_vectors[1].size); + std::string large_image_key = + Memory::GetString(request.in_vectors[2].address, request.in_vectors[2].size); + std::string large_image_text = + Memory::GetString(request.in_vectors[3].address, request.in_vectors[3].size); + std::string small_image_key = + Memory::GetString(request.in_vectors[4].address, request.in_vectors[4].size); + std::string small_image_text = + Memory::GetString(request.in_vectors[5].address, request.in_vectors[5].size); + + int64_t start_timestamp = Memory::Read_U64(request.in_vectors[6].address); + int64_t end_timestamp = Memory::Read_U64(request.in_vectors[7].address); + int party_size = Memory::Read_U32(request.in_vectors[8].address); + int party_max = Memory::Read_U32(request.in_vectors[9].address); + + bool ret = Host_UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text, + small_image_key, small_image_text, start_timestamp, + end_timestamp, party_size, party_max); + + if (!ret) + return IPCReply(IPC_EACCES); + + return IPCReply(IPC_SUCCESS); +} + +IPCReply ResetDiscord(const IOCtlVRequest& request) +{ + if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) + return IPCReply(IPC_EACCES); + + Host_UpdateDiscordClientID(); + + return IPCReply(IPC_SUCCESS); +} + } // namespace std::optional DolphinDevice::IOCtlV(const IOCtlVRequest& request) @@ -178,6 +244,13 @@ std::optional DolphinDevice::IOCtlV(const IOCtlVRequest& request) return GetCPUSpeed(request); case IOCTL_DOLPHIN_GET_REAL_PRODUCTCODE: return GetRealProductCode(request); + case IOCTL_DOLPHIN_DISCORD_SET_CLIENT: + return SetDiscordClient(request); + case IOCTL_DOLPHIN_DISCORD_SET_PRESENCE: + return SetDiscordPresence(request); + case IOCTL_DOLPHIN_DISCORD_RESET: + return ResetDiscord(request); + default: return IPCReply(IPC_EINVAL); } diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 03ea30ec17..11bbf55da1 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -122,6 +122,30 @@ void Host_TitleChanged() #endif } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +#ifdef USE_DISCORD_PRESENCE + Discord::UpdateClientID(client_id); +#endif +} + +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ +#ifdef USE_DISCORD_PRESENCE + return Discord::UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text, + small_image_key, small_image_text, start_timestamp, + end_timestamp, party_size, party_max); +#else + return false; +#endif +} + std::unique_ptr Host_CreateGBAHost(std::weak_ptr core) { return nullptr; diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index 876ab25123..5ccd10189c 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -287,6 +287,30 @@ void Host_TitleChanged() #endif } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +#ifdef USE_DISCORD_PRESENCE + Discord::UpdateClientID(client_id); +#endif +} + +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ +#ifdef USE_DISCORD_PRESENCE + return Discord::UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text, + small_image_key, small_image_text, start_timestamp, + end_timestamp, party_size, party_max); +#else + return false; +#endif +} + #ifndef HAS_LIBMGBA std::unique_ptr Host_CreateGBAHost(std::weak_ptr core) { diff --git a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp index 7dc1cfaa60..4034df5ae0 100644 --- a/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp +++ b/Source/Core/DolphinTool/ToolHeadlessPlatform.cpp @@ -43,6 +43,21 @@ void Host_UpdateTitle(const std::string& title) { } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +} + +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ + return false; +} + void Host_UpdateDisasmDialog() { } diff --git a/Source/Core/UICommon/DiscordPresence.cpp b/Source/Core/UICommon/DiscordPresence.cpp index 63036370ef..f269c27c0a 100644 --- a/Source/Core/UICommon/DiscordPresence.cpp +++ b/Source/Core/UICommon/DiscordPresence.cpp @@ -189,12 +189,27 @@ void Init() handlers.ready = HandleDiscordReady; handlers.joinRequest = HandleDiscordJoinRequest; handlers.joinGame = HandleDiscordJoin; - // The number is the client ID for Dolphin, it's used for images and the application name - Discord_Initialize("455712169795780630", &handlers, 1, nullptr); + Discord_Initialize(DEFAULT_CLIENT_ID.c_str(), &handlers, 1, nullptr); UpdateDiscordPresence(); #endif } +void UpdateClientID(const std::string& new_client) +{ +#ifdef USE_DISCORD_PRESENCE + if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) + return; + + s_using_custom_client = new_client.empty() || new_client.compare(DEFAULT_CLIENT_ID) != 0; + + Shutdown(); + if (s_using_custom_client) + Discord_Initialize(new_client.c_str(), nullptr, 0, nullptr); + else // if initialising dolphin's client ID, make sure to restore event handlers + Init(); +#endif +} + void CallPendingCallbacks() { #ifdef USE_DISCORD_PRESENCE @@ -213,6 +228,39 @@ void InitNetPlayFunctionality(Handler& handler) #endif } +bool UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, const int64_t start_timestamp, + const int64_t end_timestamp, const int party_size, + const int party_max) +{ +#ifdef USE_DISCORD_PRESENCE + if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) + return false; + + // only /dev/dolphin sets this, don't let homebrew change official client ID raw presence + if (!s_using_custom_client) + return false; + + DiscordRichPresence discord_presence = {}; + discord_presence.details = details.c_str(); + discord_presence.state = state.c_str(); + discord_presence.largeImageKey = large_image_key.c_str(); + discord_presence.largeImageText = large_image_text.c_str(); + discord_presence.smallImageKey = small_image_key.c_str(); + discord_presence.smallImageText = small_image_text.c_str(); + discord_presence.startTimestamp = start_timestamp; + discord_presence.endTimestamp = end_timestamp; + discord_presence.partySize = party_size; + discord_presence.partyMax = party_max; + Discord_UpdatePresence(&discord_presence); + + return true; +#endif +} + void UpdateDiscordPresence(int party_size, SecretType type, const std::string& secret, const std::string& current_game) { @@ -220,6 +268,10 @@ void UpdateDiscordPresence(int party_size, SecretType type, const std::string& s if (!Config::Get(Config::MAIN_USE_DISCORD_PRESENCE)) return; + // reset the client ID if running homebrew has changed it + if (s_using_custom_client) + UpdateClientID(DEFAULT_CLIENT_ID); + const std::string& title = current_game.empty() ? SConfig::GetInstance().GetTitleDescription() : current_game; std::string game_artwork = ArtworkForGameId(SConfig::GetInstance().GetGameID()); diff --git a/Source/Core/UICommon/DiscordPresence.h b/Source/Core/UICommon/DiscordPresence.h index 7e66c3cb32..c8381fcea7 100644 --- a/Source/Core/UICommon/DiscordPresence.h +++ b/Source/Core/UICommon/DiscordPresence.h @@ -8,6 +8,9 @@ namespace Discord { +// The number is the client ID for Dolphin, it's used for images and the application name +const std::string DEFAULT_CLIENT_ID = "455712169795780630"; + class Handler { public: @@ -24,9 +27,19 @@ enum class SecretType RoomID, }; +static bool s_using_custom_client = false; + void Init(); void InitNetPlayFunctionality(Handler& handler); void CallPendingCallbacks(); +void UpdateClientID(const std::string& new_client = {}); +bool UpdateDiscordPresenceRaw(const std::string& details = {}, const std::string& state = {}, + const std::string& large_image_key = {}, + const std::string& large_image_text = {}, + const std::string& small_image_key = {}, + const std::string& small_image_text = {}, + const int64_t start_timestamp = 0, const int64_t end_timestamp = 0, + const int party_size = 0, const int party_max = 0); void UpdateDiscordPresence(int party_size = 0, SecretType type = SecretType::Empty, const std::string& secret = {}, const std::string& current_game = {}); std::string CreateSecretFromIPAddress(const std::string& ip_address, int port); diff --git a/Source/DSPTool/StubHost.cpp b/Source/DSPTool/StubHost.cpp index de20be198f..50a27e2d28 100644 --- a/Source/DSPTool/StubHost.cpp +++ b/Source/DSPTool/StubHost.cpp @@ -25,6 +25,19 @@ void Host_Message(HostMessageID) void Host_UpdateTitle(const std::string&) { } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +} +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ + return false; +} void Host_UpdateDisasmDialog() { } diff --git a/Source/UnitTests/StubHost.cpp b/Source/UnitTests/StubHost.cpp index 233af18f79..cf808bcce1 100644 --- a/Source/UnitTests/StubHost.cpp +++ b/Source/UnitTests/StubHost.cpp @@ -25,6 +25,19 @@ void Host_Message(HostMessageID) void Host_UpdateTitle(const std::string&) { } +void Host_UpdateDiscordClientID(const std::string& client_id) +{ +} +bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state, + const std::string& large_image_key, + const std::string& large_image_text, + const std::string& small_image_key, + const std::string& small_image_text, + const int64_t start_timestamp, const int64_t end_timestamp, + const int party_size, const int party_max) +{ + return false; +} void Host_UpdateDisasmDialog() { }