From a68bb5cd308d68be00f10afcd7150f9c6ebfa175 Mon Sep 17 00:00:00 2001 From: p0358 Date: Fri, 11 Mar 2022 21:39:08 +0100 Subject: [PATCH] add support for buttons and overlay guild and activity invites --- examples/send-presence/send-presence.c | 57 +++++++++++++- include/discord_rpc.h | 12 +++ src/discord_rpc.cpp | 30 ++++++++ src/serialization.cpp | 101 +++++++++++++++++++++++-- src/serialization.h | 12 +++ 5 files changed, 201 insertions(+), 11 deletions(-) diff --git a/examples/send-presence/send-presence.c b/examples/send-presence/send-presence.c index 1b651f2c..ee7680c6 100644 --- a/examples/send-presence/send-presence.c +++ b/examples/send-presence/send-presence.c @@ -14,7 +14,8 @@ static const char* APPLICATION_ID = "345229890980937739"; static int FrustrationLevel = 0; static int64_t StartTime; -static int SendPresence = 1; +static char SendPresence = 1; +static char SendButtons = 0; static int prompt(char* line, size_t size) { @@ -52,6 +53,15 @@ static void updateDiscordPresence() discordPresence.joinSecret = "join"; discordPresence.spectateSecret = "look"; discordPresence.instance = 0; + + DiscordButton buttons[] = { + {.label = "Test", .url = "https://example.com"}, + {.label = "Test 2", .url = "https://discord.gg/fortnite"}, + }; + + if (SendButtons) + discordPresence.buttons = buttons; + Discord_UpdatePresence(&discordPresence); } else { @@ -153,6 +163,12 @@ static void gameLoop() continue; } + if (line[0] == 'y') { + printf("Reinit Discord.\n"); + discordInit(); + continue; + } + if (line[0] == 'c') { if (SendPresence) { printf("Clearing presence information.\n"); @@ -166,11 +182,44 @@ static void gameLoop() continue; } - if (line[0] == 'y') { - printf("Reinit Discord.\n"); - discordInit(); + if (line[0] == 'b') { + if (SendButtons) { + printf("Removing buttons.\n"); + SendButtons = 0; + } + else { + printf("Adding buttons.\n"); + SendButtons = 1; + } + updateDiscordPresence(); continue; } + + if (line[0] == 'i' && line[1]) { + if (line[1] == 'a') { + printf("Opening activity invite (type 1).\n"); + Discord_OpenActivityInvite(1); + continue; + } + + if (line[1] == '2') { // does not seem to work + printf("Opening activity invite (type 2).\n"); + Discord_OpenActivityInvite(2); + continue; + } + + if (line[1] == '0') { // does not seem to work either... + printf("Opening activity invite (type 0).\n"); + Discord_OpenActivityInvite(0); + continue; + } + + if (line[1] == 'g') { + printf("Opening guild invite.\n"); + Discord_OpenGuildInvite("fortnite"); + continue; + } + } if (time(NULL) & 1) { printf("I don't understand that.\n"); diff --git a/include/discord_rpc.h b/include/discord_rpc.h index 9470434a..c66995f1 100644 --- a/include/discord_rpc.h +++ b/include/discord_rpc.h @@ -23,6 +23,11 @@ extern "C" { #endif +typedef struct DiscordButton { + const char* label; + const char* url; +} DiscordButton; + typedef struct DiscordRichPresence { const char* state; /* max 128 bytes */ const char* details; /* max 128 bytes */ @@ -40,6 +45,7 @@ typedef struct DiscordRichPresence { const char* joinSecret; /* max 128 bytes */ const char* spectateSecret; /* max 128 bytes */ int8_t instance; + const DiscordButton* buttons; } DiscordRichPresence; typedef struct DiscordUser { @@ -64,6 +70,9 @@ typedef struct DiscordEventHandlers { #define DISCORD_PARTY_PRIVATE 0 #define DISCORD_PARTY_PUBLIC 1 +#define DISCORD_ACTIVITY_ACTION_TYPE_JOIN 1 +#define DISCORD_ACTIVITY_ACTION_TYPE_SPECTATE 2 + DISCORD_EXPORT void Discord_Initialize(const char* applicationId, DiscordEventHandlers* handlers, int autoRegister, @@ -83,6 +92,9 @@ DISCORD_EXPORT void Discord_ClearPresence(void); DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); +DISCORD_EXPORT void Discord_OpenActivityInvite(/* DISCORD_ACTIVITY_ACTION_TYPE_ */ int8_t type); +DISCORD_EXPORT void Discord_OpenGuildInvite(const char* code); + DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers); #ifdef __cplusplus diff --git a/src/discord_rpc.cpp b/src/discord_rpc.cpp index a425f1f4..d0295193 100644 --- a/src/discord_rpc.cpp +++ b/src/discord_rpc.cpp @@ -397,6 +397,36 @@ extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_RE } } +extern "C" DISCORD_EXPORT void Discord_OpenActivityInvite(int8_t type) +{ + // if we are not connected, let's not batch up stale messages for later + if (!Connection || !Connection->IsOpen()) { + return; + } + auto qmessage = SendQueue.GetNextAddMessage(); + if (qmessage) { + qmessage->length = JsonWriteOpenOverlayActivityInvite( + qmessage->buffer, sizeof(qmessage->buffer), type, Nonce++, Pid); + SendQueue.CommitAdd(); + SignalIOActivity(); + } +} + +extern "C" DISCORD_EXPORT void Discord_OpenGuildInvite(const char* code) +{ + // if we are not connected, let's not batch up stale messages for later + if (!Connection || !Connection->IsOpen()) { + return; + } + auto qmessage = SendQueue.GetNextAddMessage(); + if (qmessage) { + qmessage->length = + JsonWriteOpenOverlayGuildInvite(qmessage->buffer, sizeof(qmessage->buffer), code, Nonce++, Pid); + SendQueue.CommitAdd(); + SignalIOActivity(); + } +} + extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void) { // Note on some weirdness: internally we might connect, get other signals, disconnect any number diff --git a/src/serialization.cpp b/src/serialization.cpp index 70efa637..6f36123d 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -149,17 +149,41 @@ size_t JsonWriteRichPresenceObj(char* dest, } } - if ((presence->matchSecret && presence->matchSecret[0]) || - (presence->joinSecret && presence->joinSecret[0]) || - (presence->spectateSecret && presence->spectateSecret[0])) { - WriteObject secrets(writer, "secrets"); - WriteOptionalString(writer, "match", presence->matchSecret); - WriteOptionalString(writer, "join", presence->joinSecret); - WriteOptionalString(writer, "spectate", presence->spectateSecret); + // Send secrets only when buttons aren't set + if (!presence->buttons) { + if ((presence->matchSecret && presence->matchSecret[0]) || + (presence->joinSecret && presence->joinSecret[0]) || + (presence->spectateSecret && presence->spectateSecret[0])) { + WriteObject secrets(writer, "secrets"); + WriteOptionalString(writer, "match", presence->matchSecret); + WriteOptionalString(writer, "join", presence->joinSecret); + WriteOptionalString(writer, "spectate", presence->spectateSecret); + } } writer.Key("instance"); writer.Bool(presence->instance != 0); + + if (presence->buttons) { + const auto btns = presence->buttons; + WriteArray buttons(writer, "buttons"); + + if (btns[0].label[0]) { + WriteObject button0(writer); + WriteKey(writer, "url"); + writer.String(btns[0].url); + WriteKey(writer, "label"); + writer.String(btns[0].label); + } + + if (btns[1].label[0]) { + WriteObject button1(writer); + WriteKey(writer, "url"); + writer.String(btns[1].url); + WriteKey(writer, "label"); + writer.String(btns[1].label); + } + } } } } @@ -248,3 +272,66 @@ size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int rep return writer.Size(); } + +size_t JsonWriteOpenOverlayActivityInvite(char* dest, + size_t maxLen, + int8_t type, + int nonce, + int pid) +{ + JsonWriter writer(dest, maxLen); + + { + WriteObject obj(writer); + + WriteKey(writer, "cmd"); + writer.String("OPEN_OVERLAY_ACTIVITY_INVITE"); + + WriteKey(writer, "args"); + { + WriteObject args(writer); + + WriteKey(writer, "type"); + writer.Int(type); + + // just to make sure? + WriteKey(writer, "pid"); + writer.Int(pid); + } + + JsonWriteNonce(writer, nonce); + } + + return writer.Size(); +} + +size_t JsonWriteOpenOverlayGuildInvite(char* dest, + size_t maxLen, + const char* code, + int nonce, + int pid) +{ + JsonWriter writer(dest, maxLen); + + { + WriteObject obj(writer); + + WriteKey(writer, "cmd"); + writer.String("OPEN_OVERLAY_GUILD_INVITE"); + + WriteKey(writer, "args"); + { + WriteObject args(writer); + + WriteKey(writer, "code"); + writer.String(code); + + WriteKey(writer, "pid"); + writer.Int(pid); + } + + JsonWriteNonce(writer, nonce); + } + + return writer.Size(); +} diff --git a/src/serialization.h b/src/serialization.h index 9c462dc2..0df70a06 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -51,6 +51,18 @@ size_t JsonWriteUnsubscribeCommand(char* dest, size_t maxLen, int nonce, const c size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce); +size_t JsonWriteOpenOverlayActivityInvite(char* dest, + size_t maxLen, + int8_t type, + int nonce, + int pid); + +size_t JsonWriteOpenOverlayGuildInvite(char* dest, + size_t maxLen, + const char* code, + int nonce, + int pid); + // I want to use as few allocations as I can get away with, and to do that with RapidJson, you need // to supply some of your own allocators for stuff rather than use the defaults