diff --git a/src/tools/ecode/plugins/discordRPC/sdk/ipc.cpp b/src/tools/ecode/plugins/discordRPC/sdk/ipc.cpp index 9578ea370..ca5311fee 100644 --- a/src/tools/ecode/plugins/discordRPC/sdk/ipc.cpp +++ b/src/tools/ecode/plugins/discordRPC/sdk/ipc.cpp @@ -1,14 +1,15 @@ #include "ipc.hpp" #include +#include using namespace EE::System; using json = nlohmann::json; DiscordIPC::DiscordIPC() { - mPID = Sys::getProcessID(); // Before we get to live testing stage, I assume plugin thread PID is fine + mPID = Sys::getProcessID(); - mcClientID = "133573039394874989"; // TODO: Implement actual config reading + mcClientID = "1335730393948749898"; // TODO: Implement actual config reading #if defined(__unix__) || defined(_APPLE__) mSocket = -1; @@ -88,7 +89,8 @@ bool DiscordIPC::tryConnect() { close(mSocket); return false; } - + + doHandshake(); return true; } } @@ -99,9 +101,7 @@ bool DiscordIPC::tryConnect() { } void DiscordIPC::doHandshake() { - json j; - - j = json{ + json j = { {"v", 1}, {"client_id", mcClientID} }; @@ -110,30 +110,126 @@ void DiscordIPC::doHandshake() { } void DiscordIPC::clearActivity() { - json j; - - j = json{ - {"cmd", "SET_ACTIVITY"}, - {"args", { - {"pid",0}, - {"activity", NULL} - }, - {"nonce", "-"} // TODO: Null nonce for dev purposes, change to UUIDV4 in finished product - }}; - + json j = { + {"cmd", "SET_ACTIVITY"}, + {"args", { + {"pid", 0}, + {"activity", nullptr} + }}, + {"nonce", "-"} // TODO: Null nonce for dev purposes, change to UUIDV4 in finished product + }; sendPacket(DiscordIPCOpcodes::Frame, j); } void DiscordIPC::setActivity( DiscordIPCActivity a ) { - json j; - - // TODO, map out the request json + json aj = { + {"type", static_cast(a.type)}, + {"instance", true}, + }; + + if (!a.state.empty()) + aj["state"] = a.state; + if (!a.details.empty()) + aj["details"] = a.details; + + json as; + if (!a.largeImage.empty()) { + as["large_image"] = a.largeImage; + } + if (!a.largeText.empty()) { + as["large_text"] = a.largeText; + } + if (!a.smallImage.empty()) { + as["small_image"] = a.smallImage; + } + if (!a.smallText.empty()) { + as["small_text"] = a.smallText; + } + if (!as.empty()) { + aj["assets"] = as; + } + + json t; + if (a.start != 0) { + t["start"] = a.start; + } + if (a.end != 0) { + t["end"] = a.end; + } + if (!t.empty()) { + aj["timestamps"] = t; + } + + json b; + if (!a.buttons[0].url.empty()){ + b[0]["label"] = a.buttons[0].label; + b[0]["url"] = a.buttons[0].url; + } + if (!a.buttons[1].url.empty()){ + b[1]["label"] = a.buttons[1].label; + b[1]["url"] = a.buttons[1].url; + } + if (!b.empty()) { + aj["buttons"] = b; + } + + json j{ + {"cmd", "SET_ACTIVITY"}, + {"args", { + {"pid", mPID}, + {"activity", aj}, + }}, + {"nonce", "-"} + }; sendPacket(DiscordIPCOpcodes::Frame, j); + mActivity = a; } void DiscordIPC::sendPacket(DiscordIPCOpcodes opcode, json j) { - // TODO + Log::debug("Packet is: %s", j.dump(4)); // Packet json logging before we start sending to ipc + #if defined(__unix__) || defined(_APPLE__) + const std::string packet = j.dump(); + std::vector data; + + // Conversion to little endian + union { + uint32_t value; + uint8_t bytes[4]; + } bytes; + + bytes.value = htonl(opcode); + for (int i = 3; i >= 0; --i) { + data.push_back(bytes.bytes[i]); + } + + bytes.value = htonl(packet.length()); + for (int i = 3; i >= 0; --i) { + data.push_back(bytes.bytes[i]); + } + + for (char c : packet) { + data.push_back(static_cast(c)); + } + + long unsigned int bytesSent = send(mSocket, data.data(), data.size(), 0); + if (bytesSent != data.size()) { + Log::error("Failed to send all data to Unix socket: %zu bytes sent, %zu bytes expected", bytesSent, data.size()); + } + + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 500000; // 0.5 seconds in microseconds + setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + char buffer[1024]; + ssize_t bytesRead = recv(mSocket, buffer, sizeof(buffer), 0); + + // TODO: Implement nonce checking? (does it even really matter?) + + //return bytesRead; + #endif } void DiscordIPC::reconnect() { diff --git a/src/tools/ecode/plugins/discordRPC/sdk/ipc.hpp b/src/tools/ecode/plugins/discordRPC/sdk/ipc.hpp index dbba89322..b9fd0669e 100644 --- a/src/tools/ecode/plugins/discordRPC/sdk/ipc.hpp +++ b/src/tools/ecode/plugins/discordRPC/sdk/ipc.hpp @@ -1,4 +1,3 @@ - #include #include #include @@ -8,59 +7,75 @@ #include #if defined(__unix__) || defined(_APPLE__) - #include - #include + #include + #include #endif +enum DiscordIPCActivityTypes { + Playing = 0, + Listening = 2, + Watching = 3, + Competing = 5, +}; + +struct DiscordIPCActivityButton { + std::string label = ""; + std::string url = ""; +}; + struct DiscordIPCActivity { - std::string state = ""; - std::string details = ""; - time_t start = NULL; - time_t stop = NULL; - std::string largeImage = ""; - std::string largeText = ""; - std::string smallImage = ""; - std::string smallText = ""; + DiscordIPCActivityTypes type = DiscordIPCActivityTypes::Playing; + std::string state = ""; + std::string details = ""; + + time_t start = 0; + time_t end = 0; + + std::string largeImage = ""; + std::string largeText = ""; + std::string smallImage = ""; + std::string smallText = ""; + + DiscordIPCActivityButton buttons[2]; }; enum DiscordIPCOpcodes { - Handshake = 0, - Frame, - Close, - Ping, - Pong + Handshake = 0, + Frame, + Close, + Ping, + Pong }; class DiscordIPC { - public: - - virtual ~DiscordIPC(); - DiscordIPC(); - - // false - FileNotFound/OSNotSupported - // true - Success - bool tryConnect(); - - void setActivity( DiscordIPCActivity a ); - void clearActivity(); - - - - protected: - std::string mIpcPath; - int mPID; - - //Configurables - std::string mcClientID; - - #if defined(__unix__) || defined(_APPLE__) - int mSocket; - #endif - - void doHandshake(); - - void reconnect(); - - void sendPacket( DiscordIPCOpcodes opcode, nlohmann::json j ); -}; + public: + + virtual ~DiscordIPC(); + DiscordIPC(); + // false - FileNotFound/OSNotSupported + // true - Success + bool tryConnect(); + + void setActivity( DiscordIPCActivity a ); + DiscordIPCActivity *getActivity() { return &mActivity; } + void clearActivity(); + + protected: + std::string mIpcPath; + int mPID; + + DiscordIPCActivity mActivity; + + //Configurables + std::string mcClientID; + + #if defined(__unix__) || defined(_APPLE__) + int mSocket; + #endif + + void doHandshake(); + void reconnect(); + + void sendPacket( DiscordIPCOpcodes opcode, nlohmann::json j ); +}; \ No newline at end of file