diff --git a/.gitmodules b/.gitmodules index 63897dc..7a4238a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "discord-rpc"] path = discord-rpc - url = https://github.com/discord/discord-rpc.git + url = https://github.com/multitheftauto/discord-rpc.git diff --git a/DiscordEuroscope/ConfigData.cpp b/DiscordEuroscope/ConfigData.cpp index c95ce3e..64bd9be 100644 --- a/DiscordEuroscope/ConfigData.cpp +++ b/DiscordEuroscope/ConfigData.cpp @@ -32,6 +32,8 @@ namespace DiscordEuroScope_Configuration this->discord_presence_small_image_key = ""; this->sweatbox_bypass = false; this->loaded_from_ese = false; + this->buttons[0] = Button(); + this->buttons[1] = Button(); this->RadioCallsigns.clear(); for (int i = 0; i < 7; i++) { @@ -40,6 +42,8 @@ namespace DiscordEuroScope_Configuration this->states[i].presence_large_image_text = ""; this->states[i].presence_small_image_key = ""; this->states[i].presence_small_image_text = ""; + this->states[i].buttons[0] = Button(); + this->states[i].buttons[1] = Button(); this->states[i].used = false; } } diff --git a/DiscordEuroscope/ConfigData.h b/DiscordEuroscope/ConfigData.h index 67ac2c9..0afc35b 100644 --- a/DiscordEuroscope/ConfigData.h +++ b/DiscordEuroscope/ConfigData.h @@ -34,6 +34,26 @@ namespace DiscordEuroScope_Configuration std::string radio_callsign; } RadioCallsignElement_t; typedef std::vector RadioCallsigns_t; + + struct Button + { + std::string label; + std::string url; + + Button() + { + } + + Button(std::string label, std::string url) : label{label}, url{url} + { + } + + inline bool IsValid() const + { + return !label.empty() && !url.empty(); + } + }; + enum States_Enum { State_Idle, @@ -53,6 +73,7 @@ namespace DiscordEuroScope_Configuration std::string details; std::string presence_small_image_text; std::string presence_large_image_text; + Button buttons[2]; }; class ConfigData @@ -62,6 +83,7 @@ namespace DiscordEuroScope_Configuration std::string discord_presence_large_image_key; std::string discord_presence_small_image_key; bool sweatbox_bypass; + Button buttons[2]; State states[7]; RadioCallsigns_t RadioCallsigns; bool loaded_from_ese = false; diff --git a/DiscordEuroscope/ConfigManager.cpp b/DiscordEuroscope/ConfigManager.cpp index e44405f..f832c1a 100644 --- a/DiscordEuroscope/ConfigManager.cpp +++ b/DiscordEuroscope/ConfigManager.cpp @@ -72,6 +72,22 @@ namespace DiscordEuroScope_Configuration data.discord_presence_small_image_key = json_document["discord_presence_small_image_key"].GetString(); assert(json_document["sweatbox_bypass"].IsBool()); data.sweatbox_bypass = json_document["sweatbox_bypass"].GetBool(); + + if (json_document.HasMember("buttons") && json_document["buttons"].IsArray()) + { + auto& arr = json_document["buttons"].GetArray(); + int len = arr.Size(); + if (len > 2) { + len = 2; + } + for (int i = 0; i < len; i++) + { + auto& element = arr[i]; + assert(arr[i]["label"].IsString()); + assert(arr[i]["url"].IsString()); + data.buttons[i] = Button(arr[i]["label"].GetString(), arr[i]["url"].GetString()); + } + } assert(json_document["states"].IsObject()); assert(json_document["states"]["idle"].IsObject()); @@ -96,6 +112,22 @@ namespace DiscordEuroScope_Configuration data.states[i].details = objects[i]["details"].GetString(); data.states[i].presence_small_image_text = objects[i]["presence_small_image_text"].GetString(); data.states[i].presence_large_image_text = objects[i]["presence_large_image_text"].GetString(); + + if (objects[i].HasMember("buttons") && objects[i]["buttons"].IsArray()) + { + auto& arr = objects[i]["buttons"].GetArray(); + int len = arr.Size(); + if (len > 2) { + len = 2; + } + for (int j = 0; j < len; j++) + { + auto& element = arr[j]; + assert(arr[j]["label"].IsString()); + assert(arr[j]["url"].IsString()); + data.states[i].buttons[j] = Button(arr[j]["label"].GetString(), arr[j]["url"].GetString()); + } + } } for (int i = 4; i < 7; i++) @@ -125,6 +157,22 @@ namespace DiscordEuroScope_Configuration data.states[i].details = obj["details"].GetString(); data.states[i].presence_small_image_text = obj["presence_small_image_text"].GetString(); data.states[i].presence_large_image_text = obj["presence_large_image_text"].GetString(); + + if (obj.HasMember("buttons") && obj["buttons"].IsArray()) + { + auto& arr = obj["buttons"].GetArray(); + int len = arr.Size(); + if (len > 2) { + len = 2; + } + for (int j = 0; j < len; j++) + { + auto& element = arr[j]; + assert(arr[j]["label"].IsString()); + assert(arr[j]["url"].IsString()); + data.states[i].buttons[j] = Button(arr[j]["label"].GetString(), arr[j]["url"].GetString()); + } + } } _ready = true; } diff --git a/DiscordEuroscope/DefaultFileContent.h b/DiscordEuroscope/DefaultFileContent.h index 5389cc4..91483cd 100644 --- a/DiscordEuroscope/DefaultFileContent.h +++ b/DiscordEuroscope/DefaultFileContent.h @@ -68,6 +68,12 @@ }\r\ }\r\ },\r\ + \"buttons\": [\r\ + {\r\ + \"label\": \"Callsign stats\",\r\ + \"url\" : \"https://stats.vatsim.net/search/{callsign}\"\r\ + },\r\ + ],\r\ \"radio_callsigns\": {\r\ \"config\": {\r\ \"load_from_ese\": false,\r\ diff --git a/DiscordEuroscope/DiscordEuroscopeExt.cpp b/DiscordEuroscope/DiscordEuroscopeExt.cpp index 410587a..d9298a5 100644 --- a/DiscordEuroscope/DiscordEuroscopeExt.cpp +++ b/DiscordEuroscope/DiscordEuroscopeExt.cpp @@ -101,6 +101,8 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent discordPresence.largeImageKey = data.discord_presence_large_image_key.c_str(); discordPresence.smallImageKey = data.discord_presence_small_image_key.c_str(); discordPresence.startTimestamp = pMyPlugIn->EuroInittime; + + DiscordButton buttons[2] = { nullptr, nullptr }; switch (pMyPlugIn->GetConnectionType()) { @@ -112,6 +114,25 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Idle].presence_small_image_key, data.discord_presence_small_image_key).c_str(); discordPresence.smallImageText = data.states[State_Idle].presence_small_image_text.c_str(); discordPresence.state = data.states[State_Idle].state.c_str(); + if (data.buttons[0].IsValid()) { + buttons[0] = + { + ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[0].label, data.buttons[0].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[0].url, data.buttons[0].url).c_str() + }; + // Nested condition because there will never be a single button in index 1. + if (data.buttons[1].IsValid()) { + buttons[1] = + { + ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[1].label, data.buttons[1].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Idle].buttons[1].url, data.buttons[1].url).c_str() + }; + } + else { + buttons[1] = { "" , "" }; + } + discordPresence.buttons = buttons; + } Discord_UpdatePresence(&discordPresence); Discord_RunCallbacks(); return; @@ -122,6 +143,25 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Playback].presence_small_image_key, data.discord_presence_small_image_key).c_str(); discordPresence.smallImageText = data.states[State_Playback].presence_small_image_text.c_str(); discordPresence.state = data.states[State_Playback].state.c_str(); + if (data.buttons[0].IsValid()) { + buttons[0] = + { + ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[0].label, data.buttons[0].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[0].url, data.buttons[0].url).c_str() + }; + // Nested condition because there will never be a single button in index 1. + if (data.buttons[1].IsValid()) { + buttons[1] = + { + ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[1].label, data.buttons[1].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Playback].buttons[1].url, data.buttons[1].url).c_str() + }; + } + else { + buttons[1] = { "" , "" }; + } + discordPresence.buttons = buttons; + } Discord_UpdatePresence(&discordPresence); Discord_RunCallbacks(); return; @@ -134,6 +174,24 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent discordPresence.smallImageKey = ConfigData::LocalOrGlobal(data.states[State_Sweatbox].presence_small_image_key, data.discord_presence_small_image_key).c_str(); discordPresence.smallImageText = data.states[State_Sweatbox].presence_small_image_text.c_str(); discordPresence.state = data.states[State_Sweatbox].state.c_str(); + if (data.buttons[0].IsValid()) { + buttons[0] = + { + ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[0].label, data.buttons[0].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[0].url, data.buttons[0].url).c_str() + }; + if (data.buttons[1].IsValid()) { + buttons[1] = + { + ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[1].label, data.buttons[1].label).c_str(), + ConfigData::LocalOrGlobal(data.states[State_Sweatbox].buttons[1].url, data.buttons[1].url).c_str() + }; + } + else { + buttons[1] = { "" , "" }; + } + discordPresence.buttons = buttons; + } Discord_UpdatePresence(&discordPresence); Discord_RunCallbacks(); return; @@ -195,6 +253,32 @@ VOID CALLBACK DiscordTimer(_In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent presence_small_image_text = ConfigData::LocalOrGlobal(data.states[use_state].presence_small_image_text, data.states[State_Direct].presence_small_image_text); presence_large_image_text = ConfigData::LocalOrGlobal(data.states[use_state].presence_large_image_text, data.states[State_Direct].presence_large_image_text); + if (data.buttons[0].IsValid()) { + std::string button1_label = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[0].label, data.states[State_Direct].buttons[0].label), data.buttons[0].label); + MessageFormatter::formatmap(button1_label, Dictionary); + std::string button1_url = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[0].url, data.states[State_Direct].buttons[0].url), data.buttons[0].url); + MessageFormatter::formatmap(button1_url, Dictionary); + buttons[0] = { + button1_label.c_str(), + button1_url.c_str() + }; + if (data.buttons[1].IsValid()) { + + std::string button2_label = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[1].label, data.states[State_Direct].buttons[1].label), data.buttons[1].label); + MessageFormatter::formatmap(button2_label, Dictionary); + std::string button2_url = ConfigData::LocalOrGlobal(ConfigData::LocalOrGlobal(data.states[use_state].buttons[1].url, data.states[State_Direct].buttons[1].url), data.buttons[1].url); + MessageFormatter::formatmap(button2_url, Dictionary); + buttons[1] = { + button2_label.c_str(), + button2_url.c_str() + }; + } + else { + buttons[1] = { "" , "" }; + } + discordPresence.buttons = buttons; + } + MessageFormatter::formatmap(state, Dictionary); MessageFormatter::formatmap(details, Dictionary); MessageFormatter::formatmap(presence_small_image_text, Dictionary); diff --git a/DiscordEuroscope/inc/discord_rpc.h b/DiscordEuroscope/inc/discord_rpc.h index 3e1441e..bd6cc8d 100644 --- a/DiscordEuroscope/inc/discord_rpc.h +++ b/DiscordEuroscope/inc/discord_rpc.h @@ -23,6 +23,11 @@ extern "C" { #endif +typedef struct DiscordButton { + const char* label; /* TODO: max 128 bytes? */ + const char* url; /* TODO: would be reasonable to expect it to be >=128 bytes? */ +} DiscordButton; + typedef struct DiscordRichPresence { const char* state; /* max 128 bytes */ const char* details; /* max 128 bytes */ @@ -35,10 +40,12 @@ typedef struct DiscordRichPresence { const char* partyId; /* max 128 bytes */ int partySize; int partyMax; + int partyPrivacy; const char* matchSecret; /* max 128 bytes */ const char* joinSecret; /* max 128 bytes */ const char* spectateSecret; /* max 128 bytes */ int8_t instance; + const DiscordButton* buttons; } DiscordRichPresence; typedef struct DiscordUser { @@ -60,6 +67,8 @@ typedef struct DiscordEventHandlers { #define DISCORD_REPLY_NO 0 #define DISCORD_REPLY_YES 1 #define DISCORD_REPLY_IGNORE 2 +#define DISCORD_PARTY_PRIVATE 0 +#define DISCORD_PARTY_PUBLIC 1 DISCORD_EXPORT void Discord_Initialize(const char* applicationId, DiscordEventHandlers* handlers, diff --git a/DiscordEuroscope/lib/3.2/discord-rpc-rel.lib b/DiscordEuroscope/lib/3.2/discord-rpc-rel.lib index b08bd9c..2a3bb9d 100644 Binary files a/DiscordEuroscope/lib/3.2/discord-rpc-rel.lib and b/DiscordEuroscope/lib/3.2/discord-rpc-rel.lib differ diff --git a/DiscordEuroscope/lib/3.2/discord-rpc.lib b/DiscordEuroscope/lib/3.2/discord-rpc.lib index 090f97c..76dea3c 100644 Binary files a/DiscordEuroscope/lib/3.2/discord-rpc.lib and b/DiscordEuroscope/lib/3.2/discord-rpc.lib differ diff --git a/discord-rpc b/discord-rpc index 963aa9f..b9b5699 160000 --- a/discord-rpc +++ b/discord-rpc @@ -1 +1 @@ -Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33 +Subproject commit b9b56999eaccfb54c1071868da04fe72a9869520