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:
Mason Sciotti 2018-03-23 10:25:28 -07:00 committed by GitHub
parent 7e5d57e6fd
commit 087282cd4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 38 deletions

View file

@ -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();

View file

@ -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

View file

@ -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(); {
local.Copy(QueuedPresence); std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.length = 0; local.Copy(QueuedPresence);
PresenceMutex.unlock(); 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
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,11 +280,14 @@ extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
Pid = GetProcessId(); Pid = GetProcessId();
if (handlers) { {
Handlers = *handlers; std::lock_guard<std::mutex> guard(HandlerMutex);
} if (handlers) {
else { Handlers = *handlers;
Handlers = {}; }
else {
Handlers = {};
}
} }
if (Connection) { if (Connection) {
@ -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(); {
QueuedPresence.length = JsonWriteRichPresenceObj( std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence); QueuedPresence.length = JsonWriteRichPresenceObj(
PresenceMutex.unlock(); QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
}
SignalIOActivity(); SignalIOActivity();
} }
@ -360,25 +367,38 @@ 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)) {
Handlers.ready(); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.ready) {
Handlers.ready();
}
} }
if (GotErrorMessage.exchange(false) && Handlers.errored) { if (GotErrorMessage.exchange(false)) {
Handlers.errored(LastErrorCode, LastErrorMessage); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.errored) {
Handlers.errored(LastErrorCode, LastErrorMessage);
}
} }
if (WasJoinGame.exchange(false) && Handlers.joinGame) { if (WasJoinGame.exchange(false)) {
Handlers.joinGame(JoinGameSecret); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinGame) {
Handlers.joinGame(JoinGameSecret);
}
} }
if (WasSpectateGame.exchange(false) && Handlers.spectateGame) { if (WasSpectateGame.exchange(false)) {
Handlers.spectateGame(SpectateGameSecret); std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.spectateGame) {
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
@ -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();
if (Handlers.joinRequest) { {
DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar}; std::lock_guard<std::mutex> guard(HandlerMutex);
Handlers.joinRequest(&djr); if (Handlers.joinRequest) {
DiscordJoinRequest djr{req->userId, req->username, req->discriminator, req->avatar};
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;
}

View file

@ -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);

View file

@ -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