From 5679f319be85e7f452080ee185392b984e33a46a Mon Sep 17 00:00:00 2001 From: Krzyhau Date: Mon, 14 Aug 2023 22:25:30 +0200 Subject: [PATCH] untested protocol refactor --- src/Features/Tas/TasProtocol.cpp | 347 +++++++++++++++---------------- 1 file changed, 166 insertions(+), 181 deletions(-) diff --git a/src/Features/Tas/TasProtocol.cpp b/src/Features/Tas/TasProtocol.cpp index c078f18a1..bbf605d4d 100644 --- a/src/Features/Tas/TasProtocol.cpp +++ b/src/Features/Tas/TasProtocol.cpp @@ -63,14 +63,28 @@ static int g_last_debug_tick; static int g_current_debug_tick; static std::mutex g_status_mutex; -static uint32_t popRaw32(std::deque &buf) { - uint32_t val = 0; +static bool popByte(std::deque &buf, uint8_t &val) { + if (buf.size() < 1) return false; + val = buf[0]; + return true; +} + +static void encodeByte(std::deque& buf, uint8_t val) { + buf.push_back(val); +} + +static bool popRaw32(std::deque &buf, uint32_t& val) { + + if (buf.size() < 4) return false; + + val = 0; for (int i = 0; i < 4; ++i) { val <<= 8; val |= buf[0]; buf.pop_front(); } - return val; + + return true; } static void encodeRaw32(std::vector &buf, uint32_t val) { @@ -80,12 +94,33 @@ static void encodeRaw32(std::vector &buf, uint32_t val) { buf.push_back((val >> 0) & 0xFF); } -// I know this is ugly, but I think copy pasting the above code is -// stupider so whatever +static bool popRawFloat(std::deque& buf, float& val) { + return popRaw32(buf, (uint32_t&)val); +} + static void encodeRawFloat(std::vector &buf, float val) { encodeRaw32(buf, *(uint32_t *)&val); } +static bool popString(std::deque &buf, std::string &val) { + uint32_t len; + if(!popRaw32(buf, len)) return false; + if (buf.size() < len) return false; + + for (size_t i = 0; i < len; ++i) { + val += buf[0]; + buf.pop_front(); + } + return true; +} + +static void encodeString(std::vector& buf, std::string val) { + encodeRaw32(buf, (uint32_t)val.size()); + for (char c : val) { + buf.push_back(c); + } +} + static void sendAll(const std::vector &buf) { for (auto &cl : g_connections) { send(cl.sock, (const char *)buf.data(), buf.size(), 0); @@ -204,192 +239,161 @@ static void update() { } } -static bool processCommands(ConnectionData &cl) { - while (true) { - if (cl.cmdbuf.size() == 0) return true; - - size_t extra = cl.cmdbuf.size() - 1; - uint8_t packetId = cl.cmdbuf[0]; - - switch (packetId) { - case RECV_PLAY_SCRIPT: - if (extra < 8) return true; - { - std::deque copy = cl.cmdbuf; +// reads a single tas protocol command +// returns 0 if command has been handled properly +// returns 1 if there's no data to fully process a command +// returns 2 if bad command is received +static int processCommand(ConnectionData &cl) { + std::deque copy = cl.cmdbuf; - copy.pop_front(); + uint8_t packetId; + if (popByte(cl.cmdbuf, packetId)) switch (packetId) { - uint32_t len1 = popRaw32(copy); - if (extra < 8 + len1) return true; + case RECV_PLAY_SCRIPT: { + std::string filename1; + std::string filename2; - std::string filename1; - for (size_t i = 0; i < len1; ++i) { - filename1 += copy[0]; - copy.pop_front(); - } + if (!popString(cl.cmdbuf, filename1)) break; + if (!popString(cl.cmdbuf, filename2)) break; - uint32_t len2 = popRaw32(copy); - if (extra < 8 + len1 + len2) return true; + Scheduler::OnMainThread([=](){ + tasPlayer->PlayFile(filename1, filename2); + }); + + return 0; + } + case RECV_STOP: { + Scheduler::OnMainThread([=](){ + tasPlayer->Stop(true); + }); - std::string filename2; - for (size_t i = 0; i < len2; ++i) { - filename2 += copy[0]; - copy.pop_front(); - } + return 0; + } + case RECV_PLAYBACK_RATE: { + float rate; - // We actually had everything we needed, so switch to the modified buffer - cl.cmdbuf = copy; + popRawFloat(cl.cmdbuf, rate); - Scheduler::OnMainThread([=](){ - tasPlayer->PlayFile(filename1, filename2); - }); - } - break; + Scheduler::OnMainThread([=](){ + sar_tas_playback_rate.SetValue(rate); + }); + + return 0; + } + case RECV_RESUME: { + Scheduler::OnMainThread([=]() { + tasPlayer->Resume(); + }); - case RECV_STOP: - cl.cmdbuf.pop_front(); - Scheduler::OnMainThread([=](){ - tasPlayer->Stop(true); - }); - break; + return 0; + } + case RECV_PAUSE: { + Scheduler::OnMainThread([=]() { + tasPlayer->Pause(); + }); - case RECV_PLAYBACK_RATE: - if (extra < 4) return true; - cl.cmdbuf.pop_front(); - { - union { uint32_t i; float f; } rate = { popRaw32(cl.cmdbuf) }; - Scheduler::OnMainThread([=](){ - sar_tas_playback_rate.SetValue(rate.f); - }); - } - break; + return 0; + } + case RECV_FAST_FORWARD: { + int tick; + bool pause_after; - case RECV_RESUME: - cl.cmdbuf.pop_front(); - Scheduler::OnMainThread([=](){ - tasPlayer->Resume(); - }); - break; + if (!popRaw32(cl.cmdbuf, (uint32_t &)tick)) break; + if (!popByte(cl.cmdbuf, (uint8_t &)pause_after)) break; + + Scheduler::OnMainThread([=]() { + sar_tas_skipto.SetValue(tick); + if (pause_after) sar_tas_pauseat.SetValue(tick); + }); + return 0; + } + case RECV_SET_PAUSE_TICK: { + int tick; - case RECV_PAUSE: - cl.cmdbuf.pop_front(); - Scheduler::OnMainThread([=](){ - tasPlayer->Pause(); - }); - break; + if (!popRaw32(cl.cmdbuf, (uint32_t &)tick)) break; - case RECV_FAST_FORWARD: - if (extra < 5) return true; - cl.cmdbuf.pop_front(); - { - int tick = popRaw32(cl.cmdbuf); - bool pause_after = cl.cmdbuf[0]; - cl.cmdbuf.pop_front(); - Scheduler::OnMainThread([=](){ - sar_tas_skipto.SetValue(tick); - if (pause_after) sar_tas_pauseat.SetValue(tick); - }); - } - break; + Scheduler::OnMainThread([=]() { + sar_tas_pauseat.SetValue(tick); + }); + return 0; + } + case RECV_ADVANCE_TICK: { + Scheduler::OnMainThread([]() { + tasPlayer->AdvanceFrame(); + }); + return 0; + } + case RECV_MESSAGE: { + std::string message; - case RECV_SET_PAUSE_TICK: - if (extra < 4) return true; - cl.cmdbuf.pop_front(); - { - int tick = popRaw32(cl.cmdbuf); - Scheduler::OnMainThread([=](){ - sar_tas_pauseat.SetValue(tick); - }); - } - break; + if (!popString(cl.cmdbuf, message)) break; - case RECV_ADVANCE_TICK: - cl.cmdbuf.pop_front(); - Scheduler::OnMainThread([](){ - tasPlayer->AdvanceFrame(); - }); - break; + THREAD_PRINT("[TAS Protocol] %s\n", message); - case RECV_MESSAGE: - if (extra < 4) return true; - { - std::deque copy = cl.cmdbuf; + return 0; + } + case RECV_PLAY_SCRIPT_PROTOCOL: { - copy.pop_front(); + std::string slot0Name; + std::string slot0Script; + std::string slot1Name; + std::string slot1Script; - uint32_t len1 = popRaw32(copy); - if (extra < 4 + len1) return true; + if (!popString(cl.cmdbuf, slot0Name)) break; + if (!popString(cl.cmdbuf, slot0Script)) break; + if (!popString(cl.cmdbuf, slot1Name)) break; + if (!popString(cl.cmdbuf, slot1Script)) break; - std::string message; - for (size_t i = 0; i < len1; ++i) { - message += copy[0]; - copy.pop_front(); - } + Scheduler::OnMainThread([=]() { + tasPlayer->PlayScript(slot0Name, slot0Script, slot1Name, slot1Script); + }); - // We actually had everything we needed, so switch to the modified buffer - cl.cmdbuf = copy; + return 0; + } + case RECV_ENTITY_INFO: + case RECV_SET_CONT_ENTITY_INFO: { + std::string entSelector; - THREAD_PRINT("[TAS Protocol] %s\n", message); - } - break; + if (!popString(cl.cmdbuf, entSelector)) break; - case RECV_PLAY_SCRIPT_PROTOCOL: - if (extra < 16) return true; - { - std::deque copy = cl.cmdbuf; + if (packetId == RECV_SET_CONT_ENTITY_INFO) { + cl.contInfoEntSelector = entSelector; + } else { + SendEntityInfo(cl, entSelector); + } + return 0; + } + default: + // Bad command - disconnect + return 2; + } - copy.pop_front(); + // command hasn't been fully read - recover to the copy + cl.cmdbuf = copy; + return 1; +} - std::string data[4]; - uint32_t size_total = 0; - for (int i = 0; i < 4; ++i) { - uint32_t len = popRaw32(copy); - size_total += len; - if (extra < 16 + size_total) return true; +static bool receiveFromConnection(TasProtocol::ConnectionData &cl) { + char buf[1024]; + int len = recv(cl.sock, buf, sizeof buf, 0); - for (size_t j = 0; j < len; ++j) { - data[i] += copy[0]; - copy.pop_front(); - } - } + if (len == 0 || len == SOCKET_ERROR) { // Connection closed or errored + return false; + } - cl.cmdbuf = copy; // We actually had everything we needed, so switch to the modified buffer + cl.cmdbuf.insert(cl.cmdbuf.end(), std::begin(buf), std::begin(buf) + len); - Scheduler::OnMainThread([=]() { - tasPlayer->PlayScript(data[0], data[1], data[2], data[3]); - }); - } - break; - case RECV_ENTITY_INFO: - case RECV_SET_CONT_ENTITY_INFO: - if (extra < 4) return true; - { - std::deque copy = cl.cmdbuf; - copy.pop_front(); - - uint32_t len = popRaw32(copy); - if (extra < 4 + len) return true; - - std::string entSelector; - for (size_t i = 0; i < len; ++i) { - entSelector += copy[0]; - copy.pop_front(); - } - - cl.cmdbuf = copy; - - if (packetId == RECV_SET_CONT_ENTITY_INFO) { - cl.contInfoEntSelector = entSelector; - } else { - SendEntityInfo(cl, entSelector); - } - } - break; + while (true) { + int result = processCommand(cl); - default: - return false; // Bad command - disconnect + if (result == 2) { + closesocket(cl.sock); + return false; } + if (result == 1) break; } + + return true; } static bool attemptToInitializeServer() { @@ -472,25 +476,6 @@ static bool attemptConnectionToServer() { return true; } -static bool receiveFromConnection(TasProtocol::ConnectionData &cl) { - char buf[1024]; - int len = recv(cl.sock, buf, sizeof buf, 0); - - if (len == 0 || len == SOCKET_ERROR) { // Connection closed or errored - return false; - } - - cl.cmdbuf.insert(cl.cmdbuf.end(), std::begin(buf), std::begin(buf) + len); - - if (!processCommands(cl)) { - // Client sent a bad command; terminate connection - closesocket(cl.sock); - return false; - } - - return true; -} - static void processConnections(bool is_server) { fd_set set; FD_ZERO(&set);