all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

mGBA Game Boy Advance Emulator

src/third-party/discord-rpc/src/rpc_connection.cpp (view raw)

  1#include "rpc_connection.h"
  2#include "serialization.h"
  3
  4#include <atomic>
  5
  6static const int RpcVersion = 1;
  7static RpcConnection Instance;
  8
  9/*static*/ RpcConnection* RpcConnection::Create(const char* applicationId)
 10{
 11    Instance.connection = BaseConnection::Create();
 12    StringCopy(Instance.appId, applicationId);
 13    return &Instance;
 14}
 15
 16/*static*/ void RpcConnection::Destroy(RpcConnection*& c)
 17{
 18    c->Close();
 19    BaseConnection::Destroy(c->connection);
 20    c = nullptr;
 21}
 22
 23void RpcConnection::Open()
 24{
 25    if (state == State::Connected) {
 26        return;
 27    }
 28
 29    if (state == State::Disconnected && !connection->Open()) {
 30        return;
 31    }
 32
 33    if (state == State::SentHandshake) {
 34        JsonDocument message;
 35        if (Read(message)) {
 36            auto cmd = GetStrMember(&message, "cmd");
 37            auto evt = GetStrMember(&message, "evt");
 38            if (cmd && evt && !strcmp(cmd, "DISPATCH") && !strcmp(evt, "READY")) {
 39                state = State::Connected;
 40                if (onConnect) {
 41                    onConnect(message);
 42                }
 43            }
 44        }
 45    }
 46    else {
 47        sendFrame.opcode = Opcode::Handshake;
 48        sendFrame.length = (uint32_t)JsonWriteHandshakeObj(
 49          sendFrame.message, sizeof(sendFrame.message), RpcVersion, appId);
 50
 51        if (connection->Write(&sendFrame, sizeof(MessageFrameHeader) + sendFrame.length)) {
 52            state = State::SentHandshake;
 53        }
 54        else {
 55            Close();
 56        }
 57    }
 58}
 59
 60void RpcConnection::Close()
 61{
 62    if (onDisconnect && (state == State::Connected || state == State::SentHandshake)) {
 63        onDisconnect(lastErrorCode, lastErrorMessage);
 64    }
 65    connection->Close();
 66    state = State::Disconnected;
 67}
 68
 69bool RpcConnection::Write(const void* data, size_t length)
 70{
 71    sendFrame.opcode = Opcode::Frame;
 72    memcpy(sendFrame.message, data, length);
 73    sendFrame.length = (uint32_t)length;
 74    if (!connection->Write(&sendFrame, sizeof(MessageFrameHeader) + length)) {
 75        Close();
 76        return false;
 77    }
 78    return true;
 79}
 80
 81bool RpcConnection::Read(JsonDocument& message)
 82{
 83    if (state != State::Connected && state != State::SentHandshake) {
 84        return false;
 85    }
 86    MessageFrame readFrame;
 87    for (;;) {
 88        bool didRead = connection->Read(&readFrame, sizeof(MessageFrameHeader));
 89        if (!didRead) {
 90            if (!connection->isOpen) {
 91                lastErrorCode = (int)ErrorCode::PipeClosed;
 92                StringCopy(lastErrorMessage, "Pipe closed");
 93                Close();
 94            }
 95            return false;
 96        }
 97
 98        if (readFrame.length > 0) {
 99            didRead = connection->Read(readFrame.message, readFrame.length);
100            if (!didRead) {
101                lastErrorCode = (int)ErrorCode::ReadCorrupt;
102                StringCopy(lastErrorMessage, "Partial data in frame");
103                Close();
104                return false;
105            }
106            readFrame.message[readFrame.length] = 0;
107        }
108
109        switch (readFrame.opcode) {
110        case Opcode::Close: {
111            message.ParseInsitu(readFrame.message);
112            lastErrorCode = GetIntMember(&message, "code");
113            StringCopy(lastErrorMessage, GetStrMember(&message, "message", ""));
114            Close();
115            return false;
116        }
117        case Opcode::Frame:
118            message.ParseInsitu(readFrame.message);
119            return true;
120        case Opcode::Ping:
121            readFrame.opcode = Opcode::Pong;
122            if (!connection->Write(&readFrame, sizeof(MessageFrameHeader) + readFrame.length)) {
123                Close();
124            }
125            break;
126        case Opcode::Pong:
127            break;
128        case Opcode::Handshake:
129        default:
130            // something bad happened
131            lastErrorCode = (int)ErrorCode::ReadCorrupt;
132            StringCopy(lastErrorMessage, "Bad ipc frame");
133            Close();
134            return false;
135        }
136    }
137}