From 18181e080d604c9c32bdca124e1b0e075a7b4fce Mon Sep 17 00:00:00 2001 From: Archie Jaskowicz Date: Sun, 17 Dec 2023 14:50:52 +0000 Subject: [PATCH] feat: added CIs, added more documentation, rename main.hpp to main.h --- .github/pull_request_template.md | 5 + .github/workflows/ci.yml | 65 ++++++++ .gitignore | 14 +- include/main.h | 1 + include/main.hpp | 5 - include/rcon.h | 73 ++++---- src/main.cpp | 174 +++++++++---------- src/rcon.cpp | 278 +++++++++++++++---------------- 8 files changed, 349 insertions(+), 266 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ci.yml create mode 100644 include/main.h delete mode 100644 include/main.hpp diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..278c4bb --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +Replace this line with a summary of your changes. + +- [ ] My PR is titled using the [convention commits style](https://www.conventionalcommits.org/en/v1.0.0/) +- [ ] I tested my changes before raising the PR. +- [ ] I have not built my pull request using AI, a static analysis tool, or similar, without any human oversight. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e550f3b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: D++ CI +on: + push: + paths: + - '**Dockerfile' + - '**.cxx' + - '**.cpp' + - '**.h' + - '**.hpp' + - '**.cmake' + - '**ci.yml' + - '**CMakeLists.txt' + pull_request: + paths: + - '**Dockerfile' + - '**.cxx' + - '**.cpp' + - '**.h' + - '**.hpp' + - '**.cmake' + - '**ci.yml' + - '**CMakeLists.txt' + - '!**/docpages/**' + +permissions: + contents: read + +jobs: + linux: + permissions: + contents: write + name: Linux ${{matrix.cfg.arch}} (${{matrix.cfg.cpp-version}}) + runs-on: ${{matrix.cfg.os}} + strategy: + fail-fast: false # Don't fail everything if one fails. We want to test each OS/Compiler individually + matrix: + cfg: + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: clang-13, cpp-version: clang++-13} + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: clang-14, cpp-version: clang++-14} + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: clang-15, cpp-version: clang++-15} + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: g++-13, cpp-version: g++-13} + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: g++-12, cpp-version: g++-12} + - { arch: 'amd64', concurrency: 2, os: ubuntu-22.04, package: g++-11, cpp-version: g++-11} + steps: + - name: Harden Runner + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + with: + egress-policy: audit + + - name: Checkout FDR + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Install apt packages + run: sudo apt update && sudo apt-get install -y ${{ matrix.cfg.package }} pkg-config libsodium-dev libopus-dev zlib1g-dev rpm + + - name: Install DPP + run: wget -O dpp.deb https://dl.dpp.dev/ && sudo dpkg -i dpp.deb + + - name: Generate CMake + run: cmake -B build + env: + CXX: ${{matrix.cfg.cpp-version}} + + - name: Build Project + run: cmake --build build -j${{ matrix.cfg.concurrency }} diff --git a/.gitignore b/.gitignore index 84f79e6..2fa4afc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,16 @@ +# IDE stuff +.idea +.vs + +# Build directories +_build +build +cmake-build-debug + +# Xcode stuff *.xcworkspace *.xcuserdatad *.xcscheme -_build -build + +# Apple stuff. .DS_Store diff --git a/include/main.h b/include/main.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/include/main.h @@ -0,0 +1 @@ +#pragma once diff --git a/include/main.hpp b/include/main.hpp deleted file mode 100644 index 6c0c887..0000000 --- a/include/main.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -namespace Main { - -} diff --git a/include/rcon.h b/include/rcon.h index d99c513..233ed02 100644 --- a/include/rcon.h +++ b/include/rcon.h @@ -14,10 +14,21 @@ #define HEADER_SIZE 14 enum data_type { - SERVERDATA_AUTH = 3, - SERVERDATA_AUTH_RESPONSE = 2, - SERVERDATA_EXECCOMMAND = 2, - SERVERDATA_RESPONSE_VALUE = 0 + /** + * @brief A command packet. + * + * @note The server *may* send a `SERVERDATA_RESPONSE_VALUE` packet if the request was successful. + * However, The server can also not send a packet back if it only processes the packet and does nothing else. + * You should take this into account by either not using the callback or by turning feedback off. + */ + SERVERDATA_EXECCOMMAND = 2, + + /** + * @brief An authorisation packet. + * + * The server will send an empty `SERVERDATA_AUTH_RESPONSE` packet if the request was successful. + */ + SERVERDATA_AUTH = 3, }; struct rcon_packet { @@ -35,11 +46,11 @@ struct rcon_queued_request { }; class rcon { - const std::string address; - const unsigned int port; - const std::string password; - unsigned int sock{0}; - bool connected{false}; + const std::string address; + const unsigned int port; + const std::string password; + unsigned int sock{0}; + bool connected{false}; std::vector requests_queued{}; @@ -51,7 +62,7 @@ class rcon { * @note This is a blocking call (done on purpose). It needs to wait to connect to the RCON server before anything else happens. * It will timeout after 4 seconds if it can't connect. */ - rcon(const std::string& addr, const unsigned int _port, const std::string& pass); + rcon(const std::string& addr, const unsigned int _port, const std::string& pass); /** * @brief Send data to the connected RCON server. Requests from this function are added to a queue (`requests_queued`) and are handled by a different thread. @@ -76,18 +87,18 @@ class rcon { * * @returns Data given by the server from the request. */ - const std::string send_data_sync(const std::string data, const int32_t id, data_type type, bool feedback = true); + const std::string send_data_sync(const std::string data, const int32_t id, data_type type, bool feedback = true); private: - /** - * @brief Connects to RCON using `address`, `port`, and `password`. - * Those values are pre-filled when constructing this class. - * - * @warning This should only ever be called by the constructor. - * The constructor calls this function once it has filled in the required data and proceeds to login. - */ - bool connect(); + /** + * @brief Connects to RCON using `address`, `port`, and `password`. + * Those values are pre-filled when constructing this class. + * + * @warning This should only ever be called by the constructor. + * The constructor calls this function once it has filled in the required data and proceeds to login. + */ + bool connect(); /** * @brief Form a valid RCON packet. @@ -106,22 +117,22 @@ class rcon { * * @return Data given by the server. */ - std::string receive_information(int32_t id); + std::string receive_information(int32_t id); /** * @brief Gathers all the packet's content (based on the length returned by `read_packet_length`) */ - const rcon_packet read_packet(); + rcon_packet read_packet(); - inline const size_t read_packet_length() { - unsigned char* buffer = new unsigned char[4]{0}; - ::recv(sock, buffer, 4, 0); - const size_t len = byte32_to_int(buffer); - delete[] buffer; - return len; - } + inline const size_t read_packet_length() { + unsigned char* buffer = new unsigned char[4]{0}; + ::recv(sock, buffer, 4, 0); + const size_t len = byte32_to_int(buffer); + delete[] buffer; + return len; + } - inline const size_t byte32_to_int(unsigned char* buffer) { - return static_cast(buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24); - } + inline const size_t byte32_to_int(unsigned char* buffer) { + return static_cast(buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24); + } }; diff --git a/src/main.cpp b/src/main.cpp index 3d8c0cc..00b4f05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,100 +1,100 @@ -#include "main.hpp" +#include "../include/main.h" -#include "rcon.h" +#include "../include/rcon.h" #include int main(int argc, char *argv[]) { + + // ./fdr ip port password bot_token channel_id + if(argc < 6) { + std::cout << "Not enough arguments specified. Please check the command you are running." << "\n"; + return 0; + } - // ./fdr ip port password bot_token channel_id - if(argc < 6) { - std::cout << "Not enough arguments specified. Please check the command you are running." << "\n"; - return 0; - } - - unsigned int port = std::atoi(argv[2]); - const dpp::snowflake msg_channel{argv[5]}; + unsigned int port = std::atoi(argv[2]); + const dpp::snowflake msg_channel{argv[5]}; - rcon rcon_client{argv[1], port, argv[3]}; + rcon rcon_client{argv[1], port, argv[3]}; - dpp::cluster bot(argv[4], dpp::i_default_intents | dpp::i_message_content, 0, 0, 1, true, dpp::cache_policy::cpol_none); + dpp::cluster bot(argv[4], dpp::i_default_intents | dpp::i_message_content, 0, 0, 1, true, dpp::cache_policy::cpol_none); - /* Output simple log messages to stdout */ - bot.on_log(dpp::utility::cout_logger()); + /* Output simple log messages to stdout */ + bot.on_log(dpp::utility::cout_logger()); - bot.on_message_create([&rcon_client, msg_channel](const dpp::message_create_t& event) -> dpp::task { - if(event.msg.channel_id == msg_channel && !event.msg.author.is_bot()) { - // ID here doesn't matter really, we're not wanting a response. - rcon_client.send_data(event.msg.content, 999, data_type::SERVERDATA_EXECCOMMAND); - } - - co_return; - }); + bot.on_message_create([&rcon_client, msg_channel](const dpp::message_create_t& event) { + if(event.msg.channel_id == msg_channel && !event.msg.author.is_bot()) { + // ID here doesn't matter really, we're not wanting a response. + rcon_client.send_data(event.msg.content, 999, data_type::SERVERDATA_EXECCOMMAND); + } + }); - bot.on_slashcommand([&rcon_client](const dpp::slashcommand_t& event) { - if (event.command.get_command_name() == "evolution") { - rcon_client.send_data("/evolution", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } else if (event.command.get_command_name() == "time") { - rcon_client.send_data("/time", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } else if (event.command.get_command_name() == "version") { - rcon_client.send_data("/version", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } else if (event.command.get_command_name() == "players") { - rcon_client.send_data("/players online", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } else if (event.command.get_command_name() == "seed") { - rcon_client.send_data("/seed", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } else if (event.command.get_command_name() == "command") { - rcon_client.send_data("/command ", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { - event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); - }); - } - }); + bot.on_slashcommand([&rcon_client](const dpp::slashcommand_t& event) { + if (event.command.get_command_name() == "evolution") { + rcon_client.send_data("/evolution", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } else if (event.command.get_command_name() == "time") { + rcon_client.send_data("/time", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } else if (event.command.get_command_name() == "version") { + rcon_client.send_data("/version", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } else if (event.command.get_command_name() == "players") { + rcon_client.send_data("/players online", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } else if (event.command.get_command_name() == "seed") { + rcon_client.send_data("/seed", 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } else if (event.command.get_command_name() == "command") { + auto command_to_run = std::get(event.get_parameter("cmd")); - /* Register slash command here in on_ready */ - bot.on_ready([&bot, &rcon_client](const dpp::ready_t& event) { - /* Wrap command registration in run_once to make sure it doesnt run on every full reconnection */ - if (dpp::run_once()) { - dpp::slashcommand evolution_command("evolution", "See the current evolution", bot.me.id); - dpp::slashcommand time_command("time", "See the current time", bot.me.id); - dpp::slashcommand version_command("version", "See the current version", bot.me.id); - dpp::slashcommand players_command("players", "See the current amount players", bot.me.id); - dpp::slashcommand seed_command("seed", "See the current seed", bot.me.id); - - bot.global_bulk_command_create({ evolution_command, time_command, version_command, players_command, seed_command }); - } - - rcon_client.send_data("/players online count", 2, data_type::SERVERDATA_EXECCOMMAND, [&bot](const std::string& data) { - std::string players = data; - std::replace(players.begin(), players.end(), ':', ' '); - std::replace(players.begin(), players.end(), '(', ' '); - std::replace(players.begin(), players.end(), ')', ' '); - bot.set_presence(dpp::presence(dpp::presence_status::ps_online, dpp::at_custom, players)); - }); - - /* Create a timer that runs every 120 seconds, that sets the status */ - bot.start_timer([&bot, &rcon_client](const dpp::timer& timer) { - rcon_client.send_data("/players online count", 2, data_type::SERVERDATA_EXECCOMMAND, [&bot](const std::string& data) { - std::string players = data; - std::replace(players.begin(), players.end(), ':', ' '); - std::replace(players.begin(), players.end(), '(', ' '); - std::replace(players.begin(), players.end(), ')', ' '); - bot.set_presence(dpp::presence(dpp::presence_status::ps_online, dpp::at_custom, players)); - }); - }, 120); - - //std::string response = client.send("/players"); - //std::cout << response << "\n"; - }); + rcon_client.send_data("/command " + command_to_run, 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) { + event.reply(dpp::message(data).set_flags(dpp::m_ephemeral)); + }); + } + }); - - bot.start(dpp::st_wait); - return 0; + /* Register slash command here in on_ready */ + bot.on_ready([&bot, &rcon_client](const dpp::ready_t& event) { + /* Wrap command registration in run_once to make sure it doesnt run on every full reconnection */ + if (dpp::run_once()) { + dpp::slashcommand evolution_command("evolution", "See the current evolution", bot.me.id); + dpp::slashcommand time_command("time", "See the current time", bot.me.id); + dpp::slashcommand version_command("version", "See the current version", bot.me.id); + dpp::slashcommand players_command("players", "See the current amount players", bot.me.id); + dpp::slashcommand seed_command("seed", "See the current seed", bot.me.id); + dpp::slashcommand cmd_command("command", "Run any command!", bot.me.id); + + cmd_command.add_option(dpp::command_option(dpp::co_string, "cmd", "the command to run!", true)); + + bot.global_bulk_command_create({ evolution_command, time_command, version_command, players_command, seed_command, cmd_command }); + } + + rcon_client.send_data("/players online count", 2, data_type::SERVERDATA_EXECCOMMAND, + [&bot](const std::string& data) { + std::string players = data; + std::replace(players.begin(), players.end(), ':', ' '); + std::replace(players.begin(), players.end(), '(', ' '); + std::replace(players.begin(), players.end(), ')', ' '); + bot.set_presence(dpp::presence(dpp::presence_status::ps_online, dpp::at_custom, players)); + }); + + /* Create a timer that runs every 120 seconds, that sets the status */ + bot.start_timer([&bot, &rcon_client](const dpp::timer& timer) { + rcon_client.send_data("/players online count", 2, data_type::SERVERDATA_EXECCOMMAND, [&bot](const std::string& data) { + std::string players = data; + std::replace(players.begin(), players.end(), ':', ' '); + std::replace(players.begin(), players.end(), '(', ' '); + std::replace(players.begin(), players.end(), ')', ' '); + bot.set_presence(dpp::presence(dpp::presence_status::ps_online, dpp::at_custom, players)); + }); + }, 120); + }); + + bot.start(dpp::st_wait); + return 0; } diff --git a/src/rcon.cpp b/src/rcon.cpp index 4a83dc0..9c1f889 100644 --- a/src/rcon.cpp +++ b/src/rcon.cpp @@ -1,173 +1,169 @@ -#include "rcon.h" +#include "../include/rcon.h" rcon::rcon(const std::string& addr, const unsigned int _port, const std::string& pass) : address(addr), port(_port), password(pass) { - std::cout << "Attempting connection to RCON server..." << "\n"; + std::cout << "Attempting connection to RCON server..." << "\n"; - if(!connect()) { - std::cout << "RCON is aborting as it failed to initiate." << "\n"; - close(sock); - return; - } + if(!connect()) { + std::cout << "RCON is aborting as it failed to initiate." << "\n"; + close(sock); + return; + } - connected = true; + connected = true; - std::cout << "Connected successfully! Sending login data..." << "\n"; + std::cout << "Connected successfully! Sending login data..." << "\n"; - // The server will send SERVERDATA_AUTH_RESPONSE once it's happy. - send_data_sync(pass, 1, data_type::SERVERDATA_AUTH); - - std::thread queue_runner([this]() { - while(connected) { - if(requests_queued.empty()) { - continue; - } + // The server will send SERVERDATA_AUTH_RESPONSE once it's happy. + send_data_sync(pass, 1, data_type::SERVERDATA_AUTH); + + std::thread queue_runner([this]() { + while(connected) { + if(requests_queued.empty()) { + continue; + } - for(rcon_queued_request request : requests_queued) { - // Send data to callback if it's been set. - if(request.callback) - request.callback(send_data_sync(request.data, request.id, request.type)); - else - send_data_sync(request.data, request.id, request.type, false); - } + for(rcon_queued_request request : requests_queued) { + // Send data to callback if it's been set. + if(request.callback) + request.callback(send_data_sync(request.data, request.id, request.type)); + else + send_data_sync(request.data, request.id, request.type, false); + } - requests_queued.clear(); - } - }); + requests_queued.clear(); + } + }); - queue_runner.detach(); + queue_runner.detach(); }; void rcon::send_data(const std::string& data, const int32_t id, data_type type, std::function callback) { - requests_queued.emplace_back(data, id, type, callback); + requests_queued.emplace_back(data, id, type, callback); } const std::string rcon::send_data_sync(const std::string data, const int32_t id, data_type type, bool feedback) { - if(!connected) { - std::cout << "Cannot send data when not connected." << "\n"; - return ""; - } - - unsigned long long packet_len = data.length() + HEADER_SIZE; - unsigned char packet[packet_len]; - form_packet(packet, data, id, type); - - if(::send(sock, packet, packet_len, 0) < 0) { - std::cout << "Sending failed!" << "\n"; - return ""; - } - - if(type != SERVERDATA_EXECCOMMAND || !feedback) { - return ""; - } - - // Server will send a SERVERDATA_RESPONSE_VALUE packet. - return receive_information(id); + if(!connected) { + std::cout << "Cannot send data when not connected." << "\n"; + return ""; + } + + unsigned long long packet_len = data.length() + HEADER_SIZE; + unsigned char packet[packet_len]; + form_packet(packet, data, id, type); + + if(::send(sock, packet, packet_len, 0) < 0) { + std::cout << "Sending failed!" << "\n"; + return ""; + } + + if(type != SERVERDATA_EXECCOMMAND || !feedback) { + return ""; + } + + // Server will send a SERVERDATA_RESPONSE_VALUE packet. + return receive_information(id); } bool rcon::connect() { - // Create new TCP socket. - sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - if(sock == -1) { - std::cout << "Failed to open socket." << "\n"; - return false; - } - - // Setup port, address, and family. - struct sockaddr_in server; - server.sin_family = AF_INET; - server.sin_addr.s_addr = inet_addr(address.c_str()); - server.sin_port = htons(port); - - // Make it non blocking. - fcntl(sock, F_SETFL, O_NONBLOCK); - - // Set a timeout of 4 milliseconds. - struct timeval tv; - tv.tv_sec = 4; - tv.tv_usec = 0; - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); - - // Create temp status - int status = -1; - - // Connect to the socket and set the status to our temp status. - if((status = ::connect(sock, (struct sockaddr *)&server, sizeof(server))) == -1) { - if(errno != EINPROGRESS) { - return false; - } - } - - status = select(sock +1, NULL, &fds, NULL, &tv); - fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK); - - // If status was 0, abort. - if(status == 0) { - return false; - } - - return true; + // Create new TCP socket. + sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if(sock == -1) { + std::cout << "Failed to open socket." << "\n"; + return false; + } + + // Setup port, address, and family. + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = inet_addr(address.c_str()); + server.sin_port = htons(port); + + // Make it non blocking. + fcntl(sock, F_SETFL, O_NONBLOCK); + + // Set a timeout of 4 milliseconds. + struct timeval tv; + tv.tv_sec = 4; + tv.tv_usec = 0; + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + // Create temp status + int status = -1; + + // Connect to the socket and set the status to our temp status. + if((status = ::connect(sock, (struct sockaddr *)&server, sizeof(server))) == -1) { + if(errno != EINPROGRESS) { + return false; + } + } + + status = select(sock +1, NULL, &fds, NULL, &tv); + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK); + + // If status wasn't zero, we successfully connected. + return status != 0; } void rcon::form_packet(unsigned char packet[], const std::string& data, int32_t id, int32_t type) { - const char nullbytes[] = {'\x00', '\x00'}; - const int32_t min_size = sizeof(id) + sizeof(type) + sizeof(nullbytes); - const int32_t data_size = static_cast(data.size()) + min_size; - - if(data_size > 4096) { - std::cout << "This packet is too big to send. Please generate a smaller packet." << "\n"; - return; - } - - bzero(packet, data_size); + const char nullbytes[] = {'\x00', '\x00'}; + const int32_t min_size = sizeof(id) + sizeof(type) + sizeof(nullbytes); + const int32_t data_size = static_cast(data.size()) + min_size; - // Each part is 4 bytes, so we allocate each part 4 bytes away. - packet[0] = data_size; - packet[4] = id; - packet[8] = type; + if(data_size > 4096) { + std::cout << "This packet is too big to send. Please generate a smaller packet." << "\n"; + return; + } - int write_nullbytes_at{0}; + bzero(packet, data_size); - const char* data_chars = data.c_str(); - - for(int i = 0; i < data_size; i++) { - packet[12 + i] = data_chars[i]; - write_nullbytes_at = 13 + i; - } + // Each part is 4 bytes, so we allocate each part 4 bytes away. + packet[0] = data_size; + packet[4] = id; + packet[8] = type; + + int write_nullbytes_at{0}; + + const char* data_chars = data.c_str(); + + for(int i = 0; i < data_size; i++) { + packet[12 + i] = data_chars[i]; + write_nullbytes_at = 13 + i; + } } std::string rcon::receive_information(int32_t id) { - // Whilst this loop is better than a while loop, - // it should really just keep going for a certain amount of seconds. - for(int i=0; i < 500; i++) { - rcon_packet packet = read_packet(); - unsigned char* buffer = packet.data; - - int offset = packet.bytes - HEADER_SIZE + 3; - - if(offset == -1) - continue; - - std::string part(&buffer[8], &buffer[8] +offset); - - if(byte32_to_int(packet.data) == id) { - return part; - } - } - return ""; + // Whilst this loop is better than a while loop, + // it should really just keep going for a certain amount of seconds. + for(int i=0; i < 500; i++) { + rcon_packet packet = read_packet(); + unsigned char* buffer = packet.data; + + int offset = packet.bytes - HEADER_SIZE + 3; + + if(offset == -1) + continue; + + std::string part(&buffer[8], &buffer[8] + offset); + + if(byte32_to_int(packet.data) == id) { + return part; + } + } + return ""; } -const rcon_packet rcon::read_packet() { - size_t packet_length = read_packet_length(); - unsigned char* buffer = new unsigned char[packet_length]{0}; - unsigned int bytes = 0; - - do { - bytes += ::recv(sock, buffer, packet_length - bytes, 0); - } while(bytes < packet_length); +rcon_packet rcon::read_packet() { + size_t packet_length = read_packet_length(); + auto* buffer = new unsigned char[packet_length]{0}; + unsigned int bytes = 0; + + do { + bytes += ::recv(sock, buffer, packet_length - bytes, 0); + } while(bytes < packet_length); - return {bytes, buffer}; + return {bytes, buffer}; }