Compare commits
12 Commits
v3.4.0
...
choose-pip
Author | SHA1 | Date | |
---|---|---|---|
8bb85f0545 | |||
766596722c | |||
7fe88765fd | |||
1d30b94987 | |||
6796d2ffa9 | |||
d90a8efd47 | |||
544f91a5a8 | |||
2f52c24f6d | |||
d5a342c7bb | |||
4e53fa0392 | |||
d478ed5608 | |||
8db649ba5f |
54
README.md
54
README.md
@ -15,6 +15,33 @@ Zeroith, you should be set up to build things because you are a game developer,
|
|||||||
|
|
||||||
First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
|
First, head on over to the [Discord developers site](https://discordapp.com/developers/applications/me) and make yourself an app. Keep track of `Client ID` -- you'll need it here to pass to the init function.
|
||||||
|
|
||||||
|
### Unreal Engine 4 Setup
|
||||||
|
|
||||||
|
To use the Rich Presense plugin with Unreal Engine Projects:
|
||||||
|
|
||||||
|
1. Download the latest [release](https://github.com/discordapp/discord-rpc/releases) for each operating system you are targeting and the zipped source code
|
||||||
|
2. In the source code zip, copy the UE plugin—`examples/unrealstatus/Plugins/discordrpc`—to your project's plugin directory
|
||||||
|
3. At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create an `Include` folder and copy `discord_rpc.h` and `discord_register.h` to it from the zip
|
||||||
|
4. Follow the steps below for each OS
|
||||||
|
5. Build your UE4 project
|
||||||
|
6. Launch the editor, and enable the Discord plugin.
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Win64` folder
|
||||||
|
- Copy `lib/discord-rpc.lib` and `bin/discord-rpc.dll` from `[RELEASE_ZIP]/win64-dynamic` to the `Win64` folder
|
||||||
|
|
||||||
|
#### Mac
|
||||||
|
|
||||||
|
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Mac` folder
|
||||||
|
- Copy `libdiscord-rpc.dylib` from `[RELEASE_ZIP]/osx-dynamic/lib` to the `Mac` folder
|
||||||
|
|
||||||
|
#### Linux
|
||||||
|
|
||||||
|
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Linux` folder
|
||||||
|
- Inside, create another folder `x86_64-unknown-linux-gnu`
|
||||||
|
- Copy `libdiscord-rpc.so` from `[RELEASE_ZIP]/linux-dynamic/lib` to `Linux/x86_64-unknown-linux-gnu`
|
||||||
|
|
||||||
### Unity Setup
|
### Unity Setup
|
||||||
|
|
||||||
If you're a Unity developer looking to integrate Rich Presence into your game, follow this simple guide to get started towards success:
|
If you're a Unity developer looking to integrate Rich Presence into your game, follow this simple guide to get started towards success:
|
||||||
@ -101,33 +128,6 @@ This is a sample [Unity](https://unity3d.com/) project that wraps a DLL version
|
|||||||
|
|
||||||
This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to make a very simple UI. Run `python build.py unreal` in the root directory to build the correct library files and place them in their respective folders.
|
This is a sample [Unreal](https://www.unrealengine.com) project that wraps the DLL version of the library with an Unreal plugin, exposes a blueprint class for interacting with it, and uses that to make a very simple UI. Run `python build.py unreal` in the root directory to build the correct library files and place them in their respective folders.
|
||||||
|
|
||||||
### Using the Unreal Engine plugin with your own project
|
|
||||||
|
|
||||||
To use the Rich Presense plugin with Unreal Engine Projects:
|
|
||||||
|
|
||||||
1. Download the latest [release](https://github.com/discordapp/discord-rpc/releases) for each operating system you are targeting and the zipped source code
|
|
||||||
2. In the source code zip, copy the UE plugin—`examples/unrealstatus/Plugins/discordrpc`—to your project's plugin directory
|
|
||||||
3. At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create an `Include` folder and copy `discord_rpc.h` and `discord_register.h` to it from the zip
|
|
||||||
4. Follow the steps below for each OS
|
|
||||||
5. Build your UE4 project
|
|
||||||
6. Launch the editor, and enable the Discord plugin.
|
|
||||||
|
|
||||||
#### Windows
|
|
||||||
|
|
||||||
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Win64` folder
|
|
||||||
- Copy `lib/discord-rpc.lib` and `bin/discord-rpc.dll` from `[RELEASE_ZIP]/win64-dynamic` to the `Win64` folder
|
|
||||||
|
|
||||||
#### Mac
|
|
||||||
|
|
||||||
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Mac` folder
|
|
||||||
- Copy `libdiscord-rpc.dylib` from `[RELEASE_ZIP]/osx-dynamic/lib` to the `Mac` folder
|
|
||||||
|
|
||||||
#### Linux
|
|
||||||
|
|
||||||
- At `[YOUR_UE_PROJECT]/Plugins/discordrpc/source/ThirdParty/DiscordRpcLibrary/`, create a `Linux` folder
|
|
||||||
- Inside, create another folder `x86_64-unknown-linux-gnu`
|
|
||||||
- Copy `libdiscord-rpc.so` from `[RELEASE_ZIP]/linux-dynamic/lib` to `Linux/x86_64-unknown-linux-gnu`
|
|
||||||
|
|
||||||
## Wrappers and Implementations
|
## Wrappers and Implementations
|
||||||
|
|
||||||
Below is a table of unofficial, community-developed wrappers for and implementations of Rich Presence in various languages. If you would like to have yours added, please make a pull request adding your repository to the table. The repository should include:
|
Below is a table of unofficial, community-developed wrappers for and implementations of Rich Presence in various languages. If you would like to have yours added, please make a pull request adding your repository to the table. The repository should include:
|
||||||
|
@ -7,29 +7,31 @@ using AOT;
|
|||||||
public class DiscordRpc
|
public class DiscordRpc
|
||||||
{
|
{
|
||||||
[MonoPInvokeCallback(typeof(OnReadyInfo))]
|
[MonoPInvokeCallback(typeof(OnReadyInfo))]
|
||||||
public static void ReadyCallback(ref DiscordUser connectedUser) { }
|
public static void ReadyCallback(ref DiscordUser connectedUser) { Callbacks.readyCallback(ref connectedUser); }
|
||||||
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
|
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(OnDisconnectedInfo))]
|
[MonoPInvokeCallback(typeof(OnDisconnectedInfo))]
|
||||||
public static void DisconnectedCallback(int errorCode, string message) { }
|
public static void DisconnectedCallback(int errorCode, string message) { Callbacks.disconnectedCallback(errorCode, message); }
|
||||||
public delegate void OnDisconnectedInfo(int errorCode, string message);
|
public delegate void OnDisconnectedInfo(int errorCode, string message);
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(OnErrorInfo))]
|
[MonoPInvokeCallback(typeof(OnErrorInfo))]
|
||||||
public static void ErrorCallback(int errorCode, string message) { }
|
public static void ErrorCallback(int errorCode, string message) { Callbacks.errorCallback(errorCode, message); }
|
||||||
public delegate void OnErrorInfo(int errorCode, string message);
|
public delegate void OnErrorInfo(int errorCode, string message);
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(OnJoinInfo))]
|
[MonoPInvokeCallback(typeof(OnJoinInfo))]
|
||||||
public static void JoinCallback(string secret) { }
|
public static void JoinCallback(string secret) { Callbacks.joinCallback(secret); }
|
||||||
public delegate void OnJoinInfo(string secret);
|
public delegate void OnJoinInfo(string secret);
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(OnSpectateInfo))]
|
[MonoPInvokeCallback(typeof(OnSpectateInfo))]
|
||||||
public static void SpectateCallback(string secret) { }
|
public static void SpectateCallback(string secret) { Callbacks.spectateCallback(secret); }
|
||||||
public delegate void OnSpectateInfo(string secret);
|
public delegate void OnSpectateInfo(string secret);
|
||||||
|
|
||||||
[MonoPInvokeCallback(typeof(OnRequestInfo))]
|
[MonoPInvokeCallback(typeof(OnRequestInfo))]
|
||||||
public static void RequestCallback(ref DiscordUser request) { }
|
public static void RequestCallback(ref DiscordUser request) { Callbacks.requestCallback(ref request); }
|
||||||
public delegate void OnRequestInfo(ref DiscordUser request);
|
public delegate void OnRequestInfo(ref DiscordUser request);
|
||||||
|
|
||||||
|
static EventHandlers Callbacks { get; set; }
|
||||||
|
|
||||||
public struct EventHandlers
|
public struct EventHandlers
|
||||||
{
|
{
|
||||||
public OnReadyInfo readyCallback;
|
public OnReadyInfo readyCallback;
|
||||||
@ -76,8 +78,23 @@ public class DiscordRpc
|
|||||||
Ignore = 2
|
Ignore = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId, int pipe = 0)
|
||||||
|
{
|
||||||
|
Callbacks = handlers;
|
||||||
|
|
||||||
|
EventHandlers staticEventHandlers = new EventHandlers();
|
||||||
|
staticEventHandlers.readyCallback += DiscordRpc.ReadyCallback;
|
||||||
|
staticEventHandlers.disconnectedCallback += DiscordRpc.DisconnectedCallback;
|
||||||
|
staticEventHandlers.errorCallback += DiscordRpc.ErrorCallback;
|
||||||
|
staticEventHandlers.joinCallback += DiscordRpc.JoinCallback;
|
||||||
|
staticEventHandlers.spectateCallback += DiscordRpc.SpectateCallback;
|
||||||
|
staticEventHandlers.requestCallback += DiscordRpc.RequestCallback;
|
||||||
|
|
||||||
|
InitializeInternal(applicationId, ref staticEventHandlers, autoRegister, optionalSteamId, pipe);
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
|
static extern void InitializeInternal(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId, int pipe);
|
||||||
|
|
||||||
[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();
|
||||||
|
@ -129,7 +129,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);
|
Discord_Initialize(APPLICATION_ID, &handlers, 1, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gameLoop()
|
static void gameLoop()
|
||||||
|
@ -82,7 +82,8 @@ 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;
|
||||||
@ -102,7 +103,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());
|
(const char*)appId.Get(), &handlers, autoRegister, (const char*)steamId.Get(), pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDiscordRpc::Shutdown()
|
void UDiscordRpc::Shutdown()
|
||||||
|
@ -99,7 +99,8 @@ 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"),
|
||||||
|
@ -64,7 +64,8 @@ typedef struct DiscordEventHandlers {
|
|||||||
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 */
|
||||||
|
@ -72,6 +72,11 @@ if(UNIX)
|
|||||||
|
|
||||||
add_library(discord-rpc ${BASE_RPC_SRC})
|
add_library(discord-rpc ${BASE_RPC_SRC})
|
||||||
target_link_libraries(discord-rpc PUBLIC pthread)
|
target_link_libraries(discord-rpc PUBLIC pthread)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
target_link_libraries(discord-rpc PRIVATE "-framework AppKit, -mmacosx-version-min=10.10")
|
||||||
|
endif (APPLE)
|
||||||
|
|
||||||
target_compile_options(discord-rpc PRIVATE
|
target_compile_options(discord-rpc PRIVATE
|
||||||
-g
|
-g
|
||||||
-Wall
|
-Wall
|
||||||
|
@ -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();
|
bool Open(int pipe);
|
||||||
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);
|
||||||
|
@ -49,7 +49,7 @@ static const char* GetTempPath()
|
|||||||
c = nullptr;
|
c = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseConnection::Open()
|
bool BaseConnection::Open(int pipe)
|
||||||
{
|
{
|
||||||
const char* tempPath = GetTempPath();
|
const char* tempPath = GetTempPath();
|
||||||
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
|
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
|
||||||
@ -62,8 +62,7 @@ bool BaseConnection::Open()
|
|||||||
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));
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#define NOIME
|
#define NOIME
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
int GetProcessId()
|
int GetProcessId()
|
||||||
{
|
{
|
||||||
@ -30,11 +31,11 @@ static BaseConnectionWin Connection;
|
|||||||
c = nullptr;
|
c = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseConnection::Open()
|
bool BaseConnection::Open(int pipe)
|
||||||
{
|
{
|
||||||
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] = L'0';
|
pipeName[pipeDigit] += pipe;
|
||||||
auto self = reinterpret_cast<BaseConnectionWin*>(this);
|
auto self = reinterpret_cast<BaseConnectionWin*>(this);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
self->pipe = ::CreateFileW(
|
self->pipe = ::CreateFileW(
|
||||||
|
@ -273,7 +273,8 @@ 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) {
|
||||||
@ -308,7 +309,7 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection = RpcConnection::Create(applicationId);
|
Connection = RpcConnection::Create(applicationId, pipe);
|
||||||
Connection->onConnect = [](JsonDocument& readyMessage) {
|
Connection->onConnect = [](JsonDocument& readyMessage) {
|
||||||
Discord_UpdateHandlers(&QueuedHandlers);
|
Discord_UpdateHandlers(&QueuedHandlers);
|
||||||
if (QueuedPresence.length > 0) {
|
if (QueuedPresence.length > 0) {
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
static const int RpcVersion = 1;
|
static const int RpcVersion = 1;
|
||||||
static RpcConnection Instance;
|
static RpcConnection Instance;
|
||||||
|
|
||||||
/*static*/ RpcConnection* RpcConnection::Create(const char* applicationId)
|
/*static*/ RpcConnection* RpcConnection::Create(const char* applicationId, int pipe)
|
||||||
{
|
{
|
||||||
Instance.connection = BaseConnection::Create();
|
Instance.connection = BaseConnection::Create();
|
||||||
StringCopy(Instance.appId, applicationId);
|
StringCopy(Instance.appId, applicationId);
|
||||||
|
Instance.pipe = pipe;
|
||||||
return &Instance;
|
return &Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ void RpcConnection::Open()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == State::Disconnected && !connection->Open()) {
|
if (state == State::Disconnected && !connection->Open(Instance.pipe)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,11 +43,12 @@ 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);
|
static RpcConnection* Create(const char* applicationId, int pipe);
|
||||||
static void Destroy(RpcConnection*&);
|
static void Destroy(RpcConnection*&);
|
||||||
|
|
||||||
inline bool IsOpen() const { return state == State::Connected; }
|
inline bool IsOpen() const { return state == State::Connected; }
|
||||||
|
Reference in New Issue
Block a user