Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
3bdb88d918 | |||
aa79c70bf9 | |||
a089aab53e | |||
dafd85c39f | |||
b1d6a7c0fc | |||
e4b3ef63b7 | |||
86ca320cb9 | |||
6cdc830544 | |||
990c8d4be6 | |||
6fa00223ad | |||
39ff0bf3e4 | |||
25b6f1dcde | |||
e7cdfaa64e | |||
a5a56bcf68 | |||
11e74bca5e | |||
fb87e7c193 |
19
LICENSE
Normal file
19
LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright 2017 Discord, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
9
build.py
Normal file → Executable file
9
build.py
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import click
|
||||
import os
|
||||
import subprocess
|
||||
@ -45,7 +47,7 @@ def build_lib(build_name, generator, options):
|
||||
|
||||
|
||||
def create_archive():
|
||||
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc.zip')
|
||||
archive_file_path = os.path.join(SCRIPT_PATH, 'builds', 'discord-rpc-%s.zip' % sys.platform)
|
||||
archive_file = zipfile.ZipFile(archive_file_path, 'w', zipfile.ZIP_DEFLATED)
|
||||
archive_src_base_path = os.path.join(SCRIPT_PATH, 'builds', 'install')
|
||||
archive_dst_base_path = 'discord-rpc'
|
||||
@ -64,6 +66,8 @@ def main(clean):
|
||||
if clean:
|
||||
shutil.rmtree('builds', ignore_errors=True)
|
||||
|
||||
mkdir_p('builds')
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
generator32 = 'Visual Studio 14 2015'
|
||||
generator64 = 'Visual Studio 14 2015 Win64'
|
||||
@ -79,6 +83,9 @@ def main(clean):
|
||||
shutil.copy(src_dll, dst_dll)
|
||||
dst_dll = os.path.join(SCRIPT_PATH, 'examples', 'unrealstatus', 'Plugins', 'discordrpc', 'Binaries', 'ThirdParty', 'discordrpcLibrary', 'Win64', 'discord-rpc.dll')
|
||||
shutil.copy(src_dll, dst_dll)
|
||||
elif sys.platform == 'darwin':
|
||||
build_lib('osx-static', None, {})
|
||||
build_lib('osx-dynamic', None, {'BUILD_DYNAMIC_LIB': True})
|
||||
|
||||
create_archive()
|
||||
|
||||
|
@ -10,7 +10,7 @@ By committing to an RPC-only integration, you decide to forego the work our libr
|
||||
|
||||
## Application Protocol Registration
|
||||
|
||||
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` from `src/discord-register.cpp` as a good example of how to properly register an application protocol for use with Discord.
|
||||
One thing that cannot be explicitly done over RPC is registering an application protocol for your game. If you choose to do an RPC-only implementation, you will have to register your application protocol yourself in the format of `discord-[your_app_id]://`. You can use `Discord_Register()` as a good(?) example of how to properly register an application protocol for use with Discord. For OSX and Linux it is probably simpler to handle the protocol registration as part of your install/packaging.
|
||||
|
||||
## New RPC Command
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
|
||||
public class DiscordController : MonoBehaviour {
|
||||
[System.Serializable]
|
||||
public class DiscordJoinEvent : UnityEngine.Events.UnityEvent<string> { }
|
||||
|
||||
[System.Serializable]
|
||||
public class DiscordSpectateEvent : UnityEngine.Events.UnityEvent<string> { }
|
||||
|
||||
[System.Serializable]
|
||||
public class DiscordJoinRequestEvent : UnityEngine.Events.UnityEvent<DiscordRpc.JoinRequest> { }
|
||||
|
||||
public class DiscordController : MonoBehaviour
|
||||
{
|
||||
public DiscordRpc.RichPresence presence;
|
||||
public string applicationId;
|
||||
public string optionalSteamId;
|
||||
@ -10,6 +18,9 @@ public class DiscordController : MonoBehaviour {
|
||||
public int clickCounter;
|
||||
public UnityEngine.Events.UnityEvent onConnect;
|
||||
public UnityEngine.Events.UnityEvent onDisconnect;
|
||||
public DiscordJoinEvent onJoin;
|
||||
public DiscordJoinEvent onSpectate;
|
||||
public DiscordJoinRequestEvent onJoinRequest;
|
||||
|
||||
DiscordRpc.EventHandlers handlers;
|
||||
|
||||
@ -47,18 +58,29 @@ public class DiscordController : MonoBehaviour {
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: join ({0})", secret));
|
||||
onJoin.Invoke(secret);
|
||||
}
|
||||
|
||||
public void SpectateCallback(string secret)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: spectate ({0})", secret));
|
||||
onSpectate.Invoke(secret);
|
||||
}
|
||||
|
||||
void Start () {
|
||||
public void RequestCallback(DiscordRpc.JoinRequest request)
|
||||
{
|
||||
++callbackCalls;
|
||||
Debug.Log(string.Format("Discord: join request {0}: {1}", request.username, request.userId));
|
||||
onJoinRequest.Invoke(request);
|
||||
}
|
||||
|
||||
void Update () {
|
||||
void Start()
|
||||
{
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
DiscordRpc.RunCallbacks();
|
||||
}
|
||||
|
||||
@ -73,6 +95,7 @@ public class DiscordController : MonoBehaviour {
|
||||
handlers.errorCallback += ErrorCallback;
|
||||
handlers.joinCallback += JoinCallback;
|
||||
handlers.spectateCallback += SpectateCallback;
|
||||
handlers.requestCallback += RequestCallback;
|
||||
DiscordRpc.Initialize(applicationId, ref handlers, true, optionalSteamId);
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,24 @@
|
||||
|
||||
public class DiscordRpc
|
||||
{
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void ReadyCallback();
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void DisconnectedCallback(int errorCode, string message);
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void ErrorCallback(int errorCode, string message);
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void JoinCallback(string secret);
|
||||
|
||||
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void SpectateCallback(string secret);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void RequestCallback(JoinRequest request);
|
||||
|
||||
public struct EventHandlers
|
||||
{
|
||||
public ReadyCallback readyCallback;
|
||||
@ -24,6 +27,7 @@ public class DiscordRpc
|
||||
public ErrorCallback errorCallback;
|
||||
public JoinCallback joinCallback;
|
||||
public SpectateCallback spectateCallback;
|
||||
public RequestCallback requestCallback;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
@ -46,16 +50,34 @@ public class DiscordRpc
|
||||
public bool instance;
|
||||
}
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize")]
|
||||
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown")]
|
||||
public static extern void Shutdown();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks")]
|
||||
public static extern void RunCallbacks();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence")]
|
||||
public static extern void UpdatePresence(ref RichPresence presence);
|
||||
[System.Serializable]
|
||||
public struct JoinRequest
|
||||
{
|
||||
public string userId;
|
||||
public string username;
|
||||
public string avatar;
|
||||
}
|
||||
|
||||
public enum Reply
|
||||
{
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
Ignore = 2
|
||||
}
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Shutdown", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Shutdown();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_RunCallbacks", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RunCallbacks();
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_UpdatePresence", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void UpdatePresence(ref RichPresence presence);
|
||||
|
||||
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Respond(string userId, Reply reply);
|
||||
}
|
||||
|
||||
|
Binary file not shown.
@ -703,6 +703,21 @@ MonoBehaviour:
|
||||
m_CallState: 2
|
||||
m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=null
|
||||
onJoin:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_TypeName: DiscordJoinEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=null
|
||||
onSpectate:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_TypeName: DiscordJoinEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=null
|
||||
onJoinRequest:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_TypeName: DiscordJoinRequestEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=null
|
||||
--- !u!4 &1929635630
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -1 +1 @@
|
||||
m_EditorVersion: 2017.1.0f3
|
||||
m_EditorVersion: 2017.1.1f1
|
||||
|
@ -1,5 +1,13 @@
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
add_executable(send-presence send-presence.c)
|
||||
add_executable(
|
||||
send-presence
|
||||
MACOSX_BUNDLE
|
||||
send-presence.c
|
||||
)
|
||||
set_target_properties(send-presence PROPERTIES
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Send Presence"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.discordapp.examples.send-presence"
|
||||
)
|
||||
target_link_libraries(send-presence discord-rpc)
|
||||
|
||||
install(
|
||||
@ -7,4 +15,7 @@ install(
|
||||
RUNTIME
|
||||
DESTINATION "bin"
|
||||
CONFIGURATIONS Release
|
||||
BUNDLE
|
||||
DESTINATION "bin"
|
||||
CONFIGURATIONS Release
|
||||
)
|
@ -15,6 +15,21 @@ static const char* APPLICATION_ID = "345229890980937739";
|
||||
static int FrustrationLevel = 0;
|
||||
static int64_t StartTime;
|
||||
|
||||
static int prompt(char* line, size_t size)
|
||||
{
|
||||
int res;
|
||||
char* nl;
|
||||
printf("\n> ");
|
||||
fflush(stdout);
|
||||
res = fgets(line, (int)size, stdin) ? 1 : 0;
|
||||
line[size - 1] = 0;
|
||||
nl = strchr(line, '\n');
|
||||
if (nl) {
|
||||
*nl = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void updateDiscordPresence()
|
||||
{
|
||||
char buffer[256];
|
||||
@ -62,19 +77,37 @@ static void handleDiscordSpectate(const char* secret)
|
||||
printf("\nDiscord: spectate (%s)\n", secret);
|
||||
}
|
||||
|
||||
static int prompt(char* line, size_t size)
|
||||
static void handleDiscordJoinRequest(const DiscordJoinRequest* request)
|
||||
{
|
||||
int res;
|
||||
char* nl;
|
||||
printf("\n> ");
|
||||
fflush(stdout);
|
||||
res = fgets(line, (int)size, stdin) ? 1 : 0;
|
||||
line[size - 1] = 0;
|
||||
nl = strchr(line, '\n');
|
||||
if (nl) {
|
||||
*nl = 0;
|
||||
int response = -1;
|
||||
char yn[4];
|
||||
printf("\nDiscord: join request from %s - %s - %s\n",
|
||||
request->username,
|
||||
request->avatar,
|
||||
request->userId);
|
||||
do {
|
||||
printf("Accept? (y/n)");
|
||||
if (!prompt(yn, sizeof(yn))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!yn[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (yn[0] == 'y') {
|
||||
response = DISCORD_REPLY_YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (yn[0] == 'n') {
|
||||
response = DISCORD_REPLY_NO;
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
if (response != -1) {
|
||||
Discord_Respond(request->userId, response);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void discordInit()
|
||||
@ -86,6 +119,7 @@ static void discordInit()
|
||||
handlers.errored = handleDiscordError;
|
||||
handlers.joinGame = handleDiscordJoin;
|
||||
handlers.spectateGame = handleDiscordSpectate;
|
||||
handlers.joinRequest = handleDiscordJoinRequest;
|
||||
Discord_Initialize(APPLICATION_ID, &handlers, 1, NULL);
|
||||
}
|
||||
|
||||
|
@ -41,30 +41,43 @@ typedef struct DiscordRichPresence {
|
||||
int8_t instance;
|
||||
} DiscordRichPresence;
|
||||
|
||||
typedef struct DiscordJoinRequest {
|
||||
const char* userId;
|
||||
const char* username;
|
||||
const char* avatar;
|
||||
} DiscordJoinRequest;
|
||||
|
||||
typedef struct DiscordEventHandlers {
|
||||
void (*ready)();
|
||||
void (*disconnected)(int errorCode, const char* message);
|
||||
void (*errored)(int errorCode, const char* message);
|
||||
void (*joinGame)(const char* joinSecret);
|
||||
void (*spectateGame)(const char* spectateSecret);
|
||||
void (*joinRequest)(const DiscordJoinRequest* request);
|
||||
} DiscordEventHandlers;
|
||||
|
||||
#define DISCORD_REPLY_NO 0
|
||||
#define DISCORD_REPLY_YES 1
|
||||
#define DISCORD_REPLY_IGNORE 2
|
||||
|
||||
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId);
|
||||
DISCORD_EXPORT void Discord_Shutdown();
|
||||
DISCORD_EXPORT void Discord_Shutdown(void);
|
||||
|
||||
/* checks for incoming messages, dispatches callbacks */
|
||||
DISCORD_EXPORT void Discord_RunCallbacks();
|
||||
DISCORD_EXPORT void Discord_RunCallbacks(void);
|
||||
|
||||
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
DISCORD_EXPORT void Discord_UpdateConnection();
|
||||
DISCORD_EXPORT void Discord_UpdateConnection(void);
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
|
||||
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
@ -6,32 +6,76 @@ option(BUILD_DYNAMIC_LIB "Build library as a DLL" OFF)
|
||||
set(BASE_RPC_SRC
|
||||
${PROJECT_SOURCE_DIR}/include/discord-rpc.h
|
||||
discord-rpc.cpp
|
||||
discord-register.h
|
||||
discord-register.cpp
|
||||
discord_register.h
|
||||
rpc_connection.h
|
||||
rpc_connection.cpp
|
||||
serialization.h
|
||||
serialization.cpp
|
||||
connection.h
|
||||
backoff.h
|
||||
msg_queue.h
|
||||
)
|
||||
|
||||
if (${BUILD_DYNAMIC_LIB})
|
||||
set(RPC_LIBRARY_TYPE SHARED)
|
||||
if(WIN32)
|
||||
set(BASE_RPC_SRC ${BASE_RPC_SRC} dllmain.cpp)
|
||||
endif(WIN32)
|
||||
else(${BUILD_DYNAMIC_LIB})
|
||||
set(RPC_LIBRARY_TYPE STATIC)
|
||||
endif(${BUILD_DYNAMIC_LIB})
|
||||
|
||||
if(WIN32)
|
||||
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC} connection_win.cpp)
|
||||
target_compile_options(discord-rpc PRIVATE /W4)
|
||||
add_definitions(-DDISCORD_WINDOWS)
|
||||
set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_win.cpp discord_register_win.cpp)
|
||||
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC})
|
||||
target_compile_options(discord-rpc PRIVATE /EHsc
|
||||
/Wall
|
||||
/wd4100 # unreferenced formal parameter
|
||||
/wd4514 # unreferenced inline
|
||||
/wd4625 # copy constructor deleted
|
||||
/wd5026 # move constructor deleted
|
||||
/wd4626 # move assignment operator deleted
|
||||
/wd4668 # not defined preprocessor macro
|
||||
/wd4710 # function not inlined
|
||||
/wd4711 # function was inlined
|
||||
/wd4820 # structure padding
|
||||
/wd4946 # reinterpret_cast used between related classes
|
||||
/wd5027 # move assignment operator was implicitly defined as deleted
|
||||
)
|
||||
endif(WIN32)
|
||||
|
||||
if(UNIX)
|
||||
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC} connection_unix.cpp)
|
||||
set(BASE_RPC_SRC ${BASE_RPC_SRC} connection_unix.cpp)
|
||||
|
||||
if (APPLE)
|
||||
add_definitions(-DDISCORD_OSX)
|
||||
set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_osx.m)
|
||||
else (APPLE)
|
||||
add_definitions(-DDISCORD_LINUX)
|
||||
set(BASE_RPC_SRC ${BASE_RPC_SRC} discord_register_linux.cpp)
|
||||
endif(APPLE)
|
||||
|
||||
add_library(discord-rpc ${RPC_LIBRARY_TYPE} ${BASE_RPC_SRC})
|
||||
target_link_libraries(discord-rpc PUBLIC pthread)
|
||||
target_compile_options(discord-rpc PRIVATE -g -Wall -std=c++14)
|
||||
target_compile_options(discord-rpc PRIVATE
|
||||
-g
|
||||
-Weverything
|
||||
-Wno-unknown-pragmas # pragma push thing doesn't work on clang
|
||||
-Wno-old-style-cast # it's fine
|
||||
-Wno-c++98-compat # that was almost 2 decades ago
|
||||
-Wno-c++98-compat-pedantic
|
||||
-Wno-missing-noreturn
|
||||
-Wno-padded # structure padding
|
||||
-Wno-covered-switch-default
|
||||
-Wno-exit-time-destructors # not sure about these
|
||||
-Wno-global-constructors
|
||||
)
|
||||
target_compile_options(discord-rpc PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-std=c++14>)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(discord-rpc PRIVATE "-framework AppKit")
|
||||
endif (APPLE)
|
||||
endif(UNIX)
|
||||
|
||||
target_include_directories(discord-rpc PRIVATE ${RAPIDJSON}/include)
|
||||
|
@ -20,7 +20,7 @@ struct Backoff {
|
||||
, maxAmount(max)
|
||||
, current(min)
|
||||
, fails(0)
|
||||
, randGenerator(time(0))
|
||||
, randGenerator((uint64_t)time(0))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ bool BaseConnection::Read(void* data, size_t length)
|
||||
return false;
|
||||
}
|
||||
|
||||
int res = recv(self->sock, data, length, MsgFlags);
|
||||
int res = (int)recv(self->sock, data, length, MsgFlags);
|
||||
if (res < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return false;
|
||||
|
@ -5,10 +5,11 @@
|
||||
#define NOSERVICE
|
||||
#define NOIME
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
|
||||
int GetProcessId()
|
||||
{
|
||||
return ::GetCurrentProcessId();
|
||||
return (int)::GetCurrentProcessId();
|
||||
}
|
||||
|
||||
struct BaseConnectionWin : public BaseConnection {
|
||||
@ -75,22 +76,44 @@ bool BaseConnection::Write(const void* data, size_t length)
|
||||
return true;
|
||||
}
|
||||
auto self = reinterpret_cast<BaseConnectionWin*>(this);
|
||||
assert(self);
|
||||
if (!self) {
|
||||
return false;
|
||||
}
|
||||
if (self->pipe == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
return ::WriteFile(self->pipe, data, (DWORD)length, nullptr, nullptr) == TRUE;
|
||||
assert(data);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
const DWORD bytesLength = (DWORD)length;
|
||||
DWORD bytesWritten = 0;
|
||||
return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE &&
|
||||
bytesWritten == bytesLength;
|
||||
}
|
||||
|
||||
bool BaseConnection::Read(void* data, size_t length)
|
||||
{
|
||||
assert(data);
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
auto self = reinterpret_cast<BaseConnectionWin*>(this);
|
||||
assert(self);
|
||||
if (!self) {
|
||||
return false;
|
||||
}
|
||||
if (self->pipe == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
DWORD bytesAvailable = 0;
|
||||
if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
|
||||
if (bytesAvailable >= length) {
|
||||
if (::ReadFile(self->pipe, data, (DWORD)length, nullptr, nullptr) == TRUE) {
|
||||
DWORD bytesToRead = (DWORD)length;
|
||||
DWORD bytesRead = 0;
|
||||
if (::ReadFile(self->pipe, data, bytesToRead, &bytesRead, nullptr) == TRUE) {
|
||||
assert(bytesToRead == bytesRead);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include "discord-rpc.h"
|
||||
|
||||
#include "backoff.h"
|
||||
#include "discord-register.h"
|
||||
#include "discord_register.h"
|
||||
#include "msg_queue.h"
|
||||
#include "rpc_connection.h"
|
||||
#include "serialization.h"
|
||||
|
||||
@ -16,6 +17,7 @@
|
||||
|
||||
constexpr size_t MaxMessageSize{16 * 1024};
|
||||
constexpr size_t MessageQueueSize{8};
|
||||
constexpr size_t JoinQueueSize{8};
|
||||
|
||||
struct QueuedMessage {
|
||||
size_t length;
|
||||
@ -30,6 +32,12 @@ struct QueuedMessage {
|
||||
}
|
||||
};
|
||||
|
||||
struct JoinRequest {
|
||||
char userId[24];
|
||||
char username[48];
|
||||
char avatar[128];
|
||||
};
|
||||
|
||||
static RpcConnection* Connection{nullptr};
|
||||
static DiscordEventHandlers Handlers{};
|
||||
static std::atomic_bool WasJustConnected{false};
|
||||
@ -45,10 +53,9 @@ static int LastDisconnectErrorCode{0};
|
||||
static char LastDisconnectErrorMessage[256];
|
||||
static std::mutex PresenceMutex;
|
||||
static QueuedMessage QueuedPresence{};
|
||||
static QueuedMessage SendQueue[MessageQueueSize]{};
|
||||
static std::atomic_uint SendQueueNextAdd{0};
|
||||
static std::atomic_uint SendQueueNextSend{0};
|
||||
static std::atomic_uint SendQueuePendingSends{0};
|
||||
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
|
||||
static MsgQueue<JoinRequest, JoinQueueSize> JoinAskQueue;
|
||||
|
||||
// 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
|
||||
static Backoff ReconnectTimeMs(500, 60 * 1000);
|
||||
@ -69,31 +76,11 @@ static void UpdateReconnectTime()
|
||||
std::chrono::duration<int64_t, std::milli>{ReconnectTimeMs.nextDelay()};
|
||||
}
|
||||
|
||||
static QueuedMessage* SendQueueGetNextAddMessage()
|
||||
{
|
||||
// if we are not connected, let's not batch up stale messages for later
|
||||
if (!Connection || !Connection->IsOpen()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// if we are falling behind, bail
|
||||
if (SendQueuePendingSends.load() >= MessageQueueSize) {
|
||||
return nullptr;
|
||||
}
|
||||
auto index = (SendQueueNextAdd++) % MessageQueueSize;
|
||||
return &SendQueue[index];
|
||||
}
|
||||
static QueuedMessage* SendQueueGetNextSendMessage()
|
||||
{
|
||||
auto index = (SendQueueNextSend++) % MessageQueueSize;
|
||||
return &SendQueue[index];
|
||||
}
|
||||
static void SendQueueCommitMessage()
|
||||
{
|
||||
SendQueuePendingSends++;
|
||||
}
|
||||
|
||||
extern "C" void Discord_UpdateConnection()
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
extern "C" DISCORD_EXPORT void Discord_UpdateConnection(void)
|
||||
#else
|
||||
static void Discord_UpdateConnection(void)
|
||||
#endif
|
||||
{
|
||||
if (!Connection) {
|
||||
return;
|
||||
@ -134,7 +121,7 @@ extern "C" void Discord_UpdateConnection()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(evtName, "GAME_JOIN") == 0) {
|
||||
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
|
||||
auto data = GetObjMember(&message, "data");
|
||||
auto secret = GetStrMember(data, "secret");
|
||||
if (secret) {
|
||||
@ -142,7 +129,7 @@ extern "C" void Discord_UpdateConnection()
|
||||
WasJoinGame.store(true);
|
||||
}
|
||||
}
|
||||
else if (strcmp(evtName, "GAME_SPECTATE") == 0) {
|
||||
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) {
|
||||
auto data = GetObjMember(&message, "data");
|
||||
auto secret = GetStrMember(data, "secret");
|
||||
if (secret) {
|
||||
@ -150,6 +137,25 @@ extern "C" void Discord_UpdateConnection()
|
||||
WasSpectateGame.store(true);
|
||||
}
|
||||
}
|
||||
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) {
|
||||
auto data = GetObjMember(&message, "data");
|
||||
auto user = GetObjMember(data, "user");
|
||||
auto userId = GetStrMember(user, "id");
|
||||
auto username = GetStrMember(user, "username");
|
||||
auto avatar = GetStrMember(user, "avatar");
|
||||
auto joinReq = JoinAskQueue.GetNextAddMessage();
|
||||
if (userId && username && joinReq) {
|
||||
StringCopy(joinReq->userId, userId);
|
||||
StringCopy(joinReq->username, username);
|
||||
if (avatar) {
|
||||
StringCopy(joinReq->avatar, avatar);
|
||||
}
|
||||
else {
|
||||
joinReq->avatar[0] = 0;
|
||||
}
|
||||
JoinAskQueue.CommitAdd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,16 +174,16 @@ extern "C" void Discord_UpdateConnection()
|
||||
}
|
||||
}
|
||||
|
||||
while (SendQueuePendingSends.load()) {
|
||||
auto qmessage = SendQueueGetNextSendMessage();
|
||||
while (SendQueue.HavePendingSends()) {
|
||||
auto qmessage = SendQueue.GetNextSendMessage();
|
||||
Connection->Write(qmessage->buffer, qmessage->length);
|
||||
--SendQueuePendingSends;
|
||||
SendQueue.CommitSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISCORD_DISABLE_IO_THREAD
|
||||
void DiscordRpcIo()
|
||||
static void DiscordRpcIo(void)
|
||||
{
|
||||
const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
|
||||
|
||||
@ -190,27 +196,27 @@ void DiscordRpcIo()
|
||||
}
|
||||
#endif
|
||||
|
||||
void SignalIOActivity()
|
||||
static void SignalIOActivity()
|
||||
{
|
||||
#ifndef DISCORD_DISABLE_IO_THREAD
|
||||
WaitForIOActivity.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RegisterForEvent(const char* evtName)
|
||||
static bool RegisterForEvent(const char* evtName)
|
||||
{
|
||||
auto qmessage = SendQueueGetNextAddMessage();
|
||||
auto qmessage = SendQueue.GetNextAddMessage();
|
||||
if (qmessage) {
|
||||
qmessage->length =
|
||||
JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||
SendQueueCommitMessage();
|
||||
SendQueue.CommitAdd();
|
||||
SignalIOActivity();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" void Discord_Initialize(const char* applicationId,
|
||||
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId)
|
||||
@ -243,11 +249,15 @@ extern "C" void Discord_Initialize(const char* applicationId,
|
||||
ReconnectTimeMs.reset();
|
||||
|
||||
if (Handlers.joinGame) {
|
||||
RegisterForEvent("GAME_JOIN");
|
||||
RegisterForEvent("ACTIVITY_JOIN");
|
||||
}
|
||||
|
||||
if (Handlers.spectateGame) {
|
||||
RegisterForEvent("GAME_SPECTATE");
|
||||
RegisterForEvent("ACTIVITY_SPECTATE");
|
||||
}
|
||||
|
||||
if (Handlers.joinRequest) {
|
||||
RegisterForEvent("ACTIVITY_JOIN_REQUEST");
|
||||
}
|
||||
};
|
||||
Connection->onDisconnect = [](int err, const char* message) {
|
||||
@ -263,7 +273,7 @@ extern "C" void Discord_Initialize(const char* applicationId,
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" void Discord_Shutdown()
|
||||
extern "C" DISCORD_EXPORT void Discord_Shutdown()
|
||||
{
|
||||
if (!Connection) {
|
||||
return;
|
||||
@ -281,7 +291,7 @@ extern "C" void Discord_Shutdown()
|
||||
RpcConnection::Destroy(Connection);
|
||||
}
|
||||
|
||||
extern "C" void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
||||
extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
||||
{
|
||||
PresenceMutex.lock();
|
||||
QueuedPresence.length = JsonWriteRichPresenceObj(
|
||||
@ -290,7 +300,22 @@ extern "C" void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
||||
SignalIOActivity();
|
||||
}
|
||||
|
||||
extern "C" void Discord_RunCallbacks()
|
||||
extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply)
|
||||
{
|
||||
// if we are not connected, let's not batch up stale messages for later
|
||||
if (!Connection || !Connection->IsOpen()) {
|
||||
return;
|
||||
}
|
||||
auto qmessage = SendQueue.GetNextAddMessage();
|
||||
if (qmessage) {
|
||||
qmessage->length =
|
||||
JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++);
|
||||
SendQueue.CommitAdd();
|
||||
SignalIOActivity();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_RunCallbacks()
|
||||
{
|
||||
// Note on some weirdness: internally we might connect, get other signals, disconnect any number
|
||||
// of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
|
||||
@ -326,6 +351,20 @@ extern "C" void Discord_RunCallbacks()
|
||||
Handlers.spectateGame(SpectateGameSecret);
|
||||
}
|
||||
|
||||
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
|
||||
// where the implementer would rather sequentially accept/reject each one before the next invite
|
||||
// is sent. I left it this way because I could also imagine wanting to process these all and
|
||||
// maybe show them in one common dialog and/or start fetching the avatars in parallel, and if
|
||||
// not it should be trivial for the implementer to make a queue themselves.
|
||||
while (JoinAskQueue.HavePendingSends()) {
|
||||
auto req = JoinAskQueue.GetNextSendMessage();
|
||||
if (Handlers.joinRequest) {
|
||||
DiscordJoinRequest djr{req->userId, req->username, req->avatar};
|
||||
Handlers.joinRequest(&djr);
|
||||
}
|
||||
JoinAskQueue.CommitSend();
|
||||
}
|
||||
|
||||
if (!isConnected) {
|
||||
// if we are not connected, disconnect message last
|
||||
if (wasDisconnected && Handlers.disconnected) {
|
||||
|
@ -1,4 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void Discord_Register(const char* applicationId, const char* command);
|
||||
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
96
src/discord_register_linux.cpp
Normal file
96
src/discord_register_linux.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "discord-rpc.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool Mkdir(const char* path)
|
||||
{
|
||||
int result = mkdir(path, 0755);
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
if (errno == EEXIST) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// we want to register games so we can run them from Discord client as discord-<appid>://
|
||||
extern "C" void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
// Add a desktop file and update some mime handlers so that xdg-open does the right thing.
|
||||
|
||||
const char* home = getenv("HOME");
|
||||
if (!home) {
|
||||
return;
|
||||
}
|
||||
|
||||
char exePath[1024];
|
||||
if (!command || !command[0]) {
|
||||
if (readlink("/proc/self/exe", exePath, sizeof(exePath)) <= 0) {
|
||||
return;
|
||||
}
|
||||
command = exePath;
|
||||
}
|
||||
|
||||
const char* destopFileFormat = "[Desktop Entry]\n"
|
||||
"Name=Game %s\n"
|
||||
"Exec=%s %%u\n" // note: it really wants that %u in there
|
||||
"Type=Application\n"
|
||||
"NoDisplay=true\n"
|
||||
"Categories=Discord;Games;\n"
|
||||
"MimeType=x-scheme-handler/discord-%s;\n";
|
||||
char desktopFile[2048];
|
||||
int fileLen = snprintf(
|
||||
desktopFile, sizeof(desktopFile), destopFileFormat, applicationId, command, applicationId);
|
||||
if (fileLen <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char desktopFilename[256];
|
||||
snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
|
||||
|
||||
char desktopFilePath[1024];
|
||||
snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, "/share");
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, "/applications");
|
||||
if (!Mkdir(desktopFilePath)) {
|
||||
return;
|
||||
}
|
||||
strcat(desktopFilePath, desktopFilename);
|
||||
|
||||
FILE* fp = fopen(desktopFilePath, "w");
|
||||
if (fp) {
|
||||
fwrite(desktopFile, 1, fileLen, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
char xdgMimeCommand[1024];
|
||||
snprintf(xdgMimeCommand,
|
||||
sizeof(xdgMimeCommand),
|
||||
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
|
||||
applicationId,
|
||||
applicationId);
|
||||
system(xdgMimeCommand);
|
||||
}
|
||||
|
||||
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
{
|
||||
char command[256];
|
||||
sprintf(command, "xdg-open steam://run/%s", steamId);
|
||||
Discord_Register(applicationId, command);
|
||||
}
|
97
src/discord_register_osx.m
Normal file
97
src/discord_register_osx.m
Normal file
@ -0,0 +1,97 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#include "discord_register.h"
|
||||
|
||||
static bool Mkdir(const char* path)
|
||||
{
|
||||
int result = mkdir(path, 0755);
|
||||
if (result == 0) {
|
||||
return true;
|
||||
}
|
||||
if (errno == EEXIST) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void RegisterCommand(const char* applicationId, const char* command)
|
||||
{
|
||||
// There does not appear to be a way to register arbitrary commands on OSX, so instead we'll save the command
|
||||
// to a file in the Discord config path, and when it is needed, Discord can try to load the file there, open
|
||||
// the command therein (will pass to js's window.open, so requires a url-like thing)
|
||||
|
||||
const char* home = getenv("HOME");
|
||||
if (!home) {
|
||||
return;
|
||||
}
|
||||
|
||||
char path[2048];
|
||||
sprintf(path, "%s/Library/Application Support/discord", home);
|
||||
Mkdir(path);
|
||||
strcat(path, "/games");
|
||||
Mkdir(path);
|
||||
strcat(path, "/");
|
||||
strcat(path, applicationId);
|
||||
strcat(path, ".json");
|
||||
|
||||
FILE* f = fopen(path, "w");
|
||||
if (f) {
|
||||
char jsonBuffer[2048];
|
||||
int len = snprintf(jsonBuffer, sizeof(jsonBuffer), "{\"command\": \"%s\"}", command);
|
||||
fwrite(jsonBuffer, (size_t)len, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void RegisterURL(const char* applicationId)
|
||||
{
|
||||
char url[256];
|
||||
snprintf(url, sizeof(url), "discord-%s", applicationId);
|
||||
CFStringRef cfURL = CFStringCreateWithCString(NULL, url, kCFStringEncodingUTF8);
|
||||
|
||||
NSString* myBundleId = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if (!myBundleId) {
|
||||
fprintf(stderr, "No bundle id found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL* myURL = [[NSBundle mainBundle] bundleURL];
|
||||
if (!myURL) {
|
||||
fprintf(stderr, "No bundle url found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
OSStatus status = LSSetDefaultHandlerForURLScheme(cfURL, (__bridge CFStringRef)myBundleId);
|
||||
if (status != noErr) {
|
||||
fprintf(stderr, "Error in LSSetDefaultHandlerForURLScheme: %d\n", (int)status);
|
||||
return;
|
||||
}
|
||||
|
||||
status = LSRegisterURL((__bridge CFURLRef)myURL, true);
|
||||
if (status != noErr) {
|
||||
fprintf(stderr, "Error in LSRegisterURL: %d\n", (int)status);
|
||||
}
|
||||
}
|
||||
|
||||
void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
if (command) {
|
||||
RegisterCommand(applicationId, command);
|
||||
}
|
||||
else {
|
||||
// raii lite
|
||||
void* pool = [[NSAutoreleasePool alloc] init];
|
||||
RegisterURL(applicationId);
|
||||
[(id)pool drain];
|
||||
}
|
||||
}
|
||||
|
||||
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
{
|
||||
char command[256];
|
||||
sprintf(command, "steam://run/%s", steamId);
|
||||
Discord_Register(applicationId, command);
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
#include "discord-rpc.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOSERVICE
|
||||
@ -10,9 +9,7 @@
|
||||
#include <Psapi.h>
|
||||
#include <Strsafe.h>
|
||||
#pragma comment(lib, "Psapi.lib")
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
|
||||
{
|
||||
// https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
|
||||
@ -20,9 +17,8 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
|
||||
// Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions.
|
||||
|
||||
wchar_t exeFilePath[MAX_PATH];
|
||||
int exeLen = GetModuleFileNameExW(GetCurrentProcess(), nullptr, exeFilePath, MAX_PATH);
|
||||
DWORD exeLen = GetModuleFileNameExW(GetCurrentProcess(), nullptr, exeFilePath, MAX_PATH);
|
||||
wchar_t openCommand[1024];
|
||||
const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand);
|
||||
|
||||
if (command && command[0]) {
|
||||
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
|
||||
@ -49,14 +45,14 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
|
||||
}
|
||||
DWORD len;
|
||||
LSTATUS result;
|
||||
len = lstrlenW(protocolDescription) + 1;
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result =
|
||||
RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
}
|
||||
|
||||
len = lstrlenW(protocolDescription) + 1;
|
||||
len = (DWORD)lstrlenW(protocolDescription) + 1;
|
||||
result = RegSetKeyValueW(key, nullptr, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
fprintf(stderr, "Error writing description\n");
|
||||
@ -68,7 +64,7 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
|
||||
fprintf(stderr, "Error writing icon\n");
|
||||
}
|
||||
|
||||
len = lstrlenW(openCommand) + 1;
|
||||
len = (DWORD)lstrlenW(openCommand) + 1;
|
||||
result = RegSetKeyValueW(
|
||||
key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t));
|
||||
if (FAILED(result)) {
|
||||
@ -76,12 +72,9 @@ void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
|
||||
}
|
||||
RegCloseKey(key);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Discord_Register(const char* applicationId, const char* command)
|
||||
extern "C" void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
wchar_t appId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
|
||||
@ -94,12 +87,10 @@ void Discord_Register(const char* applicationId, const char* command)
|
||||
}
|
||||
|
||||
Discord_RegisterW(appId, wcommand);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
extern "C" void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t appId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
|
||||
@ -133,5 +124,4 @@ void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://run/%s", steamPath, wSteamId);
|
||||
|
||||
Discord_RegisterW(appId, command);
|
||||
#endif
|
||||
}
|
36
src/msg_queue.h
Normal file
36
src/msg_queue.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
// A simple queue. No locks, but only works with a single thread as producer and a single thread as
|
||||
// a consumer. Mutex up as needed.
|
||||
|
||||
template <typename ElementType, size_t QueueSize>
|
||||
class MsgQueue {
|
||||
ElementType queue_[QueueSize]{};
|
||||
std::atomic_uint nextAdd_{0};
|
||||
std::atomic_uint nextSend_{0};
|
||||
std::atomic_uint pendingSends_{0};
|
||||
|
||||
public:
|
||||
MsgQueue() {}
|
||||
|
||||
ElementType* GetNextAddMessage()
|
||||
{
|
||||
// if we are falling behind, bail
|
||||
if (pendingSends_.load() >= QueueSize) {
|
||||
return nullptr;
|
||||
}
|
||||
auto index = (nextAdd_++) % QueueSize;
|
||||
return &queue_[index];
|
||||
}
|
||||
void CommitAdd() { ++pendingSends_; }
|
||||
|
||||
bool HavePendingSends() const { return pendingSends_.load() != 0; }
|
||||
ElementType* GetNextSendMessage()
|
||||
{
|
||||
auto index = (nextSend_++) % QueueSize;
|
||||
return &queue_[index];
|
||||
}
|
||||
void CommitSend() { --pendingSends_; }
|
||||
};
|
@ -129,6 +129,7 @@ bool RpcConnection::Read(JsonDocument& message)
|
||||
break;
|
||||
case Opcode::Pong:
|
||||
break;
|
||||
case Opcode::Handshake:
|
||||
default:
|
||||
// something bad happened
|
||||
lastErrorCode = (int)ErrorCode::ReadCorrupt;
|
||||
|
@ -72,7 +72,7 @@ void WriteOptionalString(JsonWriter& w, T& k, const char* value)
|
||||
}
|
||||
}
|
||||
|
||||
void JsonWriteNonce(JsonWriter& writer, int nonce)
|
||||
static void JsonWriteNonce(JsonWriter& writer, int nonce)
|
||||
{
|
||||
WriteKey(writer, "nonce");
|
||||
char nonceBuffer[32];
|
||||
@ -197,3 +197,32 @@ size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const cha
|
||||
|
||||
return writer.Size();
|
||||
}
|
||||
|
||||
size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce)
|
||||
{
|
||||
JsonWriter writer(dest, maxLen);
|
||||
|
||||
{
|
||||
WriteObject obj(writer);
|
||||
|
||||
WriteKey(writer, "cmd");
|
||||
if (reply == DISCORD_REPLY_YES) {
|
||||
writer.String("SEND_ACTIVITY_JOIN_INVITE");
|
||||
}
|
||||
else {
|
||||
writer.String("CLOSE_ACTIVITY_JOIN_REQUEST");
|
||||
}
|
||||
|
||||
WriteKey(writer, "args");
|
||||
{
|
||||
WriteObject args(writer);
|
||||
|
||||
WriteKey(writer, "user_id");
|
||||
writer.String(userId);
|
||||
}
|
||||
|
||||
JsonWriteNonce(writer, nonce);
|
||||
}
|
||||
|
||||
return writer.Size();
|
||||
}
|
||||
|
@ -2,10 +2,20 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma warning(push)
|
||||
|
||||
#pragma warning(disable : 4061) // enum is not explicitly handled by a case label
|
||||
#pragma warning(disable : 4365) // signed/unsigned mismatch
|
||||
#pragma warning(disable : 4464) // relative include path contains
|
||||
#pragma warning(disable : 4668) // is not defined as a preprocessor macro
|
||||
#pragma warning(disable : 6313) // Incorrect operator
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
// if only there was a standard library function for this
|
||||
template <size_t Len>
|
||||
inline size_t StringCopy(char (&dest)[Len], const char* src)
|
||||
@ -33,6 +43,8 @@ size_t JsonWriteRichPresenceObj(char* dest,
|
||||
const DiscordRichPresence* presence);
|
||||
size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName);
|
||||
|
||||
size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce);
|
||||
|
||||
// I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
|
||||
// to supply some of your own allocators for stuff rather than use the defaults
|
||||
|
||||
@ -109,7 +121,7 @@ public:
|
||||
}
|
||||
}
|
||||
void Flush() {}
|
||||
size_t GetSize() const { return current_ - buffer_; }
|
||||
size_t GetSize() const { return (size_t)(current_ - buffer_); }
|
||||
};
|
||||
|
||||
using MallocAllocator = rapidjson::CrtAllocator;
|
||||
|
Reference in New Issue
Block a user