mirror of
https://github.com/Tiiffi/mcrcon.git
synced 2025-12-16 10:12:54 -05:00
Add handling for protocol quirks in different servers.
RCON protocol is quite broken and inconsistent across different server implementations. This patch adds handling for the quirks found in the following servers: - Rust - Palworld - Factorio
This commit is contained in:
62
mcrcon.c
62
mcrcon.c
@@ -116,6 +116,7 @@ static unsigned int flag_wait_seconds = 0;
|
||||
static int global_connection_alive = 1;
|
||||
static bool global_valve_protocol = false;
|
||||
static bool global_minecraft_newline_fix = false;
|
||||
static bool global_is_palworld_server = false;
|
||||
static int global_rsock;
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -644,8 +645,34 @@ bool rcon_auth(int sock, char *passwd)
|
||||
goto receive;
|
||||
}
|
||||
|
||||
if (packet->id == -1)
|
||||
return false;
|
||||
|
||||
// Quirk: check if server is Palworld server
|
||||
packet = packet_build(RCON_PID, 2, "Info");
|
||||
if (packet == NULL) {
|
||||
fprintf(stderr, "Error: packet build() failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!net_send_packet(sock, packet)) {
|
||||
fprintf(stderr, "Error: net_send_packet() failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
packet = net_recv_packet(sock);
|
||||
if (packet == NULL) {
|
||||
fprintf(stderr, "Error: net_recv_packet() failed!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Palworld response to "Info" command:
|
||||
// "Welcome to Pal Server[v0.6.9.82048] Default Palworld Server"
|
||||
if (strstr((char *) packet->data, "Pal Server["))
|
||||
global_is_palworld_server = true;
|
||||
|
||||
// return true if authentication OK
|
||||
return packet->id == -1 ? false : true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int rcon_command(int sock, char *command)
|
||||
@@ -664,8 +691,11 @@ int rcon_command(int sock, char *command)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// workaround to handle valve style multipacket responses
|
||||
packet = packet_build(0xBADA55, 0xBADA55, "");
|
||||
// Workaround to handle valve style multipacket responses
|
||||
// Factorio is very touchy with command type so we use 2 for now
|
||||
// We also send payload of '\04' (end transmission) to force reply from Factorio
|
||||
if (global_is_palworld_server == false) {
|
||||
packet = packet_build(0xBADA55, 2, "\04");
|
||||
if (packet == NULL) {
|
||||
fprintf(stderr, "Error: packet build() failed!\n");
|
||||
return 0;
|
||||
@@ -675,19 +705,20 @@ int rcon_command(int sock, char *command)
|
||||
fprintf(stderr, "Error: net_send_packet() failed!\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize stuff for select()
|
||||
fd_set read_fds;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(sock, &read_fds);
|
||||
|
||||
// Set 5 second timeout
|
||||
// Set 2 second timeout
|
||||
struct timeval timeout = {0};
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_sec = 2;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
uint8_t last_character = '\0';
|
||||
int32_t last_packet_size = 0;
|
||||
int incoming = 0;
|
||||
|
||||
do {
|
||||
@@ -697,15 +728,24 @@ int rcon_command(int sock, char *command)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is RUST server hack. Rust sends these cmd (4) "echo" packets
|
||||
// Not sure what is the purpose of the packets so ignoring for now
|
||||
if (packet->cmd == 4)
|
||||
goto select;
|
||||
|
||||
// Check for packet id and multipacket guard id
|
||||
if (packet->id != RCON_PID && packet->id != 0xBADA55) {
|
||||
// RUST server guard packet seems to have packet id -1 (0xFFFFFFFF) so allow it
|
||||
// Palworld doesn't echo RCON_PID pack, it uses 0 for everything
|
||||
if (packet->id != RCON_PID && packet->id != 0xBADA55 && packet->id != -1 && packet->id != 0) {
|
||||
fprintf(stderr, "Error: invalid packet id!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (packet->id == 0xBADA55) {
|
||||
// RUST guard packet has id -1 (0xFFFFFFFF). Not 100% if this is "guard packet" though.
|
||||
if (packet->id == 0xBADA55 || packet->id == -1) {
|
||||
// Print newline after receiving multipacket guard packet
|
||||
if (last_character != '\n') {
|
||||
// last_packet_size is to prevent extra newline if last payload packet was empty
|
||||
if (last_character != '\n' && last_packet_size != 10) {
|
||||
putchar('\n');
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -719,6 +759,12 @@ int rcon_command(int sock, char *command)
|
||||
}
|
||||
}
|
||||
|
||||
if (global_is_palworld_server)
|
||||
return 1;
|
||||
|
||||
select:
|
||||
last_packet_size = packet->size;
|
||||
|
||||
int result = select(sock + 1, &read_fds, NULL, NULL, &timeout);
|
||||
if (result == -1) {
|
||||
fprintf(stderr, "Error: select() failed!\n");
|
||||
|
||||
Reference in New Issue
Block a user