Server stat tool and script

Hey all. I wrote a simple program for querying servers for connected players, game time remaining, etc on version 1.1. Feel free to do whatever you want with it. I'm using it with some bash scripts to make graphs of my server usage. It only compiles under *nix (gcc retool.c -o retool), but shouldn't be hard to port if you know any C.
Basically you use it like
where fields is a string of numbers representing stats you'd like it to spit out. See below for example.
Edit: Quick note, the port which the server receives queries on is +1 from the game port. For example, play.redeclipse.net (74.82.3.216) runs on game port 28801. You would direct queries to port 28802. If you wanted to know the number of connected clients and the maximum number of connected clients, you could run "./retool 74.82.3.216 28802 06".
Example bash script:
Basically you use it like
- {l Code}: {l Select All Code}
./retool <server ip> <server port> <fields>
where fields is a string of numbers representing stats you'd like it to spit out. See below for example.
Edit: Quick note, the port which the server receives queries on is +1 from the game port. For example, play.redeclipse.net (74.82.3.216) runs on game port 28801. You would direct queries to port 28802. If you wanted to know the number of connected clients and the maximum number of connected clients, you could run "./retool 74.82.3.216 28802 06".
- {l Code}: {l Select All Code}
/* retool.c - a server stat tool for red eclipse v1.1 */
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
void parse_stats(unsigned char *buf, size_t len, int *stats);
int main(int argc, char **argv)
{
struct sockaddr_in si_other;
int slen;
char buf[64];
int stats[10];
int sock;
int result;
int i, k;
if (argc != 4)
{
fprintf(stderr, "usage: ./retool <server ip> <port> <stats>\n");
exit(1);
}
slen = sizeof(si_other);
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == -1)
{
fprintf(stderr, "Cannot create socket.\n");
exit(1);
}
memset((char *) &si_other, 0, slen);
si_other.sin_family = AF_INET;
si_other.sin_port = htons(atoi(argv[2]));
if (inet_aton(argv[1], &si_other.sin_addr) == 0)
{
fprintf(stderr, "Failed to locate %s!.\n", argv[1]);
exit(1);
}
/* ^shrugs^ - it works */
buf[0] = 0x81;
buf[1] = 0xE1;
buf[2] = 0x38;
buf[3] = 0x02;
result = sendto(sock, buf, 4, 0, (struct sockaddr *)&si_other, slen);
if (result == -1)
{
fprintf(stderr, "Sending failed.\n");
exit(1);
}
result = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&si_other, &slen);
if (result == -1)
{
fprintf(stderr, "Receiving failed.\n");
exit(1);
}
parse_stats(buf, sizeof(buf), stats);
char *field = argv[3];
for (i = 0; i < strlen(argv[3]); i++)
{
/*
Fields:
0 Connected Clients
1 Always equal to 8
2 Game Version
3 Game Mode
4 Mutators
5 Time Remaining (in seconds)
6 Max Clients
7 Passworded?
8 Numgamevars
9 Numgamemods
*/
if ((field[i] >= '0') || (field[i] <= '9'))
printf("%d ", stats[field[i] - '0']);
}
printf("\n");
fflush(stdout);
exit(0);
}
/* See engine/server.cpp line 161 of v1.1 */
void parse_stats(unsigned char *buf, size_t len, int *stats) {
int i, c, n, k;
i = 4;
k = 0;
while ((k < 10) && (i < len))
{
c = (char)buf[i];
i++;
if ((c == -128) && (i + 2 < len))
{
stats[k] = buf[i];
stats[k] |= buf[i + 1] << 8;
i+=2;
}
else if ((c == -127) && (i + 4 < len))
{
stats[k] = buf[i];
stats[k] |= buf[i + 1] << 8;
stats[k] |= buf[i + 2] << 16;
stats[k] |= buf[i + 3] << 24;
i+=4;
}
else
{
stats[k] = c;
}
k++;
}
}
Example bash script:
- {l Code}: {l Select All Code}
#!/bin/bash
IP=$1
PORT=$2
# Which fields to include in the output. Fields are output in the order
# in which they are provided. They correspond to the following attributes:
#
# 0 Connected Clients
# 1 Always equal to 8
# 2 Game Version
# 3 Game Mode
# 4 Mutators
# 5 Time Remaining (in seconds)
# 6 Max Clients
# 7 Does server have password?
# 8 Numgamevars
# 9 Numgamemods
# This will output connected players, max players, time remaining (in that order)
FIELDS="065"
STATS="$(./retool $IP $PORT $FIELDS)"
printf "Players %d/%d (%ds remaining)\n" ${STATS[0]} ${STATS[1]} ${STATS[2]}
exit 0