Compare commits
No commits in common. "master" and "v3.2.0" have entirely different histories.
@ -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
|
||||||
|
107
README.md
107
README.md
@ -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,29 +33,27 @@ 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`.
|
||||||
|
|
||||||
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 | [](https://travis-ci.org/discordapp/discord-rpc) |
|
| TravisCI | [](https://travis-ci.org/discordapp/discord-rpc)
|
||||||
| AppVeyor | [](https://ci.appveyor.com/project/crmarsh/discord-rpc) |
|
| AppVeyor | [](https://ci.appveyor.com/project/crmarsh/discord-rpc)
|
||||||
| Buildkite (internal) | [](https://buildkite.com/discord/discord-rpc) |
|
| Buildkite (internal) | [](https://buildkite.com/discord/discord-rpc)
|
||||||
|
|
||||||
## Sample: send-presence
|
## Sample: send-presence
|
||||||
|
|
||||||
@ -144,15 +77,11 @@ 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/) |
|
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
|
||||||
| [discord-rpc.jar](https://github.com/Vatuu/discord-rpc 'Discord-RPC.jar') | Java |
|
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
|
||||||
| [java-discord-rpc](https://github.com/MinnDevelopment/java-discord-rpc) | Java |
|
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
|
||||||
| [Discord-IPC](https://github.com/jagrosh/DiscordIPC) | Java |
|
| [drpc4k](https://github.com/Bluexin/drpc4k) | [Kotlin](https://kotlinlang.org/) |
|
||||||
| [Discord Rich Presence](https://npmjs.org/discord-rich-presence) | JavaScript |
|
| [SwordRPC](https://github.com/Azoy/SwordRPC) | [Swift](https://swift.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) |
|
|
||||||
|
49
build.py
49
build.py
@ -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'
|
||||||
|
@ -7,15 +7,16 @@ public class DiscordJoinEvent : UnityEngine.Events.UnityEvent<string> { }
|
|||||||
public class DiscordSpectateEvent : UnityEngine.Events.UnityEvent<string> { }
|
public class DiscordSpectateEvent : UnityEngine.Events.UnityEvent<string> { }
|
||||||
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.DiscordUser> { }
|
public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.JoinRequest> { }
|
||||||
|
|
||||||
public class DiscordController : MonoBehaviour
|
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.JoinRequest joinRequest;
|
||||||
public UnityEngine.Events.UnityEvent onConnect;
|
public UnityEngine.Events.UnityEvent onConnect;
|
||||||
public UnityEngine.Events.UnityEvent onDisconnect;
|
public UnityEngine.Events.UnityEvent onDisconnect;
|
||||||
public UnityEngine.Events.UnityEvent hasResponded;
|
public UnityEngine.Events.UnityEvent hasResponded;
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
@ -54,37 +50,43 @@ public class DiscordController : MonoBehaviour
|
|||||||
hasResponded.Invoke();
|
hasResponded.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadyCallback(ref DiscordRpc.DiscordUser connectedUser)
|
public void ReadyCallback()
|
||||||
{
|
{
|
||||||
Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId));
|
++callbackCalls;
|
||||||
|
Debug.Log("Discord: ready");
|
||||||
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.JoinRequest 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;
|
||||||
|
@ -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();
|
||||||
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 JoinRequest 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 */
|
||||||
@ -64,7 +54,7 @@ public class DiscordRpc
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct DiscordUser
|
public struct JoinRequest
|
||||||
{
|
{
|
||||||
public string userId;
|
public string userId;
|
||||||
public string username;
|
public string username;
|
||||||
@ -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>
|
||||||
|
@ -47,24 +47,19 @@ 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";
|
||||||
discordPresence.instance = 0;
|
discordPresence.instance = 0;
|
||||||
Discord_UpdatePresence(&discordPresence);
|
Discord_UpdatePresence(&discordPresence);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Discord_ClearPresence();
|
Discord_ClearPresence();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleDiscordReady(const DiscordUser* connectedUser)
|
static void handleDiscordReady(void)
|
||||||
{
|
{
|
||||||
printf("\nDiscord: connected to user %s#%s - %s\n",
|
printf("\nDiscord: ready\n");
|
||||||
connectedUser->username,
|
|
||||||
connectedUser->discriminator,
|
|
||||||
connectedUser->userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleDiscordDisconnected(int errcode, const char* message)
|
static void handleDiscordDisconnected(int errcode, const char* message)
|
||||||
@ -87,13 +82,13 @@ static void handleDiscordSpectate(const char* secret)
|
|||||||
printf("\nDiscord: spectate (%s)\n", secret);
|
printf("\nDiscord: spectate (%s)\n", secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleDiscordJoinRequest(const DiscordUser* request)
|
static void handleDiscordJoinRequest(const DiscordJoinRequest* request)
|
||||||
{
|
{
|
||||||
int response = -1;
|
int response = -1;
|
||||||
char yn[4];
|
char yn[4];
|
||||||
printf("\nDiscord: join request from %s#%s - %s\n",
|
printf("\nDiscord: join request from %s - %s - %s\n",
|
||||||
request->username,
|
request->username,
|
||||||
request->discriminator,
|
request->avatar,
|
||||||
request->userId);
|
request->userId);
|
||||||
do {
|
do {
|
||||||
printf("Accept? (y/n)");
|
printf("Accept? (y/n)");
|
||||||
@ -157,8 +152,7 @@ static void gameLoop()
|
|||||||
if (SendPresence) {
|
if (SendPresence) {
|
||||||
printf("Clearing presence information.\n");
|
printf("Clearing presence information.\n");
|
||||||
SendPresence = 0;
|
SendPresence = 0;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
printf("Restoring presence information.\n");
|
printf("Restoring presence information.\n");
|
||||||
SendPresence = 1;
|
SendPresence = 1;
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -6,22 +6,12 @@ DEFINE_LOG_CATEGORY(Discord)
|
|||||||
|
|
||||||
static UDiscordRpc* self = nullptr;
|
static UDiscordRpc* self = nullptr;
|
||||||
|
|
||||||
static void ReadyHandler(const DiscordUser* connectedUser)
|
static void ReadyHandler()
|
||||||
{
|
{
|
||||||
FDiscordUserData ud;
|
UE_LOG(Discord, Log, TEXT("Discord connected"));
|
||||||
ud.userId = ANSI_TO_TCHAR(connectedUser->userId);
|
|
||||||
ud.username = ANSI_TO_TCHAR(connectedUser->username);
|
|
||||||
ud.discriminator = ANSI_TO_TCHAR(connectedUser->discriminator);
|
|
||||||
ud.avatar = ANSI_TO_TCHAR(connectedUser->avatar);
|
|
||||||
UE_LOG(Discord,
|
|
||||||
Log,
|
|
||||||
TEXT("Discord connected to %s - %s#%s"),
|
|
||||||
*ud.userId,
|
|
||||||
*ud.username,
|
|
||||||
*ud.discriminator);
|
|
||||||
if (self) {
|
if (self) {
|
||||||
self->IsConnected = true;
|
self->IsConnected = true;
|
||||||
self->OnConnected.Broadcast(ud);
|
self->OnConnected.Broadcast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,27 +52,22 @@ static void SpectateGameHandler(const char* spectateSecret)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void JoinRequestHandler(const DiscordUser* request)
|
static void JoinRequestHandler(const DiscordJoinRequest* request)
|
||||||
{
|
{
|
||||||
FDiscordUserData ud;
|
FDiscordJoinRequestData jr;
|
||||||
ud.userId = ANSI_TO_TCHAR(request->userId);
|
jr.userId = ANSI_TO_TCHAR(request->userId);
|
||||||
ud.username = ANSI_TO_TCHAR(request->username);
|
jr.username = ANSI_TO_TCHAR(request->username);
|
||||||
ud.discriminator = ANSI_TO_TCHAR(request->discriminator);
|
jr.discriminator = ANSI_TO_TCHAR(request->discriminator);
|
||||||
ud.avatar = ANSI_TO_TCHAR(request->avatar);
|
jr.avatar = ANSI_TO_TCHAR(request->avatar);
|
||||||
UE_LOG(Discord,
|
UE_LOG(Discord, Log, TEXT("Discord join request from %s - %s#%s"), *jr.userId, *jr.username, *jr.discriminator);
|
||||||
Log,
|
|
||||||
TEXT("Discord join request from %s - %s#%s"),
|
|
||||||
*ud.userId,
|
|
||||||
*ud.username,
|
|
||||||
*ud.discriminator);
|
|
||||||
if (self) {
|
if (self) {
|
||||||
self->OnJoinRequest.Broadcast(ud);
|
self->OnJoinRequest.Broadcast(jr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDiscordRpc::Initialize(const FString& applicationId,
|
void UDiscordRpc::Initialize(const FString& applicationId,
|
||||||
bool autoRegister,
|
bool autoRegister,
|
||||||
const FString& optionalSteamId)
|
const FString& optionalSteamId)
|
||||||
{
|
{
|
||||||
self = this;
|
self = this;
|
||||||
IsConnected = false;
|
IsConnected = false;
|
||||||
@ -102,7 +87,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDiscordRpc::Shutdown()
|
void UDiscordRpc::Shutdown()
|
||||||
@ -153,7 +138,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);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* Ask to join callback data
|
* Ask to join callback data
|
||||||
*/
|
*/
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FDiscordUserData {
|
struct FDiscordJoinRequestData {
|
||||||
GENERATED_USTRUCT_BODY()
|
GENERATED_USTRUCT_BODY()
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadOnly)
|
UPROPERTY(BlueprintReadOnly)
|
||||||
@ -24,35 +24,15 @@ 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);
|
||||||
|
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordConnected, const FDiscordUserData&, joinRequest);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDiscordConnected);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordDisconnected, int, errorCode, const FString&, errorMessage);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordDisconnected, int, errorCode, const FString&, errorMessage);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordErrored, int, errorCode, const FString&, errorMessage);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDiscordErrored, int, errorCode, const FString&, errorMessage);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoin, const FString&, joinSecret);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoin, const FString&, joinSecret);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordSpectate, const FString&, spectateSecret);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordSpectate, const FString&, spectateSecret);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoinRequest, const FDiscordUserData&, joinRequest);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDiscordJoinRequest, const FDiscordJoinRequestData&, joinRequest);
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -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;
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(DISCORD_DYNAMIC_LIB)
|
#if defined(DISCORD_DYNAMIC_LIB)
|
||||||
#if defined(_WIN32)
|
# if defined(_WIN32)
|
||||||
#if defined(DISCORD_BUILDING_SDK)
|
# if defined(DISCORD_BUILDING_SDK)
|
||||||
#define DISCORD_EXPORT __declspec(dllexport)
|
# define DISCORD_EXPORT __declspec(dllexport)
|
||||||
|
# else
|
||||||
|
# define DISCORD_EXPORT __declspec(dllimport)
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||||
|
# endif
|
||||||
#else
|
#else
|
||||||
#define DISCORD_EXPORT __declspec(dllimport)
|
# define DISCORD_EXPORT
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define DISCORD_EXPORT __attribute__((visibility("default")))
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define DISCORD_EXPORT
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -35,34 +35,31 @@ 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 */
|
||||||
int8_t instance;
|
int8_t instance;
|
||||||
} DiscordRichPresence;
|
} DiscordRichPresence;
|
||||||
|
|
||||||
typedef struct DiscordUser {
|
typedef struct DiscordJoinRequest {
|
||||||
const char* userId;
|
const char* userId;
|
||||||
const char* username;
|
const char* username;
|
||||||
const char* discriminator;
|
const char* discriminator;
|
||||||
const char* avatar;
|
const char* avatar;
|
||||||
} DiscordUser;
|
} DiscordJoinRequest;
|
||||||
|
|
||||||
typedef struct DiscordEventHandlers {
|
typedef struct DiscordEventHandlers {
|
||||||
void (*ready)(const DiscordUser* request);
|
void (*ready)(void);
|
||||||
void (*disconnected)(int errorCode, const char* message);
|
void (*disconnected)(int errorCode, const char* message);
|
||||||
void (*errored)(int errorCode, const char* message);
|
void (*errored)(int errorCode, const char* message);
|
||||||
void (*joinGame)(const char* joinSecret);
|
void (*joinGame)(const char* joinSecret);
|
||||||
void (*spectateGame)(const char* spectateSecret);
|
void (*spectateGame)(const char* spectateSecret);
|
||||||
void (*joinRequest)(const DiscordUser* request);
|
void (*joinRequest)(const DiscordJoinRequest* request);
|
||||||
} DiscordEventHandlers;
|
} 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,
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +72,7 @@ static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* comma
|
|||||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
|
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
|
//StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
|
||||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath);
|
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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);
|
||||||
|
@ -32,7 +32,7 @@ struct QueuedMessage {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct User {
|
struct JoinRequest {
|
||||||
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
|
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
|
||||||
// terminator = 21
|
// terminator = 21
|
||||||
char userId[32];
|
char userId[32];
|
||||||
@ -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};
|
||||||
@ -65,8 +64,7 @@ static std::mutex PresenceMutex;
|
|||||||
static std::mutex HandlerMutex;
|
static std::mutex HandlerMutex;
|
||||||
static QueuedMessage QueuedPresence{};
|
static QueuedMessage QueuedPresence{};
|
||||||
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
|
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
|
||||||
static MsgQueue<User, JoinQueueSize> JoinAskQueue;
|
static MsgQueue<JoinRequest, JoinQueueSize> JoinAskQueue;
|
||||||
static User connectedUser;
|
|
||||||
|
|
||||||
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential
|
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential
|
||||||
// backoff from 0.5 seconds to 1 minute
|
// backoff from 0.5 seconds to 1 minute
|
||||||
@ -90,11 +88,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 +117,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 +212,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 +236,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 +270,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);
|
||||||
@ -302,6 +292,7 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handlers = {};
|
Handlers = {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Connection) {
|
if (Connection) {
|
||||||
@ -309,42 +300,23 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connection = RpcConnection::Create(applicationId);
|
Connection = RpcConnection::Create(applicationId);
|
||||||
Connection->onConnect = [](JsonDocument& readyMessage) {
|
Connection->onConnect = []() {
|
||||||
Discord_UpdateHandlers(&QueuedHandlers);
|
Discord_UpdateHandlers(&QueuedHandlers);
|
||||||
if (QueuedPresence.length > 0) {
|
|
||||||
UpdatePresence.exchange(true);
|
|
||||||
SignalIOActivity();
|
|
||||||
}
|
|
||||||
auto data = GetObjMember(&readyMessage, "data");
|
|
||||||
auto user = GetObjMember(data, "user");
|
|
||||||
auto userId = GetStrMember(user, "id");
|
|
||||||
auto username = GetStrMember(user, "username");
|
|
||||||
auto avatar = GetStrMember(user, "avatar");
|
|
||||||
if (userId && username) {
|
|
||||||
StringCopy(connectedUser.userId, userId);
|
|
||||||
StringCopy(connectedUser.username, username);
|
|
||||||
auto discriminator = GetStrMember(user, "discriminator");
|
|
||||||
if (discriminator) {
|
|
||||||
StringCopy(connectedUser.discriminator, discriminator);
|
|
||||||
}
|
|
||||||
if (avatar) {
|
|
||||||
StringCopy(connectedUser.avatar, avatar);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
connectedUser.avatar[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WasJustConnected.exchange(true);
|
WasJustConnected.exchange(true);
|
||||||
ReconnectTimeMs.reset();
|
ReconnectTimeMs.reset();
|
||||||
};
|
};
|
||||||
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 +327,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,8 +336,7 @@ 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();
|
||||||
}
|
}
|
||||||
@ -421,11 +385,7 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
|||||||
if (WasJustConnected.exchange(false)) {
|
if (WasJustConnected.exchange(false)) {
|
||||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (Handlers.ready) {
|
if (Handlers.ready) {
|
||||||
DiscordUser du{connectedUser.userId,
|
Handlers.ready();
|
||||||
connectedUser.username,
|
|
||||||
connectedUser.discriminator,
|
|
||||||
connectedUser.avatar};
|
|
||||||
Handlers.ready(&du);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,8 +420,8 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (Handlers.joinRequest) {
|
if (Handlers.joinRequest) {
|
||||||
DiscordUser du{req->userId, req->username, req->discriminator, req->avatar};
|
DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar};
|
||||||
Handlers.joinRequest(&du);
|
Handlers.joinRequest(&djr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JoinAskQueue.CommitSend();
|
JoinAskQueue.CommitSend();
|
||||||
@ -479,13 +439,14 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
|||||||
extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newHandlers)
|
extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newHandlers)
|
||||||
{
|
{
|
||||||
if (newHandlers) {
|
if (newHandlers) {
|
||||||
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
|
|
||||||
if (!Handlers.handler_name && newHandlers->handler_name) { \
|
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
|
||||||
RegisterForEvent(event); \
|
if (!Handlers.handler_name && newHandlers->handler_name) { \
|
||||||
} \
|
RegisterForEvent(event); \
|
||||||
else if (Handlers.handler_name && !newHandlers->handler_name) { \
|
} \
|
||||||
DeregisterForEvent(event); \
|
else if (Handlers.handler_name && !newHandlers->handler_name) { \
|
||||||
}
|
DeregisterForEvent(event); \
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
|
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
|
||||||
@ -496,7 +457,8 @@ extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newH
|
|||||||
|
|
||||||
Handlers = *newHandlers;
|
Handlers = *newHandlers;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
std::lock_guard<std::mutex> guard(HandlerMutex);
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
Handlers = {};
|
Handlers = {};
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
template <typename ElementType, size_t QueueSize>
|
template <typename ElementType, size_t QueueSize>
|
||||||
class MsgQueue {
|
class MsgQueue {
|
||||||
ElementType queue_[QueueSize];
|
ElementType queue_[QueueSize]{};
|
||||||
std::atomic_uint nextAdd_{0};
|
std::atomic_uint nextAdd_{0};
|
||||||
std::atomic_uint nextSend_{0};
|
std::atomic_uint nextSend_{0};
|
||||||
std::atomic_uint pendingSends_{0};
|
std::atomic_uint pendingSends_{0};
|
||||||
|
@ -26,8 +26,12 @@ void RpcConnection::Open()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == State::Disconnected && !connection->Open()) {
|
if (state == State::Disconnected) {
|
||||||
return;
|
if (connection->Open()) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == State::SentHandshake) {
|
if (state == State::SentHandshake) {
|
||||||
@ -38,7 +42,7 @@ void RpcConnection::Open()
|
|||||||
if (cmd && evt && !strcmp(cmd, "DISPATCH") && !strcmp(evt, "READY")) {
|
if (cmd && evt && !strcmp(cmd, "DISPATCH") && !strcmp(evt, "READY")) {
|
||||||
state = State::Connected;
|
state = State::Connected;
|
||||||
if (onConnect) {
|
if (onConnect) {
|
||||||
onConnect(message);
|
onConnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ struct RpcConnection {
|
|||||||
|
|
||||||
BaseConnection* connection{nullptr};
|
BaseConnection* connection{nullptr};
|
||||||
State state{State::Disconnected};
|
State state{State::Disconnected};
|
||||||
void (*onConnect)(JsonDocument& message){nullptr};
|
void (*onConnect)(){nullptr};
|
||||||
void (*onDisconnect)(int errorCode, const char* message){nullptr};
|
void (*onDisconnect)(int errorCode, const char* message){nullptr};
|
||||||
char appId[64]{};
|
char appId[64]{};
|
||||||
int lastErrorCode{0};
|
int lastErrorCode{0};
|
||||||
|
@ -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]) ||
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#pragma warning(disable : 4464) // relative include path contains
|
#pragma warning(disable : 4464) // relative include path contains
|
||||||
#pragma warning(disable : 4668) // is not defined as a preprocessor macro
|
#pragma warning(disable : 4668) // is not defined as a preprocessor macro
|
||||||
#pragma warning(disable : 6313) // Incorrect operator
|
#pragma warning(disable : 6313) // Incorrect operator
|
||||||
#endif // __MINGW32__
|
#endif // __MINGW32__
|
||||||
|
|
||||||
#include "rapidjson/document.h"
|
#include "rapidjson/document.h"
|
||||||
#include "rapidjson/stringbuffer.h"
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user