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 */
char tribes_info[] = { '`', '*', '*' };
char tribes_players[] = { 'b', '*', '*' };
/*  This is what the game sends to get minimal status
{ '\020', '\03', '\377', 0, (unsigned char)0xc5, 6 };
*/
char tribes_info_reponse[] = { 'a', '*', '*', 'b' };
char tribes_players_reponse[] = { 'c', '*', '*', 'b' };
char tribes_masterquery[] = { 0x10, 0x3, '\377', 0, 0x2 };
char tribes_master_response[] = { 0x10, 0x6 };

 
 
 {
    /* TRIBES */
    TRIBES_SERVER,		/* id */
    "TBS",			/* type_prefix */
    "tbs",			/* type_string */
    "-tbs",			/* type_option */
    "Tribes",			/* game_name */
    0,				/* master */
    TRIBES_DEFAULT_PORT,	/* default_port */
    0,				/* port_offset */
    TF_SINGLE_QUERY,		/* flags */
    "game",			/* game_rule */
    "TRIBES",			/* template_var */
    (char*) &tribes_info,	/* status_packet */
    sizeof( tribes_info),	/* status_len */
    (char*) &tribes_players,	/* player_packet */
    sizeof( tribes_players),	/* player_len */
    (char*) &tribes_players,	/* rule_packet */
    sizeof( tribes_players),	/* rule_len */
    NULL,			/* master_packet */
    0,				/* master_len */
    NULL,			/* master_protocol */
    NULL,			/* master_query */
    display_tribes_player_info,	/* display_player_func */
    display_server_rules,	/* display_rule_func */
    raw_display_tribes_player_info,	/* display_raw_player_func */
    raw_display_server_rules,	/* display_raw_rule_func */
    xml_display_tribes_player_info,	/* display_xml_player_func */
    xml_display_server_rules,	/* display_xml_rule_func */
    send_tribes_request_packet,	/* status_query_func */
    NULL,			/* rule_query_func */
    NULL,			/* player_query_func */
    deal_with_tribes_packet,	/* packet_func */
},






query_status_t deal_with_tribes_packet(struct qserver *server, char *rawpkt, int pktlen)
{
	unsigned char *pkt, *end;
	int len, pnum, ping, packet_loss, n_teams, t;
	struct player *player;
	struct player **teams = NULL;
	struct player **last_player = &server->players;
	char buf[24];

	debug( 2, "deal_with_tribes_packet %p, %d", server, pktlen );

	if (server->server_name == NULL)
	{
		server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
	}
	else
	{
		gettimeofday(&server->packet_time1, NULL);
	}

	if (pktlen < sizeof(tribes_info_reponse))
	{
		return PKT_ERROR;
	}

	if (strncmp(rawpkt, tribes_players_reponse, sizeof(tribes_players_reponse)) != 0)
	{
		return PKT_ERROR;
	}

	pkt = (unsigned char*) &rawpkt[sizeof(tribes_info_reponse)];

	len = *pkt; /* game name: "Tribes" */
	add_nrule(server, "gamename", (char*)pkt + 1, len);
	pkt += len + 1;
	len = *pkt; /* version */
	add_nrule(server, "version", (char*)pkt + 1, len);
	pkt += len + 1;
	len = *pkt; /* server name */
	server->server_name = strndup((char*)pkt + 1, len);
	pkt += len + 1;
	add_rule(server, "dedicated", *pkt ? "1" : "0", NO_FLAGS);
	pkt++; /* flag: dedicated server */
	add_rule(server, "needpass", *pkt ? "1" : "0", NO_FLAGS);
	pkt++; /* flag: password on server */
	server->num_players = *pkt++;
	server->max_players = *pkt++;

	sprintf(buf, "%u", (unsigned int)pkt[0] + (unsigned int)pkt[1] *256);
	add_rule(server, "cpu", buf, NO_FLAGS);
	pkt++; /* cpu speed, lsb */
	pkt++; /* cpu speed, msb */

	len = *pkt; /* Mod (game) */
	add_nrule(server, "mods", (char*)pkt + 1, len);
	pkt += len + 1;

	len = *pkt; /* game (mission): "C&H" */
	add_nrule(server, "game", (char*)pkt + 1, len);
	pkt += len + 1;

	len = *pkt; /* Mission (map) */
	server->map_name = strndup((char*)pkt + 1, len);
	pkt += len + 1;

	len = *pkt; /* description (contains Admin: and Email: ) */
	debug( 2, "%.*s\n", len, pkt + 1);
	pkt += len + 1;

	n_teams = *pkt++; /* number of teams */
	if (n_teams == 255)
	{
		return PKT_ERROR;
	}
	sprintf(buf, "%d", n_teams);
	add_rule(server, "numteams", buf, NO_FLAGS);

	len = *pkt; /* first title */
	debug( 2, "%.*s\n", len, pkt + 1);
	pkt += len + 1;

	len = *pkt; /* second title */
	debug( 2, "%.*s\n", len, pkt + 1);
	pkt += len + 1;

	if (n_teams > 1)
	{
		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;
			len = *pkt; /* team name */
			teams[t]->name = strndup((char*)pkt + 1, len);
			debug( 2, "team#0 <%.*s>\n", len, pkt + 1);
			pkt += len + 1;

			len = *pkt; /* team score */
			if (len > 2)
			{
				strncpy(buf, (char*)pkt + 1+3, len - 3);
				buf[len - 3] = '\0';
			}
			else
			{
				debug( 2, "%s score len %d\n", server->arg, len);
				buf[0] = '\0';
			}
			teams[t]->frags = atoi(buf);
			debug( 2, "team#0 <%.*s>\n", len - 3, pkt + 1+3);
			pkt += len + 1;
		}
	}
	else
	{
		len = *pkt; /* DM team? */
		debug( 2, "%.*s\n", len, pkt + 1);
		pkt += len + 1;
		pkt++;
		n_teams = 0;
	}

	pnum = 0;
	while ((char*)pkt < (rawpkt + pktlen))
	{
		ping = (unsigned int) *pkt << 2;
		pkt++;
		packet_loss = *pkt;
		pkt++;
		debug( 2, "player#%d, team #%d\n", pnum, (int) *pkt);
		pkt++;
		len = *pkt;
		if ((char*)pkt + len > (rawpkt + pktlen))
		{
			break;
		}
		player = (struct player*)calloc(1, sizeof(struct player));
		player->team = pkt[ - 1];
		if (n_teams && player->team < n_teams)
		{
			player->team_name = teams[player->team]->name;
		}
		else if (player->team == 255 && n_teams)
		{
			player->team_name = "Unknown";
		}
		player->flags |= PLAYER_FLAG_DO_NOT_FREE_TEAM;
		player->ping = ping;
		player->packet_loss = packet_loss;
		player->name = strndup((char*)pkt + 1, len);
		debug( 2, "player#%d, name %.*s\n", pnum, len, pkt + 1);
		pkt += len + 1;
		len = *pkt;
		debug( 2, "player#%d, info <%.*s>\n", pnum, len, pkt + 1);
		end = (unsigned char*)strchr((char*)pkt + 9, 0x9);
		if (end)
		{
			strncpy(buf, (char*)pkt + 9, end - (pkt + 9));
			buf[end - (pkt + 9)] = '\0';
			player->frags = atoi(buf);
			debug( 2, "player#%d, score <%.*s>\n", pnum, (unsigned)(end - (pkt + 9)), pkt + 9);
		}

		 *last_player = player;
		last_player = &player->next;

		pkt += len + 1;
		pnum++;
	}

	for (t = n_teams; t;)
	{
		t--;
		teams[t]->next = server->players;
		server->players = teams[t];
	}
	free(teams);

	return DONE_AUTO;
}
