Compare commits

..

7 Commits

Author SHA1 Message Date
Mason Sciotti
963aa9f3e5
[Maintenance PR for Legacy Gamedevs] Add party privacy enum (#306)
* Add party privacy enum

* Add party privacy enum

* Possible unreal working

* Cast to int
2020-09-21 14:51:48 -07:00
msciotti
e4c0c569ec
Clarify deprecation 2019-11-27 11:26:13 -08:00
IceNinjaman
b6d0a9cdbd wchar.h instead cwchar when compiler is MinGW to prevent weird behavior with vsnwprintf (#277) 2019-11-27 11:24:32 -08:00
msciotti
eff23a770a
add deprecation note to readme 2019-07-10 14:39:38 -07:00
Kenny McCormick
34ce3ac803 fix "destopFileFormat" typo (#283)
resolution for #282
2019-04-30 11:39:14 -07:00
msciotti
c59fd6df20
Revert "Choose pipe number on initialize (#250)"
This reverts commit 4824b20f28b1ebffb2a57881684ef87f76659a6c.
2019-01-24 13:23:35 -08:00
Mason Sciotti
4824b20f28
Choose pipe number on initialize (#250)
* Choose pipe number on initialize

* Get pipe from base connection instance

* UE4 support

* Warnings as errors yelling

* Fix windows connection

* Oops all variables

* maybe this fixes it

* This one actually works!!!!

* Fix double function declaration
2019-01-14 00:16:22 -08:00
16 changed files with 66 additions and 29 deletions

View File

@ -1,5 +1,11 @@
# Discord RPC # Discord RPC
## Deprecation Notice
This library has been deprecated in favor of Discord's GameSDK. [Learn more here](https://discordapp.com/developers/docs/game-sdk/sdk-starter-guide)
---
This is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is. PRs/feedback welcome if you have an improvement everyone might want, or can describe how this doesn't meet your needs. This is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is. PRs/feedback welcome if you have an improvement everyone might want, or can describe how this doesn't meet your needs.
Included here are some quick demos that implement the very minimal subset to show current status, and Included here are some quick demos that implement the very minimal subset to show current status, and

View File

@ -31,6 +31,11 @@ public class DiscordController : MonoBehaviour
clickCounter++; clickCounter++;
presence.details = string.Format("Button clicked {0} times", clickCounter); presence.details = string.Format("Button clicked {0} times", clickCounter);
presence.joinSecret = "aSecret";
presence.partyId = "aPartyId";
presence.partySize = 1;
presence.partyMax = 3;
presence.partyPrivacy = DiscordRpc.PartyPrivacy.Public;
DiscordRpc.UpdatePresence(presence); DiscordRpc.UpdatePresence(presence);
} }

View File

@ -56,6 +56,7 @@ public class DiscordRpc
public IntPtr partyId; /* max 128 bytes */ public IntPtr partyId; /* max 128 bytes */
public int partySize; public int partySize;
public int partyMax; public int partyMax;
public int partyPrivacy;
public IntPtr matchSecret; /* max 128 bytes */ public IntPtr matchSecret; /* max 128 bytes */
public IntPtr joinSecret; /* max 128 bytes */ public IntPtr joinSecret; /* max 128 bytes */
public IntPtr spectateSecret; /* max 128 bytes */ public IntPtr spectateSecret; /* max 128 bytes */
@ -78,7 +79,13 @@ public class DiscordRpc
Ignore = 2 Ignore = 2
} }
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId, int pipe = 0) public enum PartyPrivacy
{
Private = 0,
Public = 1
}
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId)
{ {
Callbacks = handlers; Callbacks = handlers;
@ -90,11 +97,11 @@ public class DiscordRpc
staticEventHandlers.spectateCallback += DiscordRpc.SpectateCallback; staticEventHandlers.spectateCallback += DiscordRpc.SpectateCallback;
staticEventHandlers.requestCallback += DiscordRpc.RequestCallback; staticEventHandlers.requestCallback += DiscordRpc.RequestCallback;
InitializeInternal(applicationId, ref staticEventHandlers, autoRegister, optionalSteamId, pipe); InitializeInternal(applicationId, ref staticEventHandlers, autoRegister, optionalSteamId);
} }
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
static extern void InitializeInternal(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId, int pipe); static extern void InitializeInternal(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)] [DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
public static extern void Shutdown(); public static extern void Shutdown();
@ -137,6 +144,7 @@ public class DiscordRpc
public string partyId; /* max 128 bytes */ public string partyId; /* max 128 bytes */
public int partySize; public int partySize;
public int partyMax; public int partyMax;
public PartyPrivacy partyPrivacy;
public string matchSecret; /* max 128 bytes */ public string matchSecret; /* max 128 bytes */
public string joinSecret; /* max 128 bytes */ public string joinSecret; /* max 128 bytes */
public string spectateSecret; /* max 128 bytes */ public string spectateSecret; /* max 128 bytes */
@ -164,6 +172,7 @@ public class DiscordRpc
_presence.partyId = StrToPtr(partyId); _presence.partyId = StrToPtr(partyId);
_presence.partySize = partySize; _presence.partySize = partySize;
_presence.partyMax = partyMax; _presence.partyMax = partyMax;
_presence.partyPrivacy = (int)partyPrivacy;
_presence.matchSecret = StrToPtr(matchSecret); _presence.matchSecret = StrToPtr(matchSecret);
_presence.joinSecret = StrToPtr(joinSecret); _presence.joinSecret = StrToPtr(joinSecret);
_presence.spectateSecret = StrToPtr(spectateSecret); _presence.spectateSecret = StrToPtr(spectateSecret);

View File

@ -47,6 +47,7 @@ static void updateDiscordPresence()
discordPresence.partyId = "party1234"; discordPresence.partyId = "party1234";
discordPresence.partySize = 1; discordPresence.partySize = 1;
discordPresence.partyMax = 6; discordPresence.partyMax = 6;
discordPresence.partyPrivacy = DISCORD_PARTY_PUBLIC;
discordPresence.matchSecret = "xyzzy"; discordPresence.matchSecret = "xyzzy";
discordPresence.joinSecret = "join"; discordPresence.joinSecret = "join";
discordPresence.spectateSecret = "look"; discordPresence.spectateSecret = "look";
@ -129,7 +130,7 @@ static void discordInit()
handlers.joinGame = handleDiscordJoin; handlers.joinGame = handleDiscordJoin;
handlers.spectateGame = handleDiscordSpectate; handlers.spectateGame = handleDiscordSpectate;
handlers.joinRequest = handleDiscordJoinRequest; handlers.joinRequest = handleDiscordJoinRequest;
Discord_Initialize(APPLICATION_ID, &handlers, 1, NULL, 0); Discord_Initialize(APPLICATION_ID, &handlers, 1, NULL);
} }
static void gameLoop() static void gameLoop()

View File

@ -82,8 +82,7 @@ static void JoinRequestHandler(const DiscordUser* request)
void UDiscordRpc::Initialize(const FString& applicationId, void UDiscordRpc::Initialize(const FString& applicationId,
bool autoRegister, bool autoRegister,
const FString& optionalSteamId, const FString& optionalSteamId)
int pipe)
{ {
self = this; self = this;
IsConnected = false; IsConnected = false;
@ -103,7 +102,7 @@ void UDiscordRpc::Initialize(const FString& applicationId,
auto appId = StringCast<ANSICHAR>(*applicationId); auto appId = StringCast<ANSICHAR>(*applicationId);
auto steamId = StringCast<ANSICHAR>(*optionalSteamId); auto steamId = StringCast<ANSICHAR>(*optionalSteamId);
Discord_Initialize( Discord_Initialize(
(const char*)appId.Get(), &handlers, autoRegister, (const char*)steamId.Get(), pipe); (const char*)appId.Get(), &handlers, autoRegister, (const char*)steamId.Get());
} }
void UDiscordRpc::Shutdown() void UDiscordRpc::Shutdown()
@ -154,6 +153,7 @@ void UDiscordRpc::UpdatePresence()
rp.endTimestamp = RichPresence.endTimestamp; rp.endTimestamp = RichPresence.endTimestamp;
rp.partySize = RichPresence.partySize; rp.partySize = RichPresence.partySize;
rp.partyMax = RichPresence.partyMax; rp.partyMax = RichPresence.partyMax;
rp.partyPrivacy = (int)RichPresence.partyPrivacy;
rp.instance = RichPresence.instance; rp.instance = RichPresence.instance;
Discord_UpdatePresence(&rp); Discord_UpdatePresence(&rp);

View File

@ -35,6 +35,16 @@ enum class EDiscordJoinResponseCodes : uint8
DISCORD_REPLY_IGNORE UMETA(DisplayName="Ignore") DISCORD_REPLY_IGNORE UMETA(DisplayName="Ignore")
}; };
/**
* Valid party privacy values
*/
UENUM(BlueprintType)
enum class EDiscordPartyPrivacy: uint8
{
DISCORD_PARTY_PRIVATE UMETA(DisplayName="Private"),
DISCORD_PARTY_PUBLIC UMETA(DisplayName="Public")
};
DECLARE_LOG_CATEGORY_EXTERN(Discord, Log, All); DECLARE_LOG_CATEGORY_EXTERN(Discord, Log, All);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordConnected, const FDiscordUserData&, joinRequest); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordConnected, const FDiscordUserData&, joinRequest);
@ -77,6 +87,8 @@ struct FDiscordRichPresence {
UPROPERTY(BlueprintReadWrite) UPROPERTY(BlueprintReadWrite)
int partyMax; int partyMax;
UPROPERTY(BlueprintReadWrite) UPROPERTY(BlueprintReadWrite)
EDiscordPartyPrivacy partyPrivacy;
UPROPERTY(BlueprintReadWrite)
FString matchSecret; FString matchSecret;
UPROPERTY(BlueprintReadWrite) UPROPERTY(BlueprintReadWrite)
FString joinSecret; FString joinSecret;
@ -99,8 +111,7 @@ public:
Category = "Discord") Category = "Discord")
void Initialize(const FString& applicationId, void Initialize(const FString& applicationId,
bool autoRegister, bool autoRegister,
const FString& optionalSteamId, const FString& optionalSteamId);
int optionalPipeNumber);
UFUNCTION(BlueprintCallable, UFUNCTION(BlueprintCallable,
meta = (DisplayName = "Shut down connection", Keywords = "Discord rpc"), meta = (DisplayName = "Shut down connection", Keywords = "Discord rpc"),

View File

@ -35,6 +35,7 @@ typedef struct DiscordRichPresence {
const char* partyId; /* max 128 bytes */ const char* partyId; /* max 128 bytes */
int partySize; int partySize;
int partyMax; int partyMax;
int partyPrivacy;
const char* matchSecret; /* max 128 bytes */ const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */ const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */ const char* spectateSecret; /* max 128 bytes */
@ -60,12 +61,13 @@ typedef struct DiscordEventHandlers {
#define DISCORD_REPLY_NO 0 #define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1 #define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2 #define DISCORD_REPLY_IGNORE 2
#define DISCORD_PARTY_PRIVATE 0
#define DISCORD_PARTY_PUBLIC 1
DISCORD_EXPORT void Discord_Initialize(const char* applicationId, DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers, DiscordEventHandlers* handlers,
int autoRegister, int autoRegister,
const char* optionalSteamId, const char* optionalSteamId);
int optionalPipeNumber);
DISCORD_EXPORT void Discord_Shutdown(void); DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */ /* checks for incoming messages, dispatches callbacks */

View File

@ -12,7 +12,7 @@ struct BaseConnection {
static BaseConnection* Create(); static BaseConnection* Create();
static void Destroy(BaseConnection*&); static void Destroy(BaseConnection*&);
bool isOpen{false}; bool isOpen{false};
bool Open(int pipe); bool Open();
bool Close(); bool Close();
bool Write(const void* data, size_t length); bool Write(const void* data, size_t length);
bool Read(void* data, size_t length); bool Read(void* data, size_t length);

View File

@ -49,7 +49,7 @@ static const char* GetTempPath()
c = nullptr; c = nullptr;
} }
bool BaseConnection::Open(int pipe) bool BaseConnection::Open()
{ {
const char* tempPath = GetTempPath(); const char* tempPath = GetTempPath();
auto self = reinterpret_cast<BaseConnectionUnix*>(this); auto self = reinterpret_cast<BaseConnectionUnix*>(this);
@ -62,7 +62,8 @@ bool BaseConnection::Open(int pipe)
int optval = 1; int optval = 1;
setsockopt(self->sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)); setsockopt(self->sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
#endif #endif
for (int pipeNum = pipe; pipeNum < 10; ++pipeNum) {
for (int pipeNum = 0; pipeNum < 10; ++pipeNum) {
snprintf( snprintf(
PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum); PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum);
int err = connect(self->sock, (const sockaddr*)&PipeAddr, sizeof(PipeAddr)); int err = connect(self->sock, (const sockaddr*)&PipeAddr, sizeof(PipeAddr));

View File

@ -6,7 +6,6 @@
#define NOIME #define NOIME
#include <assert.h> #include <assert.h>
#include <windows.h> #include <windows.h>
#include <sstream>
int GetProcessId() int GetProcessId()
{ {
@ -31,11 +30,11 @@ static BaseConnectionWin Connection;
c = nullptr; c = nullptr;
} }
bool BaseConnection::Open(int pipe) bool BaseConnection::Open()
{ {
wchar_t pipeName[]{L"\\\\?\\pipe\\discord-ipc-0"}; wchar_t pipeName[]{L"\\\\?\\pipe\\discord-ipc-0"};
const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2; const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2;
pipeName[pipeDigit] += pipe; pipeName[pipeDigit] = L'0';
auto self = reinterpret_cast<BaseConnectionWin*>(this); auto self = reinterpret_cast<BaseConnectionWin*>(this);
for (;;) { for (;;) {
self->pipe = ::CreateFileW( self->pipe = ::CreateFileW(

View File

@ -41,7 +41,7 @@ extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const
command = exePath; command = exePath;
} }
const char* destopFileFormat = "[Desktop Entry]\n" const char* desktopFileFormat = "[Desktop Entry]\n"
"Name=Game %s\n" "Name=Game %s\n"
"Exec=%s %%u\n" // note: it really wants that %u in there "Exec=%s %%u\n" // note: it really wants that %u in there
"Type=Application\n" "Type=Application\n"
@ -50,7 +50,7 @@ extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const
"MimeType=x-scheme-handler/discord-%s;\n"; "MimeType=x-scheme-handler/discord-%s;\n";
char desktopFile[2048]; char desktopFile[2048];
int fileLen = snprintf( int fileLen = snprintf(
desktopFile, sizeof(desktopFile), destopFileFormat, applicationId, command, applicationId); desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId);
if (fileLen <= 0) { if (fileLen <= 0) {
return; return;
} }

View File

@ -7,7 +7,6 @@
#define NOIME #define NOIME
#include <windows.h> #include <windows.h>
#include <psapi.h> #include <psapi.h>
#include <cwchar>
#include <cstdio> #include <cstdio>
/** /**
@ -20,6 +19,7 @@
* The entire function is rewritten * The entire function is rewritten
*/ */
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <wchar.h>
/// strsafe.h fixes /// strsafe.h fixes
static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...) static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...)
{ {
@ -34,6 +34,7 @@ static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat,
return ret; return ret;
} }
#else #else
#include <cwchar>
#include <strsafe.h> #include <strsafe.h>
#endif // __MINGW32__ #endif // __MINGW32__

View File

@ -273,8 +273,7 @@ static bool DeregisterForEvent(const char* evtName)
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId, extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers, DiscordEventHandlers* handlers,
int autoRegister, int autoRegister,
const char* optionalSteamId, const char* optionalSteamId)
int pipe)
{ {
IoThread = new (std::nothrow) IoThreadHolder(); IoThread = new (std::nothrow) IoThreadHolder();
if (IoThread == nullptr) { if (IoThread == nullptr) {
@ -309,7 +308,7 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
return; return;
} }
Connection = RpcConnection::Create(applicationId, pipe); Connection = RpcConnection::Create(applicationId);
Connection->onConnect = [](JsonDocument& readyMessage) { Connection->onConnect = [](JsonDocument& readyMessage) {
Discord_UpdateHandlers(&QueuedHandlers); Discord_UpdateHandlers(&QueuedHandlers);
if (QueuedPresence.length > 0) { if (QueuedPresence.length > 0) {

View File

@ -6,11 +6,10 @@
static const int RpcVersion = 1; static const int RpcVersion = 1;
static RpcConnection Instance; static RpcConnection Instance;
/*static*/ RpcConnection* RpcConnection::Create(const char* applicationId, int pipe) /*static*/ RpcConnection* RpcConnection::Create(const char* applicationId)
{ {
Instance.connection = BaseConnection::Create(); Instance.connection = BaseConnection::Create();
StringCopy(Instance.appId, applicationId); StringCopy(Instance.appId, applicationId);
Instance.pipe = pipe;
return &Instance; return &Instance;
} }
@ -27,7 +26,7 @@ void RpcConnection::Open()
return; return;
} }
if (state == State::Disconnected && !connection->Open(Instance.pipe)) { if (state == State::Disconnected && !connection->Open()) {
return; return;
} }

View File

@ -43,12 +43,11 @@ struct RpcConnection {
void (*onConnect)(JsonDocument& message){nullptr}; void (*onConnect)(JsonDocument& message){nullptr};
void (*onDisconnect)(int errorCode, const char* message){nullptr}; void (*onDisconnect)(int errorCode, const char* message){nullptr};
char appId[64]{}; char appId[64]{};
int pipe;
int lastErrorCode{0}; int lastErrorCode{0};
char lastErrorMessage[256]{}; char lastErrorMessage[256]{};
RpcConnection::MessageFrame sendFrame; RpcConnection::MessageFrame sendFrame;
static RpcConnection* Create(const char* applicationId, int pipe); static RpcConnection* Create(const char* applicationId);
static void Destroy(RpcConnection*&); static void Destroy(RpcConnection*&);
inline bool IsOpen() const { return state == State::Connected; } inline bool IsOpen() const { return state == State::Connected; }

View File

@ -134,7 +134,7 @@ size_t JsonWriteRichPresenceObj(char* dest,
} }
if ((presence->partyId && presence->partyId[0]) || presence->partySize || if ((presence->partyId && presence->partyId[0]) || presence->partySize ||
presence->partyMax) { presence->partyMax || presence->partyPrivacy) {
WriteObject party(writer, "party"); WriteObject party(writer, "party");
WriteOptionalString(writer, "id", presence->partyId); WriteOptionalString(writer, "id", presence->partyId);
if (presence->partySize && presence->partyMax) { if (presence->partySize && presence->partyMax) {
@ -142,6 +142,11 @@ size_t JsonWriteRichPresenceObj(char* dest,
writer.Int(presence->partySize); writer.Int(presence->partySize);
writer.Int(presence->partyMax); writer.Int(presence->partyMax);
} }
if (presence->partyPrivacy) {
WriteKey(writer, "privacy");
writer.Int(presence->partyPrivacy);
}
} }
if ((presence->matchSecret && presence->matchSecret[0]) || if ((presence->matchSecret && presence->matchSecret[0]) ||