Dynamic Event Handler Registration (#135)
- Discord_RegisterHandlers() exported - C# wrapper updated - Dynamically sub/unsub to events - Better mutex locking, for safety!
This commit is contained in:
parent
7e5d57e6fd
commit
087282cd4b
5 changed files with 117 additions and 38 deletions
|
@ -87,6 +87,9 @@ public class DiscordRpc
|
||||||
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("discord-rpc", EntryPoint = "Discord_Respond", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void Respond(string userId, Reply reply);
|
public static extern void Respond(string userId, Reply reply);
|
||||||
|
|
||||||
|
[DllImport("discord-rpc", EntryPoint = "Discord_UpdateHandlers", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void UpdateHandlers(ref EventHandlers handlers);
|
||||||
|
|
||||||
public static void UpdatePresence(RichPresence presence)
|
public static void UpdatePresence(RichPresence presence)
|
||||||
{
|
{
|
||||||
var presencestruct = presence.GetStruct();
|
var presencestruct = presence.GetStruct();
|
||||||
|
|
|
@ -80,6 +80,8 @@ DISCORD_EXPORT void Discord_ClearPresence(void);
|
||||||
|
|
||||||
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
||||||
|
|
||||||
|
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -60,6 +60,7 @@ static char LastErrorMessage[256];
|
||||||
static int LastDisconnectErrorCode{0};
|
static int LastDisconnectErrorCode{0};
|
||||||
static char LastDisconnectErrorMessage[256];
|
static char LastDisconnectErrorMessage[256];
|
||||||
static std::mutex PresenceMutex;
|
static std::mutex PresenceMutex;
|
||||||
|
static std::mutex HandlerMutex;
|
||||||
static QueuedMessage QueuedPresence{};
|
static QueuedMessage QueuedPresence{};
|
||||||
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
|
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
|
||||||
static MsgQueue<JoinRequest, JoinQueueSize> JoinAskQueue;
|
static MsgQueue<JoinRequest, JoinQueueSize> JoinAskQueue;
|
||||||
|
@ -212,15 +213,15 @@ static void Discord_UpdateConnection(void)
|
||||||
// writes
|
// writes
|
||||||
if (QueuedPresence.length) {
|
if (QueuedPresence.length) {
|
||||||
QueuedMessage local;
|
QueuedMessage local;
|
||||||
PresenceMutex.lock();
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||||
local.Copy(QueuedPresence);
|
local.Copy(QueuedPresence);
|
||||||
QueuedPresence.length = 0;
|
QueuedPresence.length = 0;
|
||||||
PresenceMutex.unlock();
|
}
|
||||||
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
|
||||||
PresenceMutex.lock();
|
std::lock_guard<std::mutex> guard(PresenceMutex);
|
||||||
QueuedPresence.Copy(local);
|
QueuedPresence.Copy(local);
|
||||||
PresenceMutex.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +251,19 @@ static bool RegisterForEvent(const char* evtName)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool DeregisterForEvent(const char* evtName)
|
||||||
|
{
|
||||||
|
auto qmessage = SendQueue.GetNextAddMessage();
|
||||||
|
if (qmessage) {
|
||||||
|
qmessage->length =
|
||||||
|
JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
|
||||||
|
SendQueue.CommitAdd();
|
||||||
|
SignalIOActivity();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||||
DiscordEventHandlers* handlers,
|
DiscordEventHandlers* handlers,
|
||||||
int autoRegister,
|
int autoRegister,
|
||||||
|
@ -266,12 +280,15 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||||
|
|
||||||
Pid = GetProcessId();
|
Pid = GetProcessId();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (handlers) {
|
if (handlers) {
|
||||||
Handlers = *handlers;
|
Handlers = *handlers;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Handlers = {};
|
Handlers = {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Connection) {
|
if (Connection) {
|
||||||
return;
|
return;
|
||||||
|
@ -279,20 +296,9 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||||
|
|
||||||
Connection = RpcConnection::Create(applicationId);
|
Connection = RpcConnection::Create(applicationId);
|
||||||
Connection->onConnect = []() {
|
Connection->onConnect = []() {
|
||||||
|
Discord_UpdateHandlers(&Handlers);
|
||||||
WasJustConnected.exchange(true);
|
WasJustConnected.exchange(true);
|
||||||
ReconnectTimeMs.reset();
|
ReconnectTimeMs.reset();
|
||||||
|
|
||||||
if (Handlers.joinGame) {
|
|
||||||
RegisterForEvent("ACTIVITY_JOIN");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Handlers.spectateGame) {
|
|
||||||
RegisterForEvent("ACTIVITY_SPECTATE");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Handlers.joinRequest) {
|
|
||||||
RegisterForEvent("ACTIVITY_JOIN_REQUEST");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Connection->onDisconnect = [](int err, const char* message) {
|
Connection->onDisconnect = [](int err, const char* message) {
|
||||||
LastDisconnectErrorCode = err;
|
LastDisconnectErrorCode = err;
|
||||||
|
@ -318,10 +324,11 @@ extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
|
||||||
|
|
||||||
extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
|
||||||
{
|
{
|
||||||
PresenceMutex.lock();
|
{
|
||||||
|
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);
|
||||||
PresenceMutex.unlock();
|
}
|
||||||
SignalIOActivity();
|
SignalIOActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,26 +367,39 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||||
|
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
// if we are connected, disconnect cb first
|
// if we are connected, disconnect cb first
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (wasDisconnected && Handlers.disconnected) {
|
if (wasDisconnected && Handlers.disconnected) {
|
||||||
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WasJustConnected.exchange(false) && Handlers.ready) {
|
if (WasJustConnected.exchange(false)) {
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
if (Handlers.ready) {
|
||||||
Handlers.ready();
|
Handlers.ready();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (GotErrorMessage.exchange(false) && Handlers.errored) {
|
if (GotErrorMessage.exchange(false)) {
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
if (Handlers.errored) {
|
||||||
Handlers.errored(LastErrorCode, LastErrorMessage);
|
Handlers.errored(LastErrorCode, LastErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WasJoinGame.exchange(false) && Handlers.joinGame) {
|
|
||||||
Handlers.joinGame(JoinGameSecret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WasSpectateGame.exchange(false) && Handlers.spectateGame) {
|
if (WasJoinGame.exchange(false)) {
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
if (Handlers.joinGame) {
|
||||||
|
Handlers.joinGame(JoinGameSecret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WasSpectateGame.exchange(false)) {
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
if (Handlers.spectateGame) {
|
||||||
Handlers.spectateGame(SpectateGameSecret);
|
Handlers.spectateGame(SpectateGameSecret);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
|
// 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
|
// where the implementer would rather sequentially accept/reject each one before the next invite
|
||||||
|
@ -388,17 +408,50 @@ extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
|
||||||
// not it should be trivial for the implementer to make a queue themselves.
|
// not it should be trivial for the implementer to make a queue themselves.
|
||||||
while (JoinAskQueue.HavePendingSends()) {
|
while (JoinAskQueue.HavePendingSends()) {
|
||||||
auto req = JoinAskQueue.GetNextSendMessage();
|
auto req = JoinAskQueue.GetNextSendMessage();
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (Handlers.joinRequest) {
|
if (Handlers.joinRequest) {
|
||||||
DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar};
|
DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar};
|
||||||
Handlers.joinRequest(&djr);
|
Handlers.joinRequest(&djr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
JoinAskQueue.CommitSend();
|
JoinAskQueue.CommitSend();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
// if we are not connected, disconnect message last
|
// if we are not connected, disconnect message last
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
if (wasDisconnected && Handlers.disconnected) {
|
if (wasDisconnected && Handlers.disconnected) {
|
||||||
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newHandlers)
|
||||||
|
{
|
||||||
|
if (newHandlers) {
|
||||||
|
|
||||||
|
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
|
||||||
|
if (!Handlers.handler_name && newHandlers->handler_name) { \
|
||||||
|
RegisterForEvent(event); \
|
||||||
|
} \
|
||||||
|
else if (Handlers.handler_name && !newHandlers->handler_name) { \
|
||||||
|
DeregisterForEvent(event); \
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
|
||||||
|
HANDLE_EVENT_REGISTRATION(spectateGame, "ACTIVITY_SPECTATE")
|
||||||
|
HANDLE_EVENT_REGISTRATION(joinRequest, "ACTIVITY_JOIN_REQUEST")
|
||||||
|
|
||||||
|
#undef HANDLE_EVENT_REGISTRATION
|
||||||
|
|
||||||
|
Handlers = *newHandlers;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(HandlerMutex);
|
||||||
|
Handlers = {};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -197,6 +197,25 @@ size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const cha
|
||||||
return writer.Size();
|
return writer.Size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t JsonWriteUnsubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName)
|
||||||
|
{
|
||||||
|
JsonWriter writer(dest, maxLen);
|
||||||
|
|
||||||
|
{
|
||||||
|
WriteObject obj(writer);
|
||||||
|
|
||||||
|
JsonWriteNonce(writer, nonce);
|
||||||
|
|
||||||
|
WriteKey(writer, "cmd");
|
||||||
|
writer.String("UNSUBSCRIBE");
|
||||||
|
|
||||||
|
WriteKey(writer, "evt");
|
||||||
|
writer.String(evtName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.Size();
|
||||||
|
}
|
||||||
|
|
||||||
size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce)
|
size_t JsonWriteJoinReply(char* dest, size_t maxLen, const char* userId, int reply, int nonce)
|
||||||
{
|
{
|
||||||
JsonWriter writer(dest, maxLen);
|
JsonWriter writer(dest, maxLen);
|
||||||
|
|
|
@ -47,6 +47,8 @@ size_t JsonWriteRichPresenceObj(char* dest,
|
||||||
const DiscordRichPresence* presence);
|
const DiscordRichPresence* presence);
|
||||||
size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName);
|
size_t JsonWriteSubscribeCommand(char* dest, size_t maxLen, int nonce, const char* evtName);
|
||||||
|
|
||||||
|
size_t JsonWriteUnsubscribeCommand(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);
|
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
|
// I want to use as few allocations as I can get away with, and to do that with RapidJson, you need
|
||||||
|
|
Loading…
Reference in a new issue