Skip to content

Commit

Permalink
fix: rcon no longer freezes if server sends no response.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaskowicz1 committed Dec 17, 2023
1 parent f4ca171 commit 31f9ddd
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 13 deletions.
6 changes: 5 additions & 1 deletion include/rcon.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ enum data_type {
struct rcon_packet {
unsigned int bytes;
unsigned char* data;

~rcon_packet() {
delete[] data;
}
};

struct rcon_queued_request {
Expand All @@ -42,7 +46,7 @@ class rcon {
const std::string address;
const unsigned int port;
const std::string password;
unsigned int sock{0};
int sock{0};
bool connected{false};

std::vector<rcon_queued_request> requests_queued{};
Expand Down
15 changes: 12 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ int main() {
event.reply(dpp::message(data).set_flags(dpp::m_ephemeral));
});
} else if (event.command.get_command_name() == "command") {
if(FDR::config.allow_achievements) {
event.reply(dpp::message("Since achievements are enabled, you are not allowed to run this command!").set_flags(dpp::m_ephemeral));
return;
}

if (std::find(event.command.member.get_roles().begin(), event.command.member.get_roles().end(), FDR::config.admin_role) == event.command.member.get_roles().end()) {
event.reply(dpp::message("You do not have the required role to run this command!").set_flags(dpp::m_ephemeral));
return;
Expand All @@ -113,6 +118,10 @@ int main() {
auto command_to_run = std::get<std::string>(event.get_parameter("cmd"));

rcon_client.send_data("/command " + command_to_run, 3, data_type::SERVERDATA_EXECCOMMAND, [event](const std::string& data) {
if(data.empty()) {
return;
}

event.reply(dpp::message(data).set_flags(dpp::m_ephemeral));
});
}
Expand Down Expand Up @@ -154,9 +163,9 @@ int main() {
});
}, 120);

rcon_client.send_data("Factorio-Discord-Relay (FDR) has loaded!", 999, data_type::SERVERDATA_EXECCOMMAND);

bot.message_create(dpp::message(FDR::config.msg_channel, "Factorio-Discord-Relay (FDR) has loaded!"));
bot.message_create(dpp::message(FDR::config.msg_channel, "Factorio-Discord-Relay (FDR) has loaded!"), [&rcon_client](const dpp::confirmation_callback_t& callback) {
rcon_client.send_data("Factorio-Discord-Relay (FDR) has loaded!", 999, data_type::SERVERDATA_EXECCOMMAND);
});
});

bot.start(dpp::st_wait);
Expand Down
41 changes: 32 additions & 9 deletions src/rcon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ bool rcon::connect() {
// Make it non-blocking.
fcntl(sock, F_SETFL, O_NONBLOCK);

// Set a timeout of 4 milliseconds.
struct timeval tv;
// Set a timeout of 4 seconds.
struct timeval tv{};
tv.tv_sec = 4;
tv.tv_usec = 0;
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

// Create temp status
int status = -1;
Expand All @@ -110,7 +111,7 @@ bool rcon::connect() {
}
}

status = select(sock +1, NULL, &fds, NULL, &tv);
status = select(sock +1, nullptr, &fds, nullptr, &tv);
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK);

// If status wasn't zero, we successfully connected.
Expand All @@ -119,7 +120,7 @@ bool rcon::connect() {

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 min_size = sizeof(id) + sizeof(type) + sizeof(nullbytes); // 10 bytes.
const int32_t data_size = static_cast<int32_t>(data.size()) + min_size;

if(data_size > 4096) {
Expand All @@ -134,13 +135,10 @@ void rcon::form_packet(unsigned char packet[], const std::string& data, int32_t
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;
}
}

Expand All @@ -149,6 +147,11 @@ std::string rcon::receive_information(int32_t id) {
// it should really just keep going for a certain amount of seconds.
for(int i=0; i < 500; i++) {
rcon_packet packet = read_packet();

if(packet.bytes == 0) {
return "";
}

unsigned char* buffer = packet.data;

int offset = packet.bytes - HEADER_SIZE + 3;
Expand All @@ -167,19 +170,39 @@ std::string rcon::receive_information(int32_t id) {

rcon_packet rcon::read_packet() {
size_t packet_length = read_packet_length();

if(packet_length == 0) {
return {0, nullptr};
}

auto* buffer = new unsigned char[packet_length]{0};
unsigned int bytes = 0;

do {
bytes += ::recv(sock, buffer, packet_length - bytes, 0);
size_t recv_bytes = ::recv(sock, buffer, packet_length - bytes, 0);
if(recv_bytes == -1) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
std::cout << "Did not receive a packet in time. Did the server send a response?";
return {0, nullptr};
}
}

bytes += recv_bytes;

} while(bytes < packet_length);

return {bytes, buffer};
}

const size_t rcon::read_packet_length() {
unsigned char* buffer = new unsigned char[4]{0};
::recv(sock, buffer, 4, 0);
size_t recv_bytes = ::recv(sock, buffer, 4, 0);
if(recv_bytes == -1) {
if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
std::cout << "Did not receive a packet in time. Did the server send a response?";
return 0;
}
}
const size_t len = byte32_to_int(buffer);
delete[] buffer;
return len;
Expand Down

0 comments on commit 31f9ddd

Please sign in to comment.