node-gamedig/reference/openttd/qstat.txt

405 lines
9.4 KiB
Plaintext

LICENSE: The Artistic License 2.0
/*
* qstat.h
* by Steve Jankowski
* steve@qstat.org
* http://www.qstat.org
*
* Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski
*/
char ottd_serverinfo[] = {
0x03, 0x00, // packet length
0x00, // packet type
};
char ottd_serverdetails[] = {
0x03, 0x00, // packet length
0x02, // packet type
};
{
/* openTTD */
OTTD_SERVER, /* id */
"OTTDS", /* type_prefix */
"ottds", /* type_string */
"-ottds", /* type_option */
"OpenTTD", /* game_name */
0, /* master */
3979, /* default_port */
0, /* port_offset */
0, /* flags */
"", /* game_rule */
"OPENTTD", /* template_var */
(char*) &ottd_serverinfo, /* status_packet */
sizeof( ottd_serverinfo), /* status_len */
NULL, /* player_packet */
0, /* player_len */
(char*) &ottd_serverdetails,/* rule_packet */
sizeof( ottd_serverdetails), /* rule_len */
NULL, /* master_packet */
0, /* master_len */
NULL, /* master_protocol */
NULL, /* master_query */
display_q2_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */
raw_display_q2_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */
xml_display_player_info,/* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */
send_ottd_request_packet, /* status_query_func */
NULL, /* rule_query_func */
NULL, /* player_query_func */
deal_with_ottd_packet, /* packet_func */
},
/*
* qstat 2.11
*
* opentTTD protocol
* Copyright 2007 Ludwig Nussel
*
* Licensed under the Artistic License, see LICENSE.txt for license terms
*/
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "qstat.h"
#include "qserver.h"
#include "debug.h"
enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 };
static const char* vehicle_types[] = {
"num_trains",
"num_trucks",
"num_busses",
"num_aircrafts",
"num_ships",
};
static const char* station_types[] = {
"num_stations",
"num_truckbays",
"num_busstations",
"num_airports",
"num_docks",
};
query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen)
{
unsigned num;
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
server->server_name = MASTER;
if(swap_short_from_little(rawpkt) != pktlen)
{
malformed_packet( server, "invalid packet length" );
return PKT_ERROR;
}
if(rawpkt[2] != 7)
{
malformed_packet( server, "invalid packet type" );
return PKT_ERROR;
}
if(rawpkt[3] != 1)
{
malformed_packet( server, "invalid packet version" );
return PKT_ERROR;
}
num = swap_short_from_little(&rawpkt[4]);
rawpkt += 6;
pktlen -= 6;
if( num && num*6 <= pktlen )
{
unsigned i;
server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen );
memset(server->master_pkt + server->master_pkt_len, 0, pktlen );
server->master_pkt_len += pktlen;
for( i = 0; i < num * 6; i += 6 )
{
memcpy(&server->master_pkt[i], &rawpkt[i], 4);
server->master_pkt[i+4] = rawpkt[i+5];
server->master_pkt[i+5] = rawpkt[i+4];
}
server->n_servers += num;
}
else
{
malformed_packet( server, "invalid packet" );
return PKT_ERROR;
}
bind_sockets();
return DONE_AUTO;
}
#define xstr(s) str(s)
#define str(s) #s
#define GET_STRING do { \
str = (char*)ptr; \
ptr = memchr(ptr, '\0', end-ptr); \
if ( !ptr ) \
{ \
malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \
return PKT_ERROR; \
} \
++ptr; \
} while(0)
#define FAIL_IF(cond, msg) \
if((cond)) { \
malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \
return PKT_ERROR; \
}
#define INVALID_IF(cond) \
FAIL_IF(cond, "invalid packet")
query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen)
{
unsigned char *ptr = (unsigned char*)rawpkt;
unsigned char *end = (unsigned char*)(rawpkt + pktlen);
unsigned char type;
char* str;
char buf[32];
unsigned ver;
server->n_servers++;
if ( server->server_name == NULL)
{
server->ping_total += time_delta( &packet_recv_time, &server->packet_time1);
server->n_requests++;
}
else
{
gettimeofday( &server->packet_time1, NULL);
}
FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet");
type = ptr[2];
ver = ptr[3];
ptr += 4;
debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver);
FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported");
if(type == 1) // info packet
{
unsigned numgrf = *ptr;
FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number");
ptr += numgrf * 20 + 1;
snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
add_rule(server, "date_days", buf, NO_FLAGS);
ptr += 4;
snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
add_rule(server, "startdate_days", buf, NO_FLAGS);
ptr += 4;
FAIL_IF(ptr + 3 > end, "invalid packet");
snprintf(buf, sizeof(buf), "%hhu", ptr[0]);
add_rule(server, "maxcompanies", buf, NO_FLAGS);
snprintf(buf, sizeof(buf), "%hhu", ptr[1]);
add_rule(server, "numcompanies", buf, NO_FLAGS);
server->max_spectators = ptr[2];
ptr += 3;
GET_STRING;
server->server_name = strdup(str);
GET_STRING;
add_rule(server, "version", str, NO_FLAGS);
FAIL_IF(ptr + 7 > end, "invalid packet");
{
static const char* langs[] = {
"any",
"English",
"German",
"French"
};
unsigned i = *ptr++;
if(i > 3) i = 0;
add_rule(server, "language", (char*)langs[i], NO_FLAGS);
}
add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS);
server->max_players = *ptr++;
server->num_players = *ptr++;
server->num_spectators = *ptr++;
GET_STRING;
server->map_name = strdup(str);
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
add_rule(server, "map_width", buf, NO_FLAGS);
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
add_rule(server, "map_height", buf, NO_FLAGS);
{
static const char* sets[] = {
"temperate",
"arctic",
"desert",
"toyland"
};
unsigned i = *ptr++;
if(i > 3) i = 0;
add_rule(server, "map_set", (char*)sets[i], NO_FLAGS);
}
add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS);
}
else if(type == 3) // player packet
{
unsigned i, j;
INVALID_IF(ptr + 2 > end);
server->num_players = *ptr++;
for(i = 0; i < server->num_players; ++i)
{
unsigned long long lli;
struct player* player;
unsigned char nr;
nr = *ptr++;
debug(3, "player number %d", nr);
player = add_player(server, i);
FAIL_IF(!player, "can't allocate player");
GET_STRING;
player->name = strdup(str);
debug(3, "name %s", str);
player->frags = 0;
INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end);
snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr));
player_add_info(player, "startdate", buf, 0);
ptr += 4;
lli = swap_long_from_little(ptr+4);
lli <<= 32;
lli += swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "value", buf, 0);
ptr += 8;
lli = swap_long_from_little(ptr+4);
lli <<= 32;
lli = swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "money", buf, 0);
ptr += 8;
lli = swap_long_from_little(ptr+4);
lli <<= 32;
lli += swap_long_from_little(ptr);
snprintf(buf, sizeof(buf), "%lld", lli);
player_add_info(player, "income", buf, 0);
ptr += 8;
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, "performance", buf, 0);
ptr += 2;
player_add_info(player, "password", *ptr?"1":"0", 0);
++ptr;
for (j = 0; j < MAX_VEHICLE_TYPES; ++j)
{
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, (char*)vehicle_types[j], buf, 0);
ptr += 2;
}
for (j = 0; j < MAX_STATION_TYPES; ++j)
{
snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr));
player_add_info(player, (char*)station_types[j], buf, 0);
ptr += 2;
}
if (ver != 5)
{
// connections
while(ptr + 1 < end && *ptr)
{
++ptr;
GET_STRING; // client name
debug(3, "%s played by %s", str, player->name);
GET_STRING; // id
INVALID_IF(ptr + 4 > end);
ptr += 4;
}
++ptr; // record terminated by zero byte
}
}
// spectators
while(ptr + 1 < end && *ptr)
{
++ptr;
GET_STRING; // client name
debug(3, "spectator %s", str);
GET_STRING; // id
INVALID_IF(ptr + 4 > end);
ptr += 4;
}
++ptr; // record terminated by zero byte
server->next_rule = NO_SERVER_RULES; // we're done
server->next_player_info = server->num_players; // we're done
}
else
{
malformed_packet( server, "invalid type" );
return PKT_ERROR;
}
server->retry1 = n_retries; // we're done with this packet, reset retry counter
return DONE_AUTO;
}
query_status_t send_ottdmaster_request_packet(struct qserver *server)
{
return qserver_send_initial(server, server->type->master_packet, server->type->master_len);
}
query_status_t send_ottd_request_packet(struct qserver *server)
{
qserver_send_initial(server, server->type->status_packet, server->type->status_len);
if(get_server_rules || get_player_info)
{
server->next_rule = ""; // trigger calling send_a2s_rule_request_packet
}
return INPROGRESS;
}