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; }