node-gamedig/reference/tribes2/qstat.txt

456 lines
10 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
*/
/* TRIBES 2 */
#define TRIBES2_QUERY_GAME_TYPES 2
#define TRIBES2_QUERY_MASTER 6
#define TRIBES2_QUERY_PING 14
#define TRIBES2_QUERY_INFO 18
#define TRIBES2_RESPONSE_GAME_TYPES 4
#define TRIBES2_RESPONSE_MASTER 8
#define TRIBES2_RESPONSE_PING 16
#define TRIBES2_RESPONSE_INFO 20
#define TRIBES2_NO_COMPRESS 2
#define TRIBES2_DEFAULT_PACKET_INDEX 255
#define TRIBES2_STATUS_DEDICATED (1<<0)
#define TRIBES2_STATUS_PASSWORD (1<<1)
#define TRIBES2_STATUS_LINUX (1<<2)
#define TRIBES2_STATUS_TOURNAMENT (1<<3)
#define TRIBES2_STATUS_NOALIAS (1<<4)
#define TRIBES2_STATUS_TEAMDAMAGE (1<<5)
#define TRIBES2_STATUS_TOURNAMENT_VER3 (1<<6)
#define TRIBES2_STATUS_NOALIAS_VER3 (1<<7)
char tribes2_game_types_request[] = { TRIBES2_QUERY_GAME_TYPES, 0, 1,2,3,4 };
char tribes2_ping[] = { TRIBES2_QUERY_PING, TRIBES2_NO_COMPRESS, 1,2,3,4 };
char tribes2_info[] = { TRIBES2_QUERY_INFO, TRIBES2_NO_COMPRESS, 1,2,3,4 };
unsigned char tribes2_masterquery[] = {
TRIBES2_QUERY_MASTER, 128, /* <= build 22228, this was 0 */
11,12,13,14,
255,
3, 'a', 'n', 'y',
3, 'a', 'n', 'y',
0, 255, /* min/max players */
0xff, 0xff, 0xff, 0xff, /* region mask */
0, 0, 0, 0, /* build version */
0, /* status */
255, /* max bots */
0, 0, /* min cpu */
0 /* # buddies */
};
#define TRIBES2_ID_OFFSET 2
{
/* TRIBES 2 */
TRIBES2_SERVER, /* id */
"T2S", /* type_prefix */
"t2s", /* type_string */
"-t2s", /* type_option */
"Tribes 2", /* game_name */
0, /* master */
TRIBES2_DEFAULT_PORT, /* default_port */
0, /* port_offset */
0, /* flags */
"game", /* game_rule */
"TRIBES2", /* template_var */
(char*) &tribes2_ping, /* status_packet */
sizeof( tribes2_ping), /* status_len */
(char*) &tribes2_info, /* player_packet */
sizeof( tribes2_info), /* player_len */
(char*) NULL, /* rule_packet */
0, /* rule_len */
NULL, /* master_packet */
0, /* master_len */
NULL, /* master_protocol */
NULL, /* master_query */
display_tribes2_player_info, /* display_player_func */
display_server_rules, /* display_rule_func */
raw_display_tribes2_player_info, /* display_raw_player_func */
raw_display_server_rules, /* display_raw_rule_func */
xml_display_tribes2_player_info, /* display_xml_player_func */
xml_display_server_rules, /* display_xml_rule_func */
send_tribes2_request_packet, /* status_query_func */
NULL, /* rule_query_func */
NULL, /* player_query_func */
deal_with_tribes2_packet, /* packet_func */
},
query_status_t send_tribes2_request_packet(struct qserver *server)
{
int rc;
if (server->flags &FLAG_BROADCAST && server->server_name == NULL)
{
rc = send_broadcast(server, server->type->status_packet, server->type->status_len);
}
else if (server->server_name == NULL)
{
rc = send(server->fd, server->type->status_packet, server->type->status_len, 0);
}
else
{
rc = send(server->fd, server->type->player_packet, server->type->player_len, 0);
}
if (rc == SOCKET_ERROR)
{
return send_error( server, rc );
}
register_send(server);
return rc;
}
void get_tribes2_player_type(struct player *player)
{
char *name = player->name;
for (; *name; name++)
{
switch (*name)
{
case 0x8:
player->type_flag = PLAYER_TYPE_NORMAL;
continue;
case 0xc:
player->type_flag = PLAYER_TYPE_ALIAS;
continue;
case 0xe:
player->type_flag = PLAYER_TYPE_BOT;
continue;
case 0xb:
break;
default:
continue;
}
name++;
if (isprint(*name))
{
char *n = name;
for (; isprint(*n); n++)
;
player->tribe_tag = strndup(name, n - name);
name = n;
}
if (! *name)
{
break;
}
}
}
query_status_t deal_with_tribes2_packet(struct qserver *server, char *pkt, int pktlen)
{
char str[256], *pktstart = pkt, *term, *start;
unsigned int minimum_net_protocol, build_version, i, t, len, s, status;
unsigned int net_protocol;
unsigned short cpu_speed;
int n_teams = 0, n_players;
struct player **teams = NULL, *player;
struct player **last_player = &server->players;
int query_version;
debug( 2, "deal_with_tribes2_packet %p, %d", server, pktlen );
pkt[pktlen] = '\0';
if (server->server_name == NULL)
{
server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
}
/*
else
gettimeofday( &server->packet_time1, NULL);
*/
if (pkt[0] == TRIBES2_RESPONSE_PING)
{
if (pkt[6] < 4 || pkt[6] > 12 || strncmp(pkt + 7, "VER", 3) != 0)
{
return PKT_ERROR;
}
strncpy(str, pkt + 10, pkt[6] - 3);
str[pkt[6] - 3] = '\0';
query_version = atoi(str);
add_nrule(server, "queryversion", pkt + 7, pkt[6]);
pkt += 7+pkt[6];
server->protocol_version = query_version;
if (query_version != 3 && query_version != 5)
{
server->server_name = strdup("Unknown query version");
return PKT_ERROR;
}
if (query_version == 5)
{
net_protocol = swap_long_from_little(pkt);
sprintf(str, "%u", net_protocol);
add_rule(server, "net_protocol", str, NO_FLAGS);
pkt += 4;
}
minimum_net_protocol = swap_long_from_little(pkt);
sprintf(str, "%u", minimum_net_protocol);
add_rule(server, "minimum_net_protocol", str, NO_FLAGS);
pkt += 4;
build_version = swap_long_from_little(pkt);
sprintf(str, "%u", build_version);
add_rule(server, "build_version", str, NO_FLAGS);
pkt += 4;
server->server_name = strndup(pkt + 1, *(unsigned char*)(pkt));
/* Always send the player request because the ping packet
* contains very little information */
send_player_request_packet(server);
return 0;
}
else if (pkt[0] != TRIBES2_RESPONSE_INFO)
{
return PKT_ERROR;
}
pkt += 6;
for (i = 0; i < *(unsigned char*)pkt; i++)
if (!isprint(pkt[i + 1]))
{
return PKT_ERROR;
}
add_nrule(server, server->type->game_rule, pkt + 1, *(unsigned char*)pkt);
server->game = strndup(pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1;
add_nrule(server, "mission", pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1;
server->map_name = strndup(pkt + 1, *(unsigned char*)pkt);
pkt += *pkt + 1;
status = *(unsigned char*)pkt;
sprintf(str, "%u", status);
add_rule(server, "status", str, NO_FLAGS);
if (status &TRIBES2_STATUS_DEDICATED)
{
add_rule(server, "dedicated", "1", NO_FLAGS);
}
if (status &TRIBES2_STATUS_PASSWORD)
{
add_rule(server, "password", "1", NO_FLAGS);
}
if (status &TRIBES2_STATUS_LINUX)
{
add_rule(server, "linux", "1", NO_FLAGS);
}
if (status &TRIBES2_STATUS_TEAMDAMAGE)
{
add_rule(server, "teamdamage", "1", NO_FLAGS);
}
if (server->protocol_version == 3)
{
if (status &TRIBES2_STATUS_TOURNAMENT_VER3)
{
add_rule(server, "tournament", "1", NO_FLAGS);
}
if (status &TRIBES2_STATUS_NOALIAS_VER3)
{
add_rule(server, "no_aliases", "1", NO_FLAGS);
}
}
else
{
if (status &TRIBES2_STATUS_TOURNAMENT)
{
add_rule(server, "tournament", "1", NO_FLAGS);
}
if (status &TRIBES2_STATUS_NOALIAS)
{
add_rule(server, "no_aliases", "1", NO_FLAGS);
}
}
pkt++;
server->num_players = *(unsigned char*)pkt;
pkt++;
server->max_players = *(unsigned char*)pkt;
pkt++;
sprintf(str, "%u", *(unsigned char*)pkt);
add_rule(server, "bot_count", str, NO_FLAGS);
pkt++;
cpu_speed = swap_short_from_little(pkt);
sprintf(str, "%hu", cpu_speed);
add_rule(server, "cpu_speed", str, NO_FLAGS);
pkt += 2;
if (strcmp(server->server_name, "VER3") == 0)
{
free(server->server_name);
server->server_name = strndup(pkt + 1, *(unsigned char*)pkt);
}
else
{
add_nrule(server, "info", pkt + 1, *(unsigned char*)pkt);
}
pkt += *(unsigned char*)pkt + 1;
len = swap_short_from_little(pkt);
pkt += 2;
start = pkt;
if (len + (pkt - pktstart) > pktlen)
{
len -= (len + (pkt - pktstart)) - pktlen;
}
if (len == 0 || pkt - pktstart >= pktlen)
{
goto info_done;
}
term = strchr(pkt, 0xa);
if (!term)
{
goto info_done;
}
*term = '\0';
n_teams = atoi(pkt);
sprintf(str, "%d", n_teams);
add_rule(server, "numteams", str, NO_FLAGS);
pkt = term + 1;
if (pkt - pktstart >= pktlen)
{
goto info_done;
}
teams = (struct player **)calloc(1, sizeof(struct player*) * n_teams);
for (t = 0; t < n_teams; t++)
{
teams[t] = (struct player*)calloc(1, sizeof(struct player));
teams[t]->number = TRIBES_TEAM;
teams[t]->team = t;
/* team name */
term = strchr(pkt, 0x9);
if (!term)
{
n_teams = t;
goto info_done;
} teams[t]->name = strndup(pkt, term - pkt);
pkt = term + 1;
term = strchr(pkt, 0xa);
if (!term)
{
n_teams = t;
goto info_done;
}
*term = '\0';
teams[t]->frags = atoi(pkt);
pkt = term + 1;
if (pkt - pktstart >= pktlen)
{
goto info_done;
}
}
term = strchr(pkt, 0xa);
if (!term || term - start >= len)
{
goto info_done;
}
*term = '\0';
n_players = atoi(pkt);
pkt = term + 1;
for (i = 0; i < n_players && pkt - start < len; i++)
{
pkt++; /* skip first byte (0x10) */
if (pkt - start >= len)
{
break;
}
player = (struct player*)calloc(1, sizeof(struct player));
term = strchr(pkt, 0x11);
if (!term || term - start >= len)
{
free(player);
break;
} player->name = strndup(pkt, term - pkt);
get_tribes2_player_type(player);
pkt = term + 1;
pkt++; /* skip 0x9 */
if (pkt - start >= len)
{
break;
}
term = strchr(pkt, 0x9);
if (!term || term - start >= len)
{
free(player->name);
free(player);
break;
}
for (t = 0; t < n_teams; t++)
{
if (term - pkt == strlen(teams[t]->name) && strncmp(pkt, teams[t]->name, term - pkt) == 0)
{
break;
}
}
if (t == n_teams)
{
player->team = - 1;
player->team_name = "Unassigned";
}
else
{
player->team = t;
player->team_name = teams[t]->name;
}
player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
pkt = term + 1;
for (s = 0; *pkt != 0xa && pkt - start < len; pkt++)
{
str[s++] = *pkt;
}
str[s] = '\0';
player->frags = atoi(str);
if (*pkt == 0xa)
{
pkt++;
}
*last_player = player;
last_player = &player->next;
}
info_done:
for (t = n_teams; t;)
{
t--;
teams[t]->next = server->players;
server->players = teams[t];
}
if (teams)
{
free(teams);
}
return DONE_FORCE;
}