Compare commits

..

No commits in common. "master" and "v3.3.0" have entirely different histories.

19 changed files with 169 additions and 315 deletions

View File

@ -43,5 +43,5 @@ before_install:
script: script:
- mkdir build - mkdir build
- cd build - cd build
- cmake -DCLANG_FORMAT_SUFFIX=$CLANG_FORMAT_SUFFIX -DWARNINGS_AS_ERRORS=On --config Release .. - cmake -DCLANG_FORMAT_SUFFIX=$CLANG_FORMAT_SUFFIX --config Release ..
- cmake --build . -- -j2 - cmake --build . -- -j2

View File

@ -1,11 +1,5 @@
# 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
@ -21,64 +15,6 @@ 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
If you're a Unity developer looking to integrate Rich Presence into your game, follow this simple guide to get started towards success:
1. Download the DLLs for any platform that you need from [our releases](https://github.com/discordapp/discord-rpc/releases)
2. In your Unity project, create a `Plugins` folder inside your `Assets` folder if you don't already have one
3. Copy the file `DiscordRpc.cs` from [here](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordRpc.cs) into your `Assets` folder. This is basically your header file for the SDK
We've got our `Plugins` folder ready, so let's get platform-specific!
#### Windows
4. Create `x86` and `x86_64` folders inside `Assets/Plugins/`
5. Copy `discord-rpc-win/win64-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86_64/`
6. Copy `discord-rpc-win/win32-dynamic/bin/discord-rpc.dll` to `Assets/Plugins/x86/`
7. Click on both DLLs and make sure they are targetting the correct architectures in the Unity editor properties pane
8. Done!
#### MacOS
4. Copy `discord-rpc-osx/osx-dynamic/lib/libdiscord-rpc.dylib` to `Assets/Plugins/`
5. Rename `libdiscord-rpc.dylib` to `discord-rpc.bundle`
6. Done!
#### Linux
4. Copy `discord-rpc-linux/linux-dynamic-lib/libdiscord-rpc.so` to `Assets/Plugins/`
5. Done!
You're ready to roll! For code examples on how to interact with the SDK using the `DiscordRpc.cs` header file, check out [our example](https://github.com/discordapp/discord-rpc/blob/master/examples/button-clicker/Assets/DiscordController.cs)
### From package ### From package
Download a release package for your platform(s) -- they have subdirs with various prebuilt options, select the one you need add `/include` to your compile includes, `/lib` to your linker paths, and link with `discord-rpc`. For the dynamically linked builds, you'll need to ship the associated file along with your game. Download a release package for your platform(s) -- they have subdirs with various prebuilt options, select the one you need add `/include` to your compile includes, `/lib` to your linker paths, and link with `discord-rpc`. For the dynamically linked builds, you'll need to ship the associated file along with your game.
@ -90,7 +26,6 @@ First-eth, you'll want `CMake`. There's a few different ways to install it on yo
To make sure it's installed correctly, type `cmake --version` into your flavor of terminal/cmd. If you get a response with a version number, you're good to go! To make sure it's installed correctly, type `cmake --version` into your flavor of terminal/cmd. If you get a response with a version number, you're good to go!
There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for you; Sometimes I use it like this: There's a [CMake](https://cmake.org/download/) file that should be able to generate the lib for you; Sometimes I use it like this:
```sh ```sh
cd <path to discord-rpc> cd <path to discord-rpc>
mkdir build mkdir build
@ -98,7 +33,6 @@ There's a [CMake](https://cmake.org/download/) file that should be able to gener
cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to> cmake .. -DCMAKE_INSTALL_PREFIX=<path to install discord-rpc to>
cmake --build . --config Release --target install cmake --build . --config Release --target install
``` ```
There is a wrapper build script `build.py` that runs `cmake` with a few different options. There is a wrapper build script `build.py` that runs `cmake` with a few different options.
Usually, I run `build.py` to get things started, then use the generated project files as I work on things. It does depend on `click` library, so do a quick `pip install click` to make sure you have it if you want to run `build.py`. Usually, I run `build.py` to get things started, then use the generated project files as I work on things. It does depend on `click` library, so do a quick `pip install click` to make sure you have it if you want to run `build.py`.
@ -106,21 +40,20 @@ Usually, I run `build.py` to get things started, then use the generated project
There are some CMake options you might care about: There are some CMake options you might care about:
| flag | default | does | | flag | default | does |
| ---------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | |------|---------|------|
| `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself. | | `ENABLE_IO_THREAD` | `ON` | When enabled, we start up a thread to do io processing, if disabled you should call `Discord_UpdateConnection` yourself.
| `USE_STATIC_CRT` | `OFF` | (Windows) Enable to statically link the CRT, avoiding requiring users install the redistributable package. (The prebuilt binaries enable this option) | | `USE_STATIC_CRT` | `OFF` | (Windows) Enable to statically link the CRT, avoiding requiring users install the redistributable package. (The prebuilt binaries enable this option)
| [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html) | `OFF` | Build library as a DLL | | [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/v3.7/variable/BUILD_SHARED_LIBS.html) | `OFF` | Build library as a DLL
| `WARNINGS_AS_ERRORS` | `OFF` | When enabled, compiles with `-Werror` (on \*nix platforms). |
## Continuous Builds ## Continuous Builds
Why do we have three of these? Three times the fun! Why do we have three of these? Three times the fun!
| CI | badge | | CI | badge |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | |----|-------|
| TravisCI | [![Build status](https://travis-ci.org/discordapp/discord-rpc.svg?branch=master)](https://travis-ci.org/discordapp/discord-rpc) | | TravisCI | [![Build status](https://travis-ci.org/discordapp/discord-rpc.svg?branch=master)](https://travis-ci.org/discordapp/discord-rpc)
| AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/qvkoc0w1c4f4b8tj?svg=true)](https://ci.appveyor.com/project/crmarsh/discord-rpc) | | AppVeyor | [![Build status](https://ci.appveyor.com/api/projects/status/qvkoc0w1c4f4b8tj?svg=true)](https://ci.appveyor.com/project/crmarsh/discord-rpc)
| Buildkite (internal) | [![Build status](https://badge.buildkite.com/e103d79d247f6776605a15246352a04b8fd83d69211b836111.svg)](https://buildkite.com/discord/discord-rpc) | | Buildkite (internal) | [![Build status](https://badge.buildkite.com/e103d79d247f6776605a15246352a04b8fd83d69211b836111.svg)](https://buildkite.com/discord/discord-rpc)
## Sample: send-presence ## Sample: send-presence
@ -145,14 +78,10 @@ Below is a table of unofficial, community-developed wrappers for and implementat
###### Rich Presence Wrappers and Implementations ###### Rich Presence Wrappers and Implementations
| Name | Language | | Name | Language |
| ------------------------------------------------------------------------- | --------------------------------- | |------|----------|
| [Discord RPC C#](https://github.com/Lachee/discord-rpc-csharp) | C# | | [discord-rpc.jar](https://github.com/Vatuu/discord-rpc "Discord-RPC.jar") | Java |
| [Discord RPC D](https://github.com/voidblaster/discord-rpc-d) | [D](https://dlang.org/) |
| [discord-rpc.jar](https://github.com/Vatuu/discord-rpc 'Discord-RPC.jar') | Java |
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java | | [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java | | [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript | | [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
| [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) | | [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) |
| [lua-discordRPC](https://github.com/pfirsich/lua-discordRPC) | LuaJIT (FFI) |
| [pypresence](https://github.com/qwertyquerty/pypresence) | [Python](https://python.org/) |
| [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.org) | | [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.org) |

View File

@ -30,7 +30,7 @@ INSTALL_ROOT = os.path.join(SCRIPT_PATH, 'builds', 'install')
def get_signtool(): def get_signtool():
""" get path to code signing tool """ """ get path to code signing tool """
if PLATFORM == 'win': if PLATFORM == 'win':
sdk_dir = 'c:\\Program Files (x86)\\Windows Kits\\10' # os.environ['WindowsSdkDir'] sdk_dir = os.environ['WindowsSdkDir']
return os.path.join(sdk_dir, 'bin', 'x86', 'signtool.exe') return os.path.join(sdk_dir, 'bin', 'x86', 'signtool.exe')
elif PLATFORM == 'osx': elif PLATFORM == 'osx':
return '/usr/bin/codesign' return '/usr/bin/codesign'
@ -70,7 +70,14 @@ def cli(ctx, clean):
@click.pass_context @click.pass_context
def unity(ctx): def unity(ctx):
""" build just dynamic libs for use in unity project """ """ build just dynamic libs for use in unity project """
ctx.invoke(libs, clean=False, static=False, shared=True, skip_formatter=True, just_release=True) ctx.invoke(
libs,
clean=False,
static=False,
shared=True,
skip_formatter=True,
just_release=True
)
BUILDS = [] BUILDS = []
click.echo('--- Copying libs and header into unity example') click.echo('--- Copying libs and header into unity example')
@ -90,8 +97,7 @@ def unity(ctx):
LIBRARY_NAME = 'discord-rpc.bundle' LIBRARY_NAME = 'discord-rpc.bundle'
BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src') BUILD_BASE_PATH = os.path.join(SCRIPT_PATH, 'builds', 'osx-dynamic', 'src')
UNITY_DLL_PATH = UNITY_PROJECT_PATH UNITY_DLL_PATH = UNITY_PROJECT_PATH
os.rename( os.rename(os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.dylib'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.bundle'))
os.path.join(BUILD_BASE_PATH, 'libdiscord-rpc.dylib'), os.path.join(BUILD_BASE_PATH, 'discord-rpc.bundle'))
BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH}) BUILDS.append({BUILD_BASE_PATH: UNITY_DLL_PATH})
@ -116,7 +122,14 @@ def unity(ctx):
@click.pass_context @click.pass_context
def unreal(ctx): def unreal(ctx):
""" build libs and copy them into the unreal project """ """ build libs and copy them into the unreal project """
ctx.invoke(libs, clean=False, static=False, shared=True, skip_formatter=True, just_release=True) ctx.invoke(
libs,
clean=False,
static=False,
shared=True,
skip_formatter=True,
just_release=True
)
BUILDS = [] BUILDS = []
click.echo('--- Copying libs and header into unreal example') click.echo('--- Copying libs and header into unreal example')
@ -165,7 +178,11 @@ def build_lib(build_name, generator, options, just_release):
mkdir_p(build_path) mkdir_p(build_path)
mkdir_p(install_path) mkdir_p(install_path)
with cd(build_path): with cd(build_path):
initial_cmake = ['cmake', SCRIPT_PATH, '-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)] initial_cmake = [
'cmake',
SCRIPT_PATH,
'-DCMAKE_INSTALL_PREFIX=%s' % os.path.join('..', 'install', build_name)
]
if generator: if generator:
initial_cmake.extend(['-G', generator]) initial_cmake.extend(['-G', generator])
for key in options: for key in options:
@ -207,28 +224,22 @@ def sign():
sign_command_base = [ sign_command_base = [
tool, tool,
'sign', 'sign',
'/n', '/n', 'Discord Inc.',
'Discord Inc.',
'/a', '/a',
'/tr', '/tr', 'http://timestamp.digicert.com/rfc3161',
'http://timestamp.digicert.com/rfc3161',
'/as', '/as',
'/td', '/td', 'sha256',
'sha256', '/fd', 'sha256',
'/fd',
'sha256',
] ]
elif PLATFORM == 'osx': elif PLATFORM == 'osx':
signable_extensions.add('.dylib') signable_extensions.add('.dylib')
sign_command_base = [ sign_command_base = [
tool, tool,
'--keychain', '--keychain', os.path.expanduser('~/Library/Keychains/login.keychain'),
os.path.expanduser('~/Library/Keychains/login.keychain'),
'-vvvv', '-vvvv',
'--deep', '--deep',
'--force', '--force',
'--sign', '--sign', 'Developer ID Application: Hammer & Chisel Inc. (53Q6R32WPB)',
'Developer ID Application: Hammer & Chisel Inc. (53Q6R32WPB)',
] ]
else: else:
click.secho('Not signing things on this platform yet', fg='red') click.secho('Not signing things on this platform yet', fg='red')
@ -275,8 +286,6 @@ def libs(clean, static, shared, skip_formatter, just_release):
if IS_BUILD_MACHINE: if IS_BUILD_MACHINE:
just_release = True just_release = True
static_options['WARNINGS_AS_ERRORS'] = True
dynamic_options['WARNINGS_AS_ERRORS'] = True
if PLATFORM == 'win': if PLATFORM == 'win':
generator32 = 'Visual Studio 14 2015' generator32 = 'Visual Studio 14 2015'

View File

@ -14,6 +14,7 @@ public class DiscordController : MonoBehaviour
public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence(); public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
public string applicationId; public string applicationId;
public string optionalSteamId; public string optionalSteamId;
public int callbackCalls;
public int clickCounter; public int clickCounter;
public DiscordRpc.DiscordUser joinRequest; public DiscordRpc.DiscordUser joinRequest;
public UnityEngine.Events.UnityEvent onConnect; public UnityEngine.Events.UnityEvent onConnect;
@ -31,11 +32,6 @@ 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);
} }
@ -56,35 +52,41 @@ public class DiscordController : MonoBehaviour
public void ReadyCallback(ref DiscordRpc.DiscordUser connectedUser) public void ReadyCallback(ref DiscordRpc.DiscordUser connectedUser)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId)); Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId));
onConnect.Invoke(); onConnect.Invoke();
} }
public void DisconnectedCallback(int errorCode, string message) public void DisconnectedCallback(int errorCode, string message)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: disconnect {0}: {1}", errorCode, message)); Debug.Log(string.Format("Discord: disconnect {0}: {1}", errorCode, message));
onDisconnect.Invoke(); onDisconnect.Invoke();
} }
public void ErrorCallback(int errorCode, string message) public void ErrorCallback(int errorCode, string message)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: error {0}: {1}", errorCode, message)); Debug.Log(string.Format("Discord: error {0}: {1}", errorCode, message));
} }
public void JoinCallback(string secret) public void JoinCallback(string secret)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: join ({0})", secret)); Debug.Log(string.Format("Discord: join ({0})", secret));
onJoin.Invoke(secret); onJoin.Invoke(secret);
} }
public void SpectateCallback(string secret) public void SpectateCallback(string secret)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: spectate ({0})", secret)); Debug.Log(string.Format("Discord: spectate ({0})", secret));
onSpectate.Invoke(secret); onSpectate.Invoke(secret);
} }
public void RequestCallback(ref DiscordRpc.DiscordUser request) public void RequestCallback(ref DiscordRpc.DiscordUser request)
{ {
++callbackCalls;
Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId)); Debug.Log(string.Format("Discord: join request {0}#{1}: {2}", request.username, request.discriminator, request.userId));
joinRequest = request; joinRequest = request;
onJoinRequest.Invoke(request); onJoinRequest.Invoke(request);
@ -102,8 +104,10 @@ public class DiscordController : MonoBehaviour
void OnEnable() void OnEnable()
{ {
Debug.Log("Discord: init"); Debug.Log("Discord: init");
callbackCalls = 0;
handlers = new DiscordRpc.EventHandlers(); handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback += ReadyCallback; handlers.readyCallback = ReadyCallback;
handlers.disconnectedCallback += DisconnectedCallback; handlers.disconnectedCallback += DisconnectedCallback;
handlers.errorCallback += ErrorCallback; handlers.errorCallback += ErrorCallback;
handlers.joinCallback += JoinCallback; handlers.joinCallback += JoinCallback;

View File

@ -2,44 +2,35 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using AOT;
public class DiscordRpc public class DiscordRpc
{ {
[MonoPInvokeCallback(typeof(OnReadyInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void ReadyCallback(ref DiscordUser connectedUser) { Callbacks.readyCallback(ref connectedUser); } public delegate void ReadyCallback(ref DiscordUser connectedUser);
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
[MonoPInvokeCallback(typeof(OnDisconnectedInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void DisconnectedCallback(int errorCode, string message) { Callbacks.disconnectedCallback(errorCode, message); } public delegate void DisconnectedCallback(int errorCode, string message);
public delegate void OnDisconnectedInfo(int errorCode, string message);
[MonoPInvokeCallback(typeof(OnErrorInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void ErrorCallback(int errorCode, string message) { Callbacks.errorCallback(errorCode, message); } public delegate void ErrorCallback(int errorCode, string message);
public delegate void OnErrorInfo(int errorCode, string message);
[MonoPInvokeCallback(typeof(OnJoinInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void JoinCallback(string secret) { Callbacks.joinCallback(secret); } public delegate void JoinCallback(string secret);
public delegate void OnJoinInfo(string secret);
[MonoPInvokeCallback(typeof(OnSpectateInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void SpectateCallback(string secret) { Callbacks.spectateCallback(secret); } public delegate void SpectateCallback(string secret);
public delegate void OnSpectateInfo(string secret);
[MonoPInvokeCallback(typeof(OnRequestInfo))] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public static void RequestCallback(ref DiscordUser request) { Callbacks.requestCallback(ref request); } public delegate void RequestCallback(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 ReadyCallback readyCallback;
public OnDisconnectedInfo disconnectedCallback; public DisconnectedCallback disconnectedCallback;
public OnErrorInfo errorCallback; public ErrorCallback errorCallback;
public OnJoinInfo joinCallback; public JoinCallback joinCallback;
public OnSpectateInfo spectateCallback; public SpectateCallback spectateCallback;
public OnRequestInfo requestCallback; public RequestCallback requestCallback;
} }
[Serializable, StructLayout(LayoutKind.Sequential)] [Serializable, StructLayout(LayoutKind.Sequential)]
@ -56,7 +47,6 @@ 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 */
@ -79,29 +69,8 @@ public class DiscordRpc
Ignore = 2 Ignore = 2
} }
public enum PartyPrivacy
{
Private = 0,
Public = 1
}
public static void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId)
{
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);
}
[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); public static extern void Initialize(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();
@ -144,7 +113,6 @@ 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 */
@ -161,21 +129,20 @@ public class DiscordRpc
FreeMem(); FreeMem();
} }
_presence.state = StrToPtr(state); _presence.state = StrToPtr(state, 128);
_presence.details = StrToPtr(details); _presence.details = StrToPtr(details, 128);
_presence.startTimestamp = startTimestamp; _presence.startTimestamp = startTimestamp;
_presence.endTimestamp = endTimestamp; _presence.endTimestamp = endTimestamp;
_presence.largeImageKey = StrToPtr(largeImageKey); _presence.largeImageKey = StrToPtr(largeImageKey, 32);
_presence.largeImageText = StrToPtr(largeImageText); _presence.largeImageText = StrToPtr(largeImageText, 128);
_presence.smallImageKey = StrToPtr(smallImageKey); _presence.smallImageKey = StrToPtr(smallImageKey, 32);
_presence.smallImageText = StrToPtr(smallImageText); _presence.smallImageText = StrToPtr(smallImageText, 128);
_presence.partyId = StrToPtr(partyId); _presence.partyId = StrToPtr(partyId, 128);
_presence.partySize = partySize; _presence.partySize = partySize;
_presence.partyMax = partyMax; _presence.partyMax = partyMax;
_presence.partyPrivacy = (int)partyPrivacy; _presence.matchSecret = StrToPtr(matchSecret, 128);
_presence.matchSecret = StrToPtr(matchSecret); _presence.joinSecret = StrToPtr(joinSecret, 128);
_presence.joinSecret = StrToPtr(joinSecret); _presence.spectateSecret = StrToPtr(spectateSecret, 128);
_presence.spectateSecret = StrToPtr(spectateSecret);
_presence.instance = instance; _presence.instance = instance;
return _presence; return _presence;
@ -185,18 +152,16 @@ public class DiscordRpc
/// Returns a pointer to a representation of the given string with a size of maxbytes /// Returns a pointer to a representation of the given string with a size of maxbytes
/// </summary> /// </summary>
/// <param name="input">String to convert</param> /// <param name="input">String to convert</param>
/// <param name="maxbytes">Max number of bytes to use</param>
/// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns> /// <returns>Pointer to the UTF-8 representation of <see cref="input"/></returns>
private IntPtr StrToPtr(string input) private IntPtr StrToPtr(string input, int maxbytes)
{ {
if (string.IsNullOrEmpty(input)) return IntPtr.Zero; if (string.IsNullOrEmpty(input)) return IntPtr.Zero;
var convbytecnt = Encoding.UTF8.GetByteCount(input); var convstr = StrClampBytes(input, maxbytes);
var buffer = Marshal.AllocHGlobal(convbytecnt + 1); var convbytecnt = Encoding.UTF8.GetByteCount(convstr);
for (int i = 0; i < convbytecnt + 1; i++) var buffer = Marshal.AllocHGlobal(convbytecnt);
{
Marshal.WriteByte(buffer, i, 0);
}
_buffers.Add(buffer); _buffers.Add(buffer);
Marshal.Copy(Encoding.UTF8.GetBytes(input), 0, buffer, convbytecnt); Marshal.Copy(Encoding.UTF8.GetBytes(convstr), 0, buffer, convbytecnt);
return buffer; return buffer;
} }
@ -216,6 +181,30 @@ public class DiscordRpc
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str)); return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(str));
} }
/// <summary>
/// Clamp the string to the given byte length preserving null termination
/// </summary>
/// <param name="toclamp">string to clamp</param>
/// <param name="maxbytes">max bytes the resulting string should have (including null termination)</param>
/// <returns>null terminated string with a byte length less or equal to <see cref="maxbytes"/></returns>
private static string StrClampBytes(string toclamp, int maxbytes)
{
var str = StrToUtf8NullTerm(toclamp);
var strbytes = Encoding.UTF8.GetBytes(str);
if (strbytes.Length <= maxbytes)
{
return str;
}
var newstrbytes = new byte[] { };
Array.Copy(strbytes, 0, newstrbytes, 0, maxbytes - 1);
newstrbytes[newstrbytes.Length - 1] = 0;
newstrbytes[newstrbytes.Length - 2] = 0;
return Encoding.UTF8.GetString(newstrbytes);
}
/// <summary> /// <summary>
/// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/> /// Free the allocated memory for conversion to <see cref="RichPresenceStruct"/>
/// </summary> /// </summary>

View File

@ -47,7 +47,6 @@ 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";

View File

@ -12,26 +12,19 @@ void FDiscordRpcModule::StartupModule()
#if defined(DISCORD_DYNAMIC_LIB) #if defined(DISCORD_DYNAMIC_LIB)
// Get the base directory of this plugin // Get the base directory of this plugin
FString BaseDir = IPluginManager::Get().FindPlugin("DiscordRpc")->GetBaseDir(); FString BaseDir = IPluginManager::Get().FindPlugin("DiscordRpc")->GetBaseDir();
const FString SDKDir = const FString SDKDir = FPaths::Combine(*BaseDir, TEXT("Source"), TEXT("ThirdParty"), TEXT("DiscordRpcLibrary"));
FPaths::Combine(*BaseDir, TEXT("Source"), TEXT("ThirdParty"), TEXT("DiscordRpcLibrary"));
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
const FString LibName = TEXT("discord-rpc"); const FString LibName = TEXT("discord-rpc");
const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Win64")); const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Win64"));
if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) { if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) {
FMessageDialog::Open( FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT(LOCTEXT_NAMESPACE, "Failed to load DiscordRpc plugin. Plug-in will not be functional."));
EAppMsgType::Ok,
LOCTEXT(LOCTEXT_NAMESPACE,
"Failed to load DiscordRpc plugin. Plug-in will not be functional."));
FreeDependency(DiscordRpcLibraryHandle); FreeDependency(DiscordRpcLibraryHandle);
} }
#elif PLATFORM_MAC #elif PLATFORM_MAC
const FString LibName = TEXT("libdiscord-rpc"); const FString LibName = TEXT("libdiscord-rpc");
const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Mac")); const FString LibDir = FPaths::Combine(*SDKDir, TEXT("Mac"));
if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) { if (!LoadDependency(LibDir, LibName, DiscordRpcLibraryHandle)) {
FMessageDialog::Open( FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT(LOCTEXT_NAMESPACE, "Failed to load DiscordRpc plugin. Plug-in will not be functional."));
EAppMsgType::Ok,
LOCTEXT(LOCTEXT_NAMESPACE,
"Failed to load DiscordRpc plugin. Plug-in will not be functional."));
FreeDependency(DiscordRpcLibraryHandle); FreeDependency(DiscordRpcLibraryHandle);
} }
#endif #endif
@ -56,7 +49,8 @@ bool FDiscordRpcModule::LoadDependency(const FString& Dir, const FString& Name,
Handle = FPlatformProcess::GetDllHandle(*Path); Handle = FPlatformProcess::GetDllHandle(*Path);
if (Handle == nullptr) { if (Handle == nullptr)
{
return false; return false;
} }
@ -65,7 +59,8 @@ bool FDiscordRpcModule::LoadDependency(const FString& Dir, const FString& Name,
void FDiscordRpcModule::FreeDependency(void*& Handle) void FDiscordRpcModule::FreeDependency(void*& Handle)
{ {
if (Handle != nullptr) { if (Handle != nullptr)
{
FPlatformProcess::FreeDllHandle(Handle); FPlatformProcess::FreeDllHandle(Handle);
Handle = nullptr; Handle = nullptr;
} }

View File

@ -153,7 +153,6 @@ 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

@ -24,26 +24,6 @@ struct FDiscordUserData {
FString avatar; FString avatar;
}; };
/**
* Valid response codes for Respond function
*/
UENUM(BlueprintType)
enum class EDiscordJoinResponseCodes : uint8
{
DISCORD_REPLY_NO UMETA(DisplayName="No"),
DISCORD_REPLY_YES UMETA(DisplayName="Yes"),
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);
@ -87,8 +67,6 @@ 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;

View File

@ -35,7 +35,6 @@ 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 */
@ -61,8 +60,6 @@ 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,

View File

@ -2,7 +2,6 @@ include_directories(${PROJECT_SOURCE_DIR}/include)
option(ENABLE_IO_THREAD "Start up a separate I/O thread, otherwise I'd need to call an update function" ON) option(ENABLE_IO_THREAD "Start up a separate I/O thread, otherwise I'd need to call an update function" ON)
option(USE_STATIC_CRT "Use /MT[d] for dynamic library" OFF) option(USE_STATIC_CRT "Use /MT[d] for dynamic library" OFF)
option(WARNINGS_AS_ERRORS "When enabled, compiles with `-Werror` (on *nix platforms)." OFF)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
@ -72,23 +71,12 @@ 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
-Wextra -Wextra
-Wpedantic -Wpedantic
) -Werror
if (${WARNINGS_AS_ERRORS})
target_compile_options(discord-rpc PRIVATE -Werror)
endif (${WARNINGS_AS_ERRORS})
target_compile_options(discord-rpc PRIVATE
-Wno-unknown-pragmas # pragma push thing doesn't work on clang -Wno-unknown-pragmas # pragma push thing doesn't work on clang
-Wno-old-style-cast # it's fine -Wno-old-style-cast # it's fine
-Wno-c++98-compat # that was almost 2 decades ago -Wno-c++98-compat # that was almost 2 decades ago

View File

@ -118,8 +118,5 @@ bool BaseConnection::Read(void* data, size_t length)
} }
Close(); Close();
} }
else if (res == 0) {
Close();
}
return res == (int)length; return res == (int)length;
} }

View File

@ -33,15 +33,13 @@ extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const
char exePath[1024]; char exePath[1024];
if (!command || !command[0]) { if (!command || !command[0]) {
ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath)); if (readlink("/proc/self/exe", exePath, sizeof(exePath)) <= 0) {
if (size <= 0 || size >= (ssize_t)sizeof(exePath)) {
return; return;
} }
exePath[size] = '\0';
command = exePath; command = exePath;
} }
const char* desktopFileFormat = "[Desktop Entry]\n" const char* destopFileFormat = "[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 +48,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), desktopFileFormat, applicationId, command, applicationId); desktopFile, sizeof(desktopFile), destopFileFormat, applicationId, command, applicationId);
if (fileLen <= 0) { if (fileLen <= 0) {
return; return;
} }
@ -93,8 +91,7 @@ extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const
} }
} }
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
const char* steamId)
{ {
char command[256]; char command[256];
sprintf(command, "xdg-open steam://rungameid/%s", steamId); sprintf(command, "xdg-open steam://rungameid/%s", steamId);

View File

@ -7,6 +7,7 @@
#define NOIME #define NOIME
#include <windows.h> #include <windows.h>
#include <psapi.h> #include <psapi.h>
#include <cwchar>
#include <cstdio> #include <cstdio>
/** /**
@ -19,22 +20,19 @@
* 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, ...)
{ {
HRESULT ret; HRESULT ret;
va_list va; va_list va;
va_start(va, pszFormat); va_start(va, pszFormat);
cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault othervise
// othervise
ret = vsnwprintf(pszDest, cbDest, pszFormat, va); ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned
va_end(va); va_end(va);
return ret; return ret;
} }
#else #else
#include <cwchar>
#include <strsafe.h> #include <strsafe.h>
#endif // __MINGW32__ #endif // __MINGW32__
@ -46,24 +44,17 @@ static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat,
#undefine RegSetKeyValueW #undefine RegSetKeyValueW
#endif #endif
#define RegSetKeyValueW regset #define RegSetKeyValueW regset
static LSTATUS regset(HKEY hkey, static LSTATUS regset(HKEY hkey, LPCWSTR subkey, LPCWSTR name, DWORD type, const void *data, DWORD len)
LPCWSTR subkey,
LPCWSTR name,
DWORD type,
const void* data,
DWORD len)
{ {
HKEY htkey = hkey, hsubkey = nullptr; HKEY htkey = hkey, hsubkey = nullptr;
LSTATUS ret; LSTATUS ret;
if (subkey && subkey[0]) { if (subkey && subkey[0])
if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) != {
ERROR_SUCCESS) if((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) != ERROR_SUCCESS) return ret;
return ret;
htkey = hsubkey; htkey = hsubkey;
} }
ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len); ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
if (hsubkey && hsubkey != hkey) if (hsubkey && hsubkey != hkey) RegCloseKey(hsubkey);
RegCloseKey(hsubkey);
return ret; return ret;
} }
@ -147,8 +138,7 @@ extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const
Discord_RegisterW(appId, wcommand); Discord_RegisterW(appId, wcommand);
} }
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
const char* steamId)
{ {
wchar_t appId[32]; wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);

View File

@ -54,7 +54,6 @@ static std::atomic_bool WasJustDisconnected{false};
static std::atomic_bool GotErrorMessage{false}; static std::atomic_bool GotErrorMessage{false};
static std::atomic_bool WasJoinGame{false}; static std::atomic_bool WasJoinGame{false};
static std::atomic_bool WasSpectateGame{false}; static std::atomic_bool WasSpectateGame{false};
static std::atomic_bool UpdatePresence{false};
static char JoinGameSecret[256]; static char JoinGameSecret[256];
static char SpectateGameSecret[256]; static char SpectateGameSecret[256];
static int LastErrorCode{0}; static int LastErrorCode{0};
@ -90,11 +89,10 @@ public:
keepRunning.store(true); keepRunning.store(true);
ioThread = std::thread([&]() { ioThread = std::thread([&]() {
const std::chrono::duration<int64_t, std::milli> maxWait{500LL}; const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
Discord_UpdateConnection();
while (keepRunning.load()) { while (keepRunning.load()) {
Discord_UpdateConnection();
std::unique_lock<std::mutex> lock(waitForIOMutex); std::unique_lock<std::mutex> lock(waitForIOMutex);
waitForIOActivity.wait_for(lock, maxWait); waitForIOActivity.wait_for(lock, maxWait);
Discord_UpdateConnection();
} }
}); });
} }
@ -120,7 +118,7 @@ public:
void Notify() {} void Notify() {}
}; };
#endif // DISCORD_DISABLE_IO_THREAD #endif // DISCORD_DISABLE_IO_THREAD
static IoThreadHolder* IoThread{nullptr}; static IoThreadHolder IoThread;
static void UpdateReconnectTime() static void UpdateReconnectTime()
{ {
@ -215,17 +213,17 @@ static void Discord_UpdateConnection(void)
} }
// writes // writes
if (UpdatePresence.exchange(false) && QueuedPresence.length) { if (QueuedPresence.length) {
QueuedMessage local; QueuedMessage local;
{ {
std::lock_guard<std::mutex> guard(PresenceMutex); std::lock_guard<std::mutex> guard(PresenceMutex);
local.Copy(QueuedPresence); local.Copy(QueuedPresence);
QueuedPresence.length = 0;
} }
if (!Connection->Write(local.buffer, local.length)) { if (!Connection->Write(local.buffer, local.length)) {
// if we fail to send, requeue // if we fail to send, requeue
std::lock_guard<std::mutex> guard(PresenceMutex); std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.Copy(local); QueuedPresence.Copy(local);
UpdatePresence.exchange(true);
} }
} }
@ -239,9 +237,7 @@ static void Discord_UpdateConnection(void)
static void SignalIOActivity() static void SignalIOActivity()
{ {
if (IoThread != nullptr) { IoThread.Notify();
IoThread->Notify();
}
} }
static bool RegisterForEvent(const char* evtName) static bool RegisterForEvent(const char* evtName)
@ -275,11 +271,6 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
int autoRegister, int autoRegister,
const char* optionalSteamId) const char* optionalSteamId)
{ {
IoThread = new (std::nothrow) IoThreadHolder();
if (IoThread == nullptr) {
return;
}
if (autoRegister) { if (autoRegister) {
if (optionalSteamId && optionalSteamId[0]) { if (optionalSteamId && optionalSteamId[0]) {
Discord_RegisterSteamGame(applicationId, optionalSteamId); Discord_RegisterSteamGame(applicationId, optionalSteamId);
@ -311,10 +302,6 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
Connection = RpcConnection::Create(applicationId); Connection = RpcConnection::Create(applicationId);
Connection->onConnect = [](JsonDocument& readyMessage) { Connection->onConnect = [](JsonDocument& readyMessage) {
Discord_UpdateHandlers(&QueuedHandlers); Discord_UpdateHandlers(&QueuedHandlers);
if (QueuedPresence.length > 0) {
UpdatePresence.exchange(true);
SignalIOActivity();
}
auto data = GetObjMember(&readyMessage, "data"); auto data = GetObjMember(&readyMessage, "data");
auto user = GetObjMember(data, "user"); auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id"); auto userId = GetStrMember(user, "id");
@ -340,11 +327,15 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
Connection->onDisconnect = [](int err, const char* message) { Connection->onDisconnect = [](int err, const char* message) {
LastDisconnectErrorCode = err; LastDisconnectErrorCode = err;
StringCopy(LastDisconnectErrorMessage, message); StringCopy(LastDisconnectErrorMessage, message);
{
std::lock_guard<std::mutex> guard(HandlerMutex);
Handlers = {};
}
WasJustDisconnected.exchange(true); WasJustDisconnected.exchange(true);
UpdateReconnectTime(); UpdateReconnectTime();
}; };
IoThread->Start(); IoThread.Start();
} }
extern "C" DISCORD_EXPORT void Discord_Shutdown(void) extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
@ -355,14 +346,7 @@ extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
Connection->onConnect = nullptr; Connection->onConnect = nullptr;
Connection->onDisconnect = nullptr; Connection->onDisconnect = nullptr;
Handlers = {}; Handlers = {};
QueuedPresence.length = 0; IoThread.Stop();
UpdatePresence.exchange(false);
if (IoThread != nullptr) {
IoThread->Stop();
delete IoThread;
IoThread = nullptr;
}
RpcConnection::Destroy(Connection); RpcConnection::Destroy(Connection);
} }
@ -372,7 +356,6 @@ extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence*
std::lock_guard<std::mutex> guard(PresenceMutex); std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.length = JsonWriteRichPresenceObj( QueuedPresence.length = JsonWriteRichPresenceObj(
QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence); QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
UpdatePresence.exchange(true);
} }
SignalIOActivity(); SignalIOActivity();
} }

View File

@ -26,9 +26,13 @@ void RpcConnection::Open()
return; return;
} }
if (state == State::Disconnected && !connection->Open()) { if (state == State::Disconnected) {
if (connection->Open()) {
}
else {
return; return;
} }
}
if (state == State::SentHandshake) { if (state == State::SentHandshake) {
JsonDocument message; JsonDocument message;

View File

@ -102,7 +102,8 @@ size_t JsonWriteRichPresenceObj(char* dest,
WriteKey(writer, "pid"); WriteKey(writer, "pid");
writer.Int(pid); writer.Int(pid);
if (presence != nullptr) { if (presence != nullptr)
{
WriteObject activity(writer, "activity"); WriteObject activity(writer, "activity");
WriteOptionalString(writer, "state", presence->state); WriteOptionalString(writer, "state", presence->state);
@ -134,7 +135,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->partyPrivacy) { presence->partyMax) {
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,11 +143,6 @@ 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]) ||