xqf/src/game.c

4125 lines
100 KiB
C

/* XQF - Quake server browser and launcher
* Copyright (C) 1998-2000 Roman Pozlevich <roma@botik.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <sys/types.h>
#include <stdio.h> /* FILE, fprintf, fopen, fclose */
#include <string.h> /* strlen, strcpy, strcmp, strtok */
#include <stdlib.h> /* strtol */
#include <unistd.h> /* stat */
#include <sys/stat.h> /* stat, chmod */
#include <sys/socket.h> /* inet_ntoa */
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h> /* inet_ntoa */
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "xqf.h"
#include "pref.h"
#include "launch.h"
#include "dialogs.h"
#include "utils.h"
#include "pixmaps.h"
#include "game.h"
#include "stat.h"
#include "server.h"
#include "statistics.h"
#include "server.h"
#include "config.h"
#include "debug.h"
#include "q3maps.h"
#include "utmaps.h"
/* in pref.c */
extern void q1_cmd_or_dir_changed(struct game* g);
extern void qw_cmd_or_dir_changed(struct game* g);
extern void q2_cmd_or_dir_changed(struct game* g);
extern void ut2004_cmd_or_dir_changed(struct game* g);
extern void doom3_cmd_or_dir_changed(struct game* g);
extern void tremulous_cmd_or_dir_changed(struct game* g);
static struct player *poqs_parse_player(char *tokens[], int num, struct server *s);
static struct player *qw_parse_player(char *tokens[], int num, struct server *s);
static struct player *q2_parse_player(char *tokens[], int num, struct server *s);
static struct player *q3_parse_player(char *tokens[], int num, struct server *s);
static struct player *t2_parse_player(char *tokens[], int num, struct server *s);
static struct player *hl_parse_player(char *tokens[], int num, struct server *s);
static struct player *un_parse_player(char *tokens[], int num, struct server *s);
static struct player *descent3_parse_player(char *tokens[], int num, struct server *s);
static struct player *savage_parse_player (char *token[], int n, struct server *s);
static struct player *ottd_parse_player(char *tokens[], int num, struct server *s);
static void quake_parse_server (char *tokens[], int num, struct server *s);
static void qw_analyze_serverinfo (struct server *s);
static void q2_analyze_serverinfo (struct server *s);
static void hl_analyze_serverinfo (struct server *s);
static void t2_analyze_serverinfo (struct server *s);
static void q3_analyze_serverinfo (struct server *s);
static void doom3_analyze_serverinfo (struct server *s);
static void un_analyze_serverinfo (struct server *s);
static void bf1942_analyze_serverinfo (struct server *s);
static void descent3_analyze_serverinfo (struct server *s);
static void savage_analyze_serverinfo (struct server *s);
static void ottd_analyze_serverinfo (struct server *s);
static int quake_config_is_valid (struct server *s);
static int config_is_valid_generic (struct server *s);
static int write_q1_vars (const struct condef *con);
static int write_qw_vars (const struct condef *con);
static int write_q2_vars (const struct condef *con);
static int teeworlds_exec (const struct condef *con, int forkit);
static int q1_exec_generic (const struct condef *con, int forkit);
static int qw_exec (const struct condef *con, int forkit);
static int q2_exec (const struct condef *con, int forkit);
static int q2_exec_generic (const struct condef *con, int forkit);
static int q3_exec (const struct condef *con, int forkit); // needs quake_private
static int hl_exec(const struct condef *con, int forkit);
static int ut_exec (const struct condef *con, int forkit);
static int t2_exec (const struct condef *con, int forkit);
static int gamespy_exec (const struct condef *con, int forkit);
static int bf1942_exec (const struct condef *con, int forkit);
static int exec_generic (const struct condef *con, int forkit);
static int ssam_exec (const struct condef *con, int forkit);
static int savage_exec (const struct condef *con, int forkit);
static int netpanzer_exec (const struct condef *con, int forkit);
static int descent3_exec (const struct condef *con, int forkit);
static int ottd_exec (const struct condef *con, int forkit);
/*
static GList *q1_custom_cfgs (struct game* this, char *dir, char *game);
static GList *qw_custom_cfgs (struct game* this, char *dir, char *game);
static GList *q2_custom_cfgs (struct game* this, char *dir, char *game);
static GList *q3_custom_cfgs (struct game* this, char *dir, char *game);
*/
static GList *quake_custom_cfgs (struct game* this, const char *path, const char *mod);
static void save_server_info (FILE *f, struct server *s);
char **get_custom_arguments(enum server_type type, const char *gamestring);
static void quake_init_maps(enum server_type);
static gboolean quake_has_map(struct server* s);
static void q3_init_maps(enum server_type);
static size_t q3_get_mapshot(struct server* s, guchar** buf);
static void unvanquished_init_maps(enum server_type);
static void xonotic_init_maps(enum server_type);
static void doom3_init_maps(enum server_type);
static size_t doom3_get_mapshot(struct server* s, guchar** buf);
static gboolean doom3_has_map(struct server* s);
static void quake4_init_maps(enum server_type);
static size_t quake4_get_mapshot(struct server* s, guchar** buf);
static gboolean quake4_has_map(struct server* s);
static void etqw_init_maps(enum server_type);
static void unreal_init_maps(enum server_type);
static gboolean unreal_has_map(struct server* s);
struct quake_private
{
GHashTable* maphash;
};
struct unreal_private
{
GHashTable* maphash;
const char* suffix;
};
#include GAMES_C_INCLUDE
struct gsname2type_s
{
char* name;
enum server_type type;
};
// gamespy names, used to determine the server type
static struct gsname2type_s gsname2type[] =
{
{ "ut", UN_SERVER },
{ "ut2", UT2_SERVER },
{ "ut2d", UT2_SERVER },
{ "rune", RUNE_SERVER },
{ "serioussam", SSAM_SERVER },
{ "serioussamse", SSAMSE_SERVER },
{ "postal2", POSTAL2_SERVER },
{ "postal2d", POSTAL2_SERVER },
{ "armygame", AAO_SERVER },
{ "bfield1942", BF1942_SERVER },
{ NULL, UNKNOWN_SERVER }
};
void init_games() {
unsigned i, j;
debug(3, "initializing games");
for (i = KNOWN_SERVER_START; i < UNKNOWN_SERVER; i++) {
g_datalist_init(&games[i].games_data);
}
for (i = KNOWN_SERVER_START; i < UNKNOWN_SERVER; i++) {
for (j = 0; games[i].attributes && games[i].attributes[j]; j += 2) {
game_set_attribute_const(i, games[i].attributes[j], games[i].attributes[j+1]);
}
}
game_set_attribute(SFS_SERVER, "game_notes", strdup(_
("Note: Soldier of Fortune will not connect to a server correctly\n"\
"without creating a startup script for the game. Please see the\n"\
"XQF documentation for more information.")));
game_set_attribute(UN_SERVER, "game_notes", strdup(_
("Note: Unreal Tournament will not launch correctly without\n"\
"modifications to the game's startup script. Please see the\n"\
"XQF documentation for more information.")));
game_set_attribute(HL_SERVER_OLD, "game_notes", strdup(_
("Sample Command Line: wine hl.exe -- hl.exe -console")));
game_set_attribute(SAS_SERVER, "game_notes", strdup(_
("Note: Savage will not launch correctly without\n"\
"modifications to the game's startup script. Please see the\n"\
"XQF documentation for more information.")));
}
void games_done() {
int i;
for (i = KNOWN_SERVER_START; i < UNKNOWN_SERVER; i++) {
g_free(games[i].real_home);
}
}
// retreive game specific value that belongs to key, do not free return value!
const char* game_get_attribute(enum server_type type, const char* attr) {
return g_datalist_get_data(&games[type].games_data, attr);
}
// set game specific key/value pair, value is _not_ copied and must not be
// freed manually
const char* game_set_attribute(enum server_type type, const char* attr, char* value) {
g_datalist_set_data_full(&games[type].games_data, attr, value, g_free);
return value;
}
// set game specific key/value pair, value is _not_ copied and must not be
// freed manually
const char* game_set_attribute_const(enum server_type type, const char* attr, const char* value) {
g_datalist_set_data_full(&games[type].games_data, attr, (void*)value, NULL);
return value;
}
enum server_type id2type (const char *id) {
int i;
g_return_val_if_fail(id != NULL, UNKNOWN_SERVER);
for (i = LAN_SERVER; i < UNKNOWN_SERVER; i++) {
g_return_val_if_fail(games[i].id != NULL, UNKNOWN_SERVER);
if (g_ascii_strcasecmp (id, games[i].id) == 0)
return games[i].type;
}
for (i = LAN_SERVER; i < UNKNOWN_SERVER; i++) {
if (g_ascii_strcasecmp (id, games[i].qstat_str) == 0)
return games[i].type;
}
// workaround for qstat beta
if (g_ascii_strcasecmp (id, "RWS") == 0) {
return WO_SERVER;
}
if (g_ascii_strcasecmp (id, "QW") == 0)
return QW_SERVER;
if (g_ascii_strcasecmp (id, "Q2") == 0)
return Q2_SERVER;
return UNKNOWN_SERVER;
}
/*
* This function should be used only for configuration saving to make
* possible qstat2.2<->qstat2.3 migration w/o loss of some game-specific
* preferences.
*/
const char *type2id (enum server_type type) {
switch (type) {
default:
return games[type].id;
}
}
GtkWidget *game_pixmap_with_label (enum server_type type) {
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *pixmap;
hbox = gtk_hbox_new (FALSE, 4);
if (games[type].pix) {
pixmap = gtk_pixmap_new (games[type].pix->pix, games[type].pix->mask);
gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 0);
gtk_widget_show (pixmap);
}
label = gtk_label_new (_(games[type].name));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
gtk_widget_show (hbox);
return hbox;
}
static gboolean has_flag(unsigned long flags, unsigned long flag) {
return (flags & flag) != 0;
}
static gboolean is_game_string_unescapable(unsigned long flags) {
return flags != 0;
}
/*
* you MUST allocate dst with g_malloc0 before, this function and others assumes that dst is filled with zeros
*/
static void unescape_game_string (char *dst, const char *src, unsigned long flags) {
/*
* skip color codes
*/
gint idst = 0;
gint isrc = 0;
gint step;
while (src[isrc] != '\0') {
step = 0;
if (src[isrc] == '^') {
if (src[isrc + 1] != '\0') {
// if "^^"
if (src[isrc + 1] == '^') {
if (has_flag(flags, COLOR_DOOM3)) {
// skip the two '^' since Doom 3's ^^ is empty string
step = 2;
}
else {
// only skip one '^', display only one '^'
step = 1;
}
goto walk;
}
if (has_flag(flags, COLOR_QUAKE3_NUMERIC)) {
if (src[isrc + 1] >= '0' && src[isrc + 1] <= '9') {
// one-char color code in the form ^# where # is a numeric digit
// skip '^' and the next char
step = 2;
goto walk;
}
}
if (has_flag(flags, COLOR_QUAKE4)) {
// escape code in the form ^n# where # is any character
// escape code is case insensitive
if(src[isrc + 1] == 'n' || src[isrc + 1] == 'N') {
if (src[isrc + 2] != '\0') {
step = 2;
goto walk;
}
}
// color code in the form ^c### where # is a numeric digit or any character
// color code is case insensitive
else if(src[isrc + 1] == 'c' || src[isrc + 1] == 'C') {
gint i;
for (i = 2; src[isrc + i] != '\0'
&& i < 5; i++) {
// `for` increments i
}
// if complete color code, skip it
if (i == 5) {
step = i;
goto walk;
}
}
// icon code in the form ^i### where # is an alphanumeric digit or any character
// for example ^iw06 is the rocket launcher icon
// icon code is case insensitive
else if(src[isrc + 1] == 'i' || src[isrc + 1] == 'I') {
gint i;
for (i = 2; src[isrc + i] != '\0'
&& i < 5; i++) {
// `for` increments i
}
// if complete icon code, use a placeholder and skip remaining chars
if (i == 5) {
dst[idst] = '_';
idst += 1;
step = i;
goto walk;
}
}
}
if (has_flag(flags, COLOR_WOLFET)) {
// see https://github.com/etlegacy/etlegacy/blob/master/src/qcommon/q_math.h
// Wolf:ET also supports ^[0-9] but that's already handled by the Quake 3 numeric color filter
// Wolf:ET also supports ^* so that flag is to be used by games that
// inherit Wolf:ET color codes without having support for COLOR_QUAKE3_ANY
// like Unvanquished that inherits these color codes from Wolf:ET through ET:XreaL
// but does not filter out unknown codes
// see https://github.com/DaemonEngine/Daemon/blob/master/src/common/Color.cpp
if ((src[isrc + 1] >= 'A' && src[isrc + 1] <= 'O')
|| (src[isrc + 1] >= 'a' && src[isrc + 1] <= 'o')
|| src[isrc + 1] == ':'
|| src[isrc + 1] == ';'
|| src[isrc + 1] == '<'
|| src[isrc + 1] == '>'
|| src[isrc + 1] == '='
|| src[isrc + 1] == '?'
|| src[isrc + 1] == '@'
|| src[isrc + 1] == '*') {
// extended one-char color code in the form ^# where # is a case-insentive alphabetic
// character between a and o or a special character from the known list above
// skip '^' and the next char
step = 2;
goto walk;
}
}
if (has_flag(flags, COLOR_XONOTIC)) {
// see https://xonotic.org/faq/#how-can-i-use-colors-in-my-nickname-and-messages
// RGB color code in the form ^x### where # is a case-insensitive hexadecimal digit
// Xonotic also supports ^[0-9] but that's already handled by the Quake 3 numeric color filter
if(src[isrc + 1] == 'x') {
gint i;
for (i = 2; (src[isrc + i] != '\0'
&& ((src[isrc + i] >= '0' && src[isrc + i] <= '9')
|| (src[isrc + i] >= 'A' && src[isrc + i] <= 'F')
|| (src[isrc + i] >= 'a' && src[isrc + i] <= 'f')))
&& i < 5; i++) {
// `for` increments i
}
// if complete color code, skip it
if (i == 5) {
step = i;
goto walk;
}
}
}
if (has_flag(flags, COLOR_SAVAGE)) {
// if Savage three chars color code in the form ^### where # is a numeric digit
gint i;
for (i = 1; (src[isrc + i] != '\0'
&& ((src[isrc + i] >= '0' && src[isrc + i] <= '9')))
&& i < 4; i++) {
// `for` increments i
}
// if complete color code, skip it
if (i == 4) {
step = i;
goto walk;
}
// if beginning of Savage clan identifier with the form ^clan ###^ where ### is a number that will not be escaped
// FIXME: this code does not ignore malformed clan code (not ending with a caret) because of the bracket writing
else if (src[isrc + 1] == 'c') {
if (src[isrc + 2] == 'l' && src[isrc + 2] != '\0') {
if (src[isrc + 3] == 'a' && src[isrc + 3] != '\0') {
if (src[isrc + 4] == 'n' && src[isrc + 4] != '\0') {
if (src[isrc + 5] == ' ' && src[isrc + 5] != '\0') {
// write brackets around the clan code like http://masterserver.savage.s2games.com/
dst[idst] = '[';
idst += 1;
step = 6;
gint i;
for (i = 0; src[isrc + step + i] != '\0' && src[isrc + step + i] != '^'; i++) {
dst[idst] = src[isrc + step + i];
idst += 1;
}
step += i;
if (src[isrc + step] == '^') {
// write brackets around the clan code like http://masterserver.savage.s2games.com/
dst[idst] = ']';
idst += 1;
step += 1;
}
goto walk;
}
}
}
}
}
}
if (has_flag(flags, COLOR_QUAKE3_ALPHA)) {
if ((src[isrc + 1] >= 'A' && src[isrc + 1] <= 'Z')
|| (src[isrc + 1] >= 'a' && src[isrc + 1] <= 'z')) {
// one-char color code in the form ^# where # is a case-insentive
// alphabetic character
// skip '^' and the next char
step = 2;
goto walk;
}
}
if (has_flag(flags, COLOR_QUAKE3_ANY)) {
// one-char color code in the form ^# where # is any character
// skip '^' and the next char
step = 2;
goto walk;
}
}
}
walk:
isrc += step;
// if not at end of string
if (src[isrc] != '\0') {
// if not another caret, print the caracter
// if another caret, it will escaped on next loop
if (src[isrc] != '^' || step == 0) {
dst[idst] = src[isrc];
isrc += 1;
idst += 1;
}
debug(6, "isrc: %d, idst: %d", isrc, idst);
debug(6, "src: [%s], dst: [%s]", src, dst);
}
}
// when finished, do nothing more, the remaining allocated space is already filled with zeroes
}
static const char delim[] = " \t\n\r";
static struct player *poqs_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
long tmp;
if (n < 7)
return NULL;
player = g_malloc0 (sizeof (struct player) + strlen (token[1]) + 1);
player->time = strtol (token[4], NULL, 10);
player->frags = strtosh (token[3]);
player->ping = -1;
tmp = strtol (token[5], NULL, 10);
player->shirt = fix_qw_player_color (tmp);
tmp = strtol (token[6], NULL, 10);
player->pants = fix_qw_player_color (tmp);
player->name = (char *) player + sizeof (struct player);
strcpy (player->name, token[1]);
return player;
}
static struct player *qw_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
char *ptr;
long tmp;
int name_strlen;
int skin_strlen;
if (n < 8)
return NULL;
name_strlen = strlen (token[1]) + 1;
skin_strlen = strlen (token[7]) + 1;
player = g_malloc0 (sizeof (struct player) + name_strlen + skin_strlen);
player->time = strtol (token[3], NULL, 10);
player->frags = strtosh (token[2]);
player->ping = strtosh (token[6]);
tmp = strtol (token[4], NULL, 10);
player->shirt = fix_qw_player_color (tmp);
tmp = strtol (token[5], NULL, 10);
player->pants = fix_qw_player_color (tmp);
ptr = (char *) player + sizeof (struct player);
player->name = strcpy (ptr, token[1]);
player->skin = strcpy (ptr + name_strlen, token[7]);
return player;
}
// Parse Tribes2 player info, abuse player->model to show when a Player is only
// a Teamname or a Bot
// player name, frags, team number, team name, player type, tribe tag
static struct player *t2_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
char* name=token[0];
char* frags=token[1];
// char* team_number=token[2];
char* team_name=token[3];
char* player_type=token[4];
// char* tribe_tag=token[5];
if (n < 6)
return NULL;
// show TEAM in model column
if (!strcmp(team_name, "TEAM"))
player_type=team_name;
player = g_malloc0 (sizeof (struct player) + strlen(name)+1 + strlen(player_type)+1);
player->time = -1;
player->frags = strtosh (frags);
player->ping = -1;
player->name = (char *) player + sizeof (struct player);
strcpy (player->name, name);
player->model = (char *) player + sizeof (struct player) + strlen(name)+1;
strcpy (player->model, player_type);
if (player_type[0] == 'B') ++s->curbots;
return player;
}
static struct player *q2_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
if (n < 3)
return NULL;
player = g_malloc0 (sizeof (struct player) + strlen (token[0]) + 1);
player->time = -1;
player->frags = strtosh (token[1]);
player->ping = strtosh (token[2]);
player->name = (char *) player + sizeof (struct player);
strcpy (player->name, token[0]);
return player;
}
// Descent 3: player name, frags, deaths, ping time, team
static struct player *descent3_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
if (n < 5)
return NULL;
player = g_malloc0 (sizeof (struct player) + strlen (token[0]) + 1);
player->time = -1;
player->frags = strtosh (token[1]);
player->ping = strtosh (token[3]);
player->name = (char *) player + sizeof (struct player);
strcpy (player->name, token[0]);
if (token[0][0] == '-' && token[0][strlen(token[0])-1] == '-')
++s->curbots;
return player;
}
static struct player *q3_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
const char* clan = NULL;
unsigned clanlen = 0;
if (n < 3)
return NULL;
if (n > 3 && strcmp(token[3], "0")) {
clan = token[3];
clanlen = strlen(clan)+1;
}
if (s->type == WARSOW_SERVER && clan) {
static const char* colors[] = { "spectator", "", "red", "blue", "green", "yellow" };
unsigned i = atoi(clan);
if (i > 5) i = 1;
clan = colors[i];
clanlen = 0;
}
player = g_malloc0 (sizeof (struct player) + strlen (token[0]) + 1 + clanlen);
player->time = -1;
player->frags = strtosh (token[1]);
player->ping = strtosh (token[2]);
/* FIXME if (dedicated == 0) s->curbots-- */
if (player->ping == 0) ++s->curbots;
player->name = (char *) player + sizeof (struct player);
// overwrite in place
unescape_game_string (player->name, token[0], games[s->type].color_flags);
// show clan name in model column
if (clan) {
if (s->type == WARSOW_SERVER) {
player->model = (char*)clan;
}
else {
player->model = (char *) player + sizeof (struct player) + strlen(player->name) + 1;
unescape_game_string(player->model, clan, games[s->type].color_flags);
}
}
return player;
}
static struct player *hl_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
if (n < 3)
return NULL;
player = g_malloc0 (sizeof (struct player) + strlen (token[0]) + 1);
player->frags = strtosh (token[1]);
player->time = strtosh (token[2]);
player->ping = -1;
player->name = (char *) player + sizeof (struct player);
strcpy (player->name, token[0]);
return player;
}
static struct player *un_parse_player (char *token[], int n, struct server *s) {
struct player *player = NULL;
char *ptr;
int name_strlen;
int skin_strlen;
int mesh_strlen;
long tmp;
/* player name, frags, ping time, team number, skin, mesh, face */
if (n < 6)
return NULL;
name_strlen = strlen (token[0]) + 1;
skin_strlen = strlen (token[4]) + 1;
mesh_strlen = strlen (token[5]) + 1;
player = g_malloc0 (sizeof (struct player) + name_strlen +
skin_strlen + mesh_strlen);
ptr = (char *) player + sizeof (struct player);
player->name = strcpy (ptr, token[0]);
ptr += name_strlen;
player->frags = strtosh (token[1]);
player->ping = strtosh (token[2]);
player->time = -1;
/* store team number to 'pants' */
tmp = strtol (token[3], NULL, 10);
player->pants = (tmp >= 0 && tmp <= 255)? tmp : 0;
player->skin = strcpy (ptr, token[4]);
ptr += skin_strlen;
player->model = strcpy (ptr, token[5]);
return player;
}
static struct player *savage_parse_player (char *token[], int n, struct server *s) {
return q2_parse_player(token, n, s);
}
static struct player *ottd_parse_player (char *token[], int n, struct server *s) {
return q2_parse_player(token, n, s);
}
static void quake_parse_server (char *token[], int n, struct server *server) {
/*
This does both Quake (?) and Unreal servers
*/
int poqs;
int offs;
/* debug (6, "quake_parse_server: Parse %s", server->name); */
poqs = (server->type == Q1_SERVER || server->type == H2_SERVER);
if (poqs && n < 10)
return;
else if (n < 8)
return;
if (*(token[2])) { /* if name is not empty */
if (!is_game_string_unescapable(games[server->type].flags)) {
server->name = g_strdup (token[2]);
}
else {
server->name = g_malloc0 (sizeof (char) * strlen (token[2]) + 1);
debug(6, "maxsize:%d", (int) (sizeof (char) * strlen (token[2]) + 1));
unescape_game_string (server->name, token[2], games[server->type].color_flags);
}
}
offs = (poqs)? 5 : 3;
if (*(token[offs])) /* if map is not empty */
server->map = g_ascii_strdown(token[offs], -1); /* g_ascii_strdown does implicit strndup */
server->maxplayers = strtoush (token[offs + 1]);
server->curplayers = strtoush (token[offs + 2]);
server->ping = strtosh (token[offs + 3]);
server->retries = strtosh (token[offs + 4]);
}
static void qw_analyze_serverinfo (struct server *server) {
char **info_ptr;
long n;
/* Clear out the flags */
server->flags = 0;
/* debug(6, "qw_analyze_serverinfo: Analyze %s", server->name); */
if ((games[server->type].flags & GAME_SPECTATE) != 0)
server->flags |= SERVER_SPECTATE;
for (info_ptr = server->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "*gamedir") == 0) {
server->game = info_ptr[1];
}
else if (strcmp (*info_ptr, "*cheats") == 0) {
server->flags |= SERVER_CHEATS;
}
else if (strcmp (*info_ptr, "maxspectators") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if (n <= 0)
server->flags &= ~SERVER_SPECTATE;
}
else if (strcmp (*info_ptr, "needpass") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if ((n & 1) != 0)
server->flags |= SERVER_PASSWORD;
if ((n & 2) != 0)
server->flags |= SERVER_SP_PASSWORD;
}
}
}
static void un_analyze_serverinfo (struct server *s) {
char **info_ptr;
unsigned short hostport=0;
enum server_type oldtype = s->type;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gametype") == 0) {
s->game = info_ptr[1];
}
else if (strcmp (*info_ptr, "gamestyle") == 0) {
s->gametype = info_ptr[1];
}
else if (strcmp (*info_ptr, "hostport") == 0) {
hostport = atoi(info_ptr[1]);
}
else if (strcmp (*info_ptr, "Mutator") == 0 && strstr(info_ptr[1], "AntiTCC")) {
s->flags |= SERVER_PUNKBUSTER;
}
else if ((s->type == UT2004_SERVER || s->type == UT2_SERVER)
&& strcmp (*info_ptr, "ServerVersion") == 0
&& strlen(info_ptr[1]) == 4) {
if (info_ptr[1][0] == '3')
s->type = UT2004_SERVER;
else if (info_ptr[1][0] == '2')
s->type = UT2_SERVER;
}
else if (strcmp (*info_ptr, "gamename") == 0) {
unsigned i;
for (i = 0 ; gsname2type[i].name ; ++i) {
if (!strcmp(info_ptr[1], gsname2type[i].name)) {
s->type = gsname2type[i].type;
break;
}
}
}
// password required?
// If not password=False or password=0, set SERVER_PASSWORD
else if ((g_ascii_strcasecmp (*info_ptr, "password") == 0 || g_ascii_strcasecmp (*info_ptr, "gamepassword") == 0) &&
(g_ascii_strcasecmp(info_ptr[1], "false") && strcmp(info_ptr[1], "0"))) {
s->flags |= SERVER_PASSWORD;
if (games[s->type].flags & GAME_SPECTATE)
s->flags |= SERVER_SP_PASSWORD;
}
}
// adjust port for unreal and rune
if (s->type != oldtype) {
switch(s->type) {
case UN_SERVER:
case UT2_SERVER:
case RUNE_SERVER:
case POSTAL2_SERVER:
// case AAO_SERVER: // doesn't work on lan
server_change_port(s, hostport);
break;
default:
break;
}
}
}
static void bf1942_analyze_serverinfo (struct server *s) {
char **info_ptr;
/* Clear out the flags */
s->flags = 0;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gametype") == 0) {
s->gametype = info_ptr[1];
}
else if (strcmp (*info_ptr, "Game Id") == 0) {
s->game = info_ptr[1];
}
//password required?
// If not password=False or password=0, set SERVER_PASSWORD
else if (strcmp (*info_ptr, "_password") == 0
&& strcmp(info_ptr[1], "0")) {
s->flags |= SERVER_PASSWORD;
if (games[s->type].flags & GAME_SPECTATE)
s->flags |= SERVER_SP_PASSWORD;
}
}
}
static void savage_analyze_serverinfo (struct server *s) {
char **info_ptr;
/* Clear out the flags */
s->flags = 0;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gametype") == 0) {
s->game = info_ptr[1];
}
}
}
static void descent3_analyze_serverinfo (struct server *s) {
char **info_ptr;
/* Clear out the flags */
s->flags = 0;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gametype") == 0) {
s->game = info_ptr[1];
}
/*
//password required?
else if (strcmp (*info_ptr, "password") == 0 && strcmp(info_ptr[1], "False")) {
s->flags |= SERVER_PASSWORD;
}
*/
}
}
static void q2_analyze_serverinfo (struct server *s) {
char **info_ptr;
long n;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gamedir") == 0) {
s->game = info_ptr[1];
// D-Day: Normandy
if (!strcmp(info_ptr[1], "dday")) {
s->type=DDAY_SERVER;
}
}
// determine mod
else if (strcmp (*info_ptr, "gamename") == 0) {
switch (s->type) {
case Q2_SERVER:
/* We only set the mod if name is not baseq2 */
if (strcmp(info_ptr[1], "baseq2"))
s->gametype = info_ptr[1];
break;
default:
s->gametype = info_ptr[1];
break;
}
}
else if (strcmp (*info_ptr, "cheats") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_CHEATS;
}
else if (s->type == Q2_SERVER && strcmp (*info_ptr, "protocol") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if (n < 34)
s->flags &= ~SERVER_SPECTATE;
}
else if (strcmp (*info_ptr, "maxspectators") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if (n <= 0)
s->flags &= ~SERVER_SPECTATE;
}
else if (strcmp (*info_ptr, "needpass") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if ((n & 1) != 0)
s->flags |= SERVER_PASSWORD;
if ((n & 2) != 0)
s->flags |= SERVER_SP_PASSWORD;
}
}
/* We only set the mod if gamedir is set */
if (!s->game) {
s->gametype=NULL;
}
}
static void t2_analyze_serverinfo (struct server *s) {
char **info_ptr;
long n;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "game") == 0) {
if (strcmp(info_ptr[1], "base")) // If it's not 'base'
s->game = info_ptr[1];
}
//determine GameType column
else if (strcmp (*info_ptr, "mission") == 0) {
s->gametype = info_ptr[1];
}
else if (strcmp (*info_ptr, "cheats") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_CHEATS;
}
else if (strcmp (*info_ptr, "maxspectators") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if (n <= 0)
s->flags &= ~SERVER_SPECTATE;
}
else if (strcmp (*info_ptr, "password") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if ((n & 1) != 0)
s->flags |= SERVER_PASSWORD;
if ((n & 2) != 0)
s->flags |= SERVER_SP_PASSWORD;
}
}
// unset game if game is base
// if (!strcmp(s->game, "base"))
// {
// strcpy(s->gametype, "Alex");
// }
}
static void hl_analyze_serverinfo (struct server *s) {
char **info_ptr;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gamedir") == 0) {
s->game = info_ptr[1];
}
//determine mod
else if (strcmp (*info_ptr, "gamename") == 0) {
s->gametype = info_ptr[1];
}
//cheats enabled?
else if (strcmp (*info_ptr, "sv_cheats") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_CHEATS;
}
//password required?
else if (strcmp (*info_ptr, "sv_password") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_PASSWORD;
}
//cheating death
else if (strcmp (*info_ptr, "cdrequired") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_PUNKBUSTER;
}
//VAC
else if (strcmp (*info_ptr, "secure") == 0 && info_ptr[1][0] != '0') {
s->flags |= SERVER_PUNKBUSTER;
}
// reserved slots
else if (strcmp (*info_ptr, "reserve_slots") == 0) {
s->private_client = strtol (info_ptr[1], NULL, 10);
}
}
// unset Mod if gamedir is valve
if (s->game && !strcmp(s->game, "valve")) {
s->gametype=NULL;
}
}
// TODO: read this stuff from a config file
#define MAX_Q3A_TYPES 9
static char *q3a_gametypes[MAX_Q3A_TYPES] = {
"FFA", /* 0 = Free for All */
"1v1", /* 1 = Tournament */
NULL, /* 2 = Single Player */
"TDM", /* 3 = Team Deathmatch */
"CTF", /* 4 = Capture the Flag */
"1FCTF", /* 5 = One Flag Capture the Flag */
"OVR", /* 6 = Overload */
"HRV", /* 7 = Harvester */
" mod ", /* 8+ is usually a client-side mod */
};
#define MAX_Q3A_OSP_TYPES 7
static char *q3a_osp_gametypes[MAX_Q3A_OSP_TYPES] = {
"FFA", /* 0 = Free for All */
"1v1", /* 1 = Tournament */
"FFA Comp", /* 2 = FFA, Competition */
"TDM", /* 3 = Team Deathmatch */
"CTF", /* 4 = Capture the Flag */
"Clan Arena", /* 5 = Clan Arena */
"Custom OSP", /* 6+ is usually a custom OSP setting */
};
#define MAX_Q3A_UT2_TYPES 9
static char *q3a_ut2_gametypes[MAX_Q3A_UT2_TYPES] = {
"FFA", /* 0 = Free for All */
"FFA", /* 1 = Free for All */
"FFA", /* 2 = Free for All */
"TDM", /* 3 = Team Deathmatch */
"Team Survivor", /* 4 = Team Survivor */
"Follow the Leader", /* 5 = Follow the Leader */
"Capture & Hold", /* 6 = Capture & Hold */
"CTF", /* 7 = Capture the Flag */
NULL, /* 8+ ?? */
};
#define MAX_Q3A_UT3_TYPES 9
static char *q3a_ut3_gametypes[MAX_Q3A_UT3_TYPES] = {
"FFA", /* 0 = Free for All */
"FFA", /* 1 = Free for All */
"FFA", /* 2 = Free for All */
"TDM", /* 3 = Team Deathmatch */
"Team Survivor", /* 4 = Team Survivor */
"Follow the Leader", /* 5 = Follow the Leader */
"Capture & Hold", /* 6 = Capture & Hold */
"CTF", /* 7 = Capture the Flag */
"Bomb Mode", /* 8 = Bomb Mode */
};
#define MAX_Q3A_Q3TC045_TYPES 11
static char *q3a_q3tc045_gametypes[MAX_Q3A_Q3TC045_TYPES] = {
"FFA", // 0 = Free for All
NULL, // 1 = ?
NULL, // 2 = ?
"Survivor", // 3 = Survivor
"TDM", // 4 = Team Deathmatch
"CTF", // 5 = Capture the Flag
"Team Survivor", // 6 = Team Survivor
NULL, // 7 = ?
"Capture & Hold", // 8 = Capture & Hold
"King Of The Hill", // 9 = King of the hill
NULL, // 10+ ??
};
#define MAX_Q3A_TRUECOMBAT_TYPES 8
static char *q3a_truecombat_gametypes[MAX_Q3A_TRUECOMBAT_TYPES] = {
"FFA", // 0 = Free for All
"Survivor", // 1 = Survivor/Last Man Standing
NULL, // 2 = ?
"TDM", // 3 = Team Deathmatch
"Reverse CTF", // 4 = Reverse CTF
"CTF", // 5 = Capture the Flag
"Team Survivor", // 6 = Team Survivor
"Mission", // 7 = Mission
};
#define MAX_Q3A_THREEWAVE_TYPES 12
static char *q3a_threewave_gametypes[MAX_Q3A_THREEWAVE_TYPES] = {
"FFA", // 0 - Free For All
"1v1", // 1 - Tournament
NULL, // 2 - Single Player (invalid, don't use this)
"TDM", // 3 - Team Deathmatch
"ThreeWave CTF", // 4 - ThreeWave CTF
"One flag CTF", // 5 - One flag CTF (invalid, don't use this)
"Obelisk", // 6 - Obelisk (invalid, don't use this)
"Harvester", // 7 - Harvester (invalid, don't use this)
"Portal", // 8 - Portal (invalid, don't use this)
"CaptureStrike", // 9 - CaptureStrike
"Classic CTF", // 10 - Classic CTF
NULL // 11+ ???
};
#define MAX_Q3A_TRIBALCTF_TYPES 10
static char *q3a_tribalctf_gametypes[MAX_Q3A_TRIBALCTF_TYPES] = {
NULL, // 0 - Unknown
NULL, // 1 - Unknown
NULL, // 2 - Unknown
NULL, // 3 - Unknown
NULL, // 4 - Unknown
NULL, // 5 - Unknown
"Freestyle", // 6 - Freestyle
"Fixed", // 7 - Fixed
"Roulette", // 8 - Roulette
NULL // 9+ ???
};
#define MAX_Q3A_SEALS_TYPES 5
static char *q3a_seals_gametypes[MAX_Q3A_SEALS_TYPES] = {
NULL, /* 0 = devmode */
NULL, /* 1 = invalid */
NULL, /* 2 = invalid */
"Operations", /* 3 = Team Deathmatch */
NULL /* 4+ invalid */
};
#define MAX_Q3A_AFTERWARDS_TYPES 3
static char *q3a_afterwards_gametypes[MAX_Q3A_AFTERWARDS_TYPES] = {
"Tactical", // 0 = Tactical
"FFA", // 1 = Deatchmatch
NULL, // 2+ ??
};
#define MAX_Q3A_ARENA_TYPES 9
// Not sure what the proper types are, but 99% of them are a game
// type of 8. Just call them all "arena"
static char *q3a_arena_gametypes[MAX_Q3A_ARENA_TYPES] = {
"arena", /* 0 = Arena */
"arena", /* 1 = Arena */
"arena", /* 2 = Arena */
"arena", /* 3 = Arena */
"arena", /* 4 = Arena */
"arena", /* 5 = Arena */
"arena", /* 6 = Arena */
"arena", /* 7 = Arena */
"arena", /* 8 = Arena */
};
#define MAX_Q3A_CPMA_TYPES 6
static char *q3a_cpma_gametypes[MAX_Q3A_CPMA_TYPES] = {
"FFA", /* 0 = Free for All */
"1v1", /* 1 = Tournament */
NULL, /* 2 = Single Player */
"TDM", /* 3 = Team Deathmatch */
"CTF", /* 4 = Capture the Flag */
"Clan Arena", /* 5 = Clan Arena */
};
#define MAX_Q3A_Q3F_TYPES 6
static char *q3a_q3f_gametypes[MAX_Q3A_Q3F_TYPES] = {
"q3f", /* 0 = Arena */
"q3f", /* 1 = Arena */
"q3f", /* 2 = Arena */
"q3f", /* 3 = Arena */
"q3f", /* 4 = Arena */
"q3f", /* 5 = Arena */
};
#define MAX_Q3A_WQ3_TYPES 6
static char *q3a_wq3_gametypes[MAX_Q3A_WQ3_TYPES] = {
"FFA",
"Duel",
NULL,
"TDM",
"Round Teamplay",
"Bank Robbery"
};
#define MAX_Q3A_WOP_TYPES 9
static char *q3a_wop_gametypes[MAX_Q3A_WOP_TYPES] = {
"Free for All", // FFA
NULL,
NULL,
"Spray Your Color", // SYC
"Last Pad Standing", // LPS
"Free For All Team", // TFFA
"Capture The Lolly", // CTL
"Spray Your Color Team", // TSYC
"Big Balloon" // BB
};
#define MAX_Q3A_EXCESSIVEPLUS_TYPES 10
static char *q3a_excessiveplus_gametypes[MAX_Q3A_EXCESSIVEPLUS_TYPES] = {
"FFA", /* 0 = Free for All */
"1v1", /* 1 = Tournament */
NULL, /* 2 = Single Player */
"TDM", /* 3 = Team Deathmatch */
"CTF", /* 4 = Capture the Flag */
"RTF", /* 5 = Return the Flag */
"One Flag CTF", /* 6 = One Flag Capture the Flag */
"Clan Arena", /* 7 = Clan Arena */
"Freeze Tag", /* 8 = Freeze Tag */
"Protect the Leader" /* 9 = Protect the Leader */
};
#define MAX_OPENARENA_TYPES MAX_Q3A_TYPES
#define openarena_gametypes q3a_gametypes
#define MAX_SMOKINGUNS_TYPES MAX_Q3A_WQ3_TYPES
#define smokinguns_gametypes q3a_wq3_gametypes
#define MAX_WORLDOFPADMAN_TYPES MAX_Q3A_WOP_TYPES
#define worldofpadman_gametypes q3a_wop_gametypes
#define MAX_WOLF_TYPES 9
static char *wolf_gametypes[MAX_WOLF_TYPES] = {
NULL, // 0 - Unknown
NULL, // 1 - Unknown
NULL, // 2 - Unknown
NULL, // 3 - Unknown
NULL, // 4 - Unknown
"WolfMP", // 5 - standard objective mode
"WolfSW", // 6 - Stopwatch mode
"WolfCP", // 7 - Checkpoint mode
NULL // 8+ ???
};
#define MAX_WOLFET_TYPES 9
static char *wolfet_gametypes[MAX_WOLFET_TYPES] = {
NULL, // 0 - Single player - unused
NULL, // 1 - Cooperative - unused
"Objective", // 2 - Single-Map Objective - standard
"Stop Watch", // 3 - Stopwatch - standard
"Campaign", // 4 - Campaign - standard
"Last Man Standing", // 5 - Last Man Standing - standard
"Map Voting", // 6 - Map Voting - de facto standard
"Team Death Match", // 7 - Team Death Match - only a few mods (like Silent mod)
NULL // 8+ ???
};
#define MAX_WOLFET_TCETEST_TYPES 8
static char *wolfet_tcetest_gametypes[MAX_WOLFET_TCETEST_TYPES] = {
NULL, // 0 - Unknown
NULL, // 1 - Unknown
NULL, // 2 - Unknown
NULL, // 3 - Unknown
NULL, // 4 - Unknown
"Demolition", // 5 - Demolition
NULL, // 6 - Unknown
"Bodycount" // 7 - Bodycount
};
#define MAX_JK2_TYPES 9
static char *jk2_gametypes[MAX_JK2_TYPES] = {
"FFA", // 0 - Free For All
"Holocron", // 1 - Holocron
"Jedi Master", // 2 - Jedi Master
"Duel", // 3 - Duel
"FFA", // 4 - Free For All
"TFFA", // 5 - Team Free For All
NULL, // 6 - Unknown
"CTF", // 7 - Capture the Flag
"CTY" // 8 - Capture the Ysalimari
};
#define MAX_JK2_SABERMOD_TYPES 11
static char *jk2_sabermod_gametypes[MAX_JK2_SABERMOD_TYPES] = {
"FFA", // 0 - Free For All
"Holocron", // 1 - Holocron
"Jedi Master", // 2 - Jedi Master
"Duel", // 3 - Duel
"FFA", // 4 - Free For All
"TFFA", // 5 - Team Free For All
NULL, // 6 - Unknown
"CTF", // 7 - Capture the Flag
"CTY", // 8 - Capture the Ysalimari
"Red Rover", // 9 - Red Rover
"Clan Arena", // 10 - Clan Arena
};
#define MAX_JK3_TYPES 9
static char *jk3_gametypes[MAX_JK3_TYPES] = {
"FFA", // 0 - Free For All
NULL, // 1 - Unknown
NULL, // 2 - Unknown
"Duel", // 3 - Duel
"Power Duel", // 4 - Power Duel
NULL, // 5 - Unknown
"TFFA", // 6 - Team Free For All
"Siege", // 7 - Siege
"CTF" // 8 - Capture the Flag
};
#define MAX_ZEQ2LITE_TYPES 1
static char *zeq2lite_gametypes[MAX_ZEQ2LITE_TYPES] = {
"Struggle" // 0 - Struggle
};
#define MAX_Q3RALLY_TYPES 9
static char *q3rally_gametypes[MAX_Q3RALLY_TYPES] = {
"Race", // 0 = Racing
"Race DM", // 1 = Racing Deathmatch
NULL, // 2 = Single Player
"Derby", // 3 = Demolition Derby
"DM", // 4 = Deathmatch
"Team DM", // 5 = Team Deathmatch
"Team Race", // 6 = Team Racing
"Team Race DM", // 7 = Team Racing Deathmatch
"CTF" // 8 = Capture the Flag
};
struct q3a_gametype_s {
char* mod;
char** gametypes;
int number;
};
struct q3a_gametype_s q3a_gametype_map[] =
{
{
"baseq3",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"Quake3Arena",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"osp",
q3a_osp_gametypes,
MAX_Q3A_OSP_TYPES
},
{
"q3ut2",
q3a_ut2_gametypes,
MAX_Q3A_UT2_TYPES
},
{
"q3ut3",
q3a_ut3_gametypes,
MAX_Q3A_UT3_TYPES
},
{
"q3ut4",
q3a_ut3_gametypes,
MAX_Q3A_UT3_TYPES
},
{
"threewave",
q3a_threewave_gametypes,
MAX_Q3A_THREEWAVE_TYPES
},
{
"seals",
q3a_seals_gametypes,
MAX_Q3A_SEALS_TYPES
},
{
"TribalCTF",
q3a_tribalctf_gametypes,
MAX_Q3A_TRIBALCTF_TYPES
},
{
"missionpack",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"generations",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"truecombat",
q3a_truecombat_gametypes,
MAX_Q3A_TRUECOMBAT_TYPES
},
{
"q3tc045",
q3a_q3tc045_gametypes,
MAX_Q3A_Q3TC045_TYPES
},
{
"freeze",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"afterwards",
q3a_afterwards_gametypes,
MAX_Q3A_AFTERWARDS_TYPES
},
{
"cpma",
q3a_cpma_gametypes,
MAX_Q3A_CPMA_TYPES
},
{
"arena",
q3a_arena_gametypes,
MAX_Q3A_ARENA_TYPES
},
{
"instaunlagged",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"instagibplus",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"beryllium",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"excessive",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"q3f",
q3a_q3f_gametypes,
MAX_Q3A_Q3F_TYPES
},
{
"q3f2",
q3a_q3f_gametypes,
MAX_Q3A_Q3F_TYPES
},
{
"westernq3",
q3a_wq3_gametypes,
MAX_Q3A_WQ3_TYPES
},
{
"wop",
q3a_wop_gametypes,
MAX_Q3A_WOP_TYPES
},
{
"excessiveplus",
q3a_excessiveplus_gametypes,
MAX_Q3A_EXCESSIVEPLUS_TYPES
},
{
"excessivemaz",
q3a_excessiveplus_gametypes,
MAX_Q3A_EXCESSIVEPLUS_TYPES
},
{
// Open Arena specific
"baseoa",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
NULL,
NULL,
0
}
};
#define oa_gametype_map q3a_gametype_map
struct q3a_gametype_s smokinguns_gametype_map[] =
{
{
"smokinguns",
smokinguns_gametypes,
MAX_SMOKINGUNS_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s wop_gametype_map[] =
{
{
"WorldofPadman",
worldofpadman_gametypes,
MAX_WORLDOFPADMAN_TYPES
},
{
"wop",
worldofpadman_gametypes,
MAX_WORLDOFPADMAN_TYPES
},
{
"wop_beryllium",
worldofpadman_gametypes,
MAX_WORLDOFPADMAN_TYPES
},
{
"pcs",
worldofpadman_gametypes,
MAX_WORLDOFPADMAN_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s wolf_gametype_map[] =
{
{
"main",
wolf_gametypes,
MAX_WOLF_TYPES
},
{
"wolfmp",
wolf_gametypes,
MAX_WOLF_TYPES
},
{
"bani",
wolf_gametypes, // exports additional gametypes via g_gametype2
MAX_WOLF_TYPES
},
{
"headshot",
wolf_gametypes,
MAX_WOLF_TYPES
},
{
"osp",
wolf_gametypes,
MAX_WOLF_TYPES
},
{
"shrubmod",
wolf_gametypes,
MAX_WOLF_TYPES
},
{
NULL,
NULL,
0
}
};
// didn't find docu about this, so use q3a types
struct q3a_gametype_s ef_gametype_map[] =
{
{
"baseEF",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
"EliteForce",
q3a_gametypes,
MAX_Q3A_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s wolfet_gametype_map[] =
{
{ // this one is also a fallback for mods not described here
"et",
wolfet_gametypes,
MAX_WOLFET_TYPES
},
{
"tcetest",
wolfet_tcetest_gametypes,
MAX_WOLFET_TCETEST_TYPES
},
{
"cqbtest",
wolfet_tcetest_gametypes,
MAX_WOLFET_TCETEST_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s jk2_gametype_map[] =
{
{ // this one is also a fallback for mods not described here
"basejk",
jk2_gametypes,
MAX_JK2_TYPES
},
{
"base",
jk2_gametypes,
MAX_JK2_TYPES
},
{
"SaberMod",
jk2_sabermod_gametypes,
MAX_JK2_SABERMOD_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s jk3_gametype_map[] =
{
{
"basejka",
jk3_gametypes,
MAX_JK3_TYPES
},
{
"base",
jk3_gametypes,
MAX_JK3_TYPES
},
{
"japlus",
jk3_gametypes,
MAX_JK3_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s iourt_gametype_map[] =
{
{
"q3ut4",
q3a_ut3_gametypes,
MAX_Q3A_UT3_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s zeq2lite_gametype_map[] =
{
{
"ZEQ2",
zeq2lite_gametypes,
MAX_ZEQ2LITE_TYPES
},
{
NULL,
NULL,
0
}
};
struct q3a_gametype_s q3rally_gametype_map[] =
{
{
"baseq3r",
q3rally_gametypes,
MAX_Q3RALLY_TYPES
},
{
"Q3Rally",
q3rally_gametypes,
MAX_Q3RALLY_TYPES
},
{
NULL,
NULL,
0
}
};
void q3_decode_gametype (struct server *s, struct q3a_gametype_s map[]) {
char *endptr;
int n;
int found=0;
struct q3a_gametype_s* ptr;
if (!s->game) return;
n = strtol (s->gametype, &endptr, 10);
// strtol returns a pointer to the first invalid digit, if both pointers
// are equal there was no number at all
if (s->gametype == endptr) {
return;
}
for (ptr=map; !found && ptr && ptr->mod != NULL; ptr++) {
if (!strcasecmp (s->game, ptr->mod)
&& n >=0
&& n < ptr->number
&& ptr->gametypes[n]) {
s->gametype = ptr->gametypes[n];
found=1;
}
// else
// Exact match not found - use the first one in the list
// which should be the game's original game types
// s->gametype = map->gametypes[n];
}
}
void q3_decode_gametype_fallback (struct server *s, struct q3a_gametype_s map[]) {
char *endptr;
int n;
struct q3a_gametype_s* ptr;
if (!s->game) return;
n = strtol (s->gametype, &endptr, 10);
// strtol returns a pointer to the first invalid digit, if both pointers
// are equal there was no number at all
if (s->gametype == endptr || n < 0) {
return;
}
// Exact match not found - use the first one in the list
// which should be the game's original game types
if (n < map->number && map->gametypes[n])
s->gametype = map->gametypes[n];
for (ptr=map; ptr && ptr->mod; ptr++) {
if (!strcasecmp (s->game, ptr->mod)
&& n < ptr->number
&& ptr->gametypes[n]) {
s->gametype = ptr->gametypes[n];
break;
}
}
}
static void q3_analyze_serverinfo (struct server *s) {
char **info_ptr;
long n;
char *fs_game=NULL;
char *game=NULL;
char *gamename=NULL;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
// check if it's really a q3 server. We need to do that first to determine
// whether the server protcol for that game is compatible
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "version") == 0) {
// Quake 3 Arena
if (!strncmp(info_ptr[1], "Q3", 2) || !strncmp(info_ptr[1], "ioq3 ", 5) || !strncmp(info_ptr[1], "ioQ3 ", 5)) {
s->type=Q3_SERVER;
}
// Open Arena
if (!strncmp(info_ptr[1], "ioq3+oa", 7)) {
s->type=OPENARENA_SERVER;
}
// Return to Castle Wolfenstein
else if (!strncmp(info_ptr[1], "Wolf", 4)) {
s->type=WO_SERVER;
}
// Wolfenstein: Enemy Territory or Enemy Territory: Legacy, recognize as Wolf:ET, discriminate later
else if (!strncmp(info_ptr[1], "ET", 2)) {
s->type=WOET_SERVER;
}
// Voyager: Elite Force
else if (!strncmp(info_ptr[1], "ST:V HM", 7)) {
s->type=EF_SERVER;
}
// Medal of Honor: Allied Assault
else if (!strncmp(info_ptr[1], "Medal", 5)) {
s->type=MOHAA_SERVER;
}
// Soldier Of Fortune 2
else if (!strncmp(info_ptr[1], "SOF2MP", 6)) {
s->type=SOF2S_SERVER;
}
// Tremulous 1.1, Tremulous GPP
else if (!strncmp(info_ptr[1], "tremulous", 9)) {
if (!strncmp(info_ptr[1], "tremulous 1.1.0", 15)) {
s->type=TREMULOUS_SERVER;
}
else if (!strncmp(info_ptr[1], "tremulous gpp1", 14)) {
s->type=TREMULOUSGPP_SERVER;
}
else {
s->type=TREMULOUS_SERVER;
}
}
// Tremulous 1.1 mod KoRx
else if (!strncmp(info_ptr[1], "KoRx", 4)) {
s->type=TREMULOUS_SERVER;
}
// Tremfusion
else if (!strncmp(info_ptr[1], "tremfusion", 10)) {
s->type=TREMFUSION_SERVER;
}
// Unvanquished
else if (!strncmp(info_ptr[1], "Unvanquished", 12)) {
s->type=UNVANQUISHED_SERVER;
}
// World of Padman
else if (!strncmp(info_ptr[1], "wop", 3)) {
s->type=WOP_SERVER;
}
// Urban Terror
else if (games[IOURT_SERVER].cmd && (!strncmp(info_ptr[1], "ioq3 1.35urt", 12) || !strncmp(info_ptr[1], "ioq3-urt", 8))) {
s->type=IOURT_SERVER;
}
// Reaction
else if (!strncmp(info_ptr[1], "Reaction", 8)) {
s->type=REACTION_SERVER;
}
// Q3 Rally
else if (!strncmp(info_ptr[1], "Q3Rally", 7)) {
s->type=Q3RALLY_SERVER;
}
// Smokin' Guns
else if (!strncmp(info_ptr[1], "Smokin' Guns", 12)) {
s->type=SMOKINGUNS_SERVER;
}
// ZEQ2 Lite
else if (!strncmp(info_ptr[1], "zeq2lite", 8)) {
s->type=ZEQ2LITE_SERVER;
}
// Spearmint
else if (!strncmp(info_ptr[1], "Spearmint", 9)) {
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "gamename") == 0) {
// Turtle Arena
if (!strcmp(info_ptr[1], "TurtleArena")) {
s->type=TURTLEARENA_SERVER;
}
break;
}
}
}
break;
}
}
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
/*
fs_game sets the active directory and is how one chooses
a mod on the command line. This should not show up in
the server string but some times it does. We will
take either fs_game, game or gamename as the "game" string.
--baa
*/
if (strcmp (*info_ptr, "fs_game") == 0) {
fs_game = info_ptr[1];
}
else if (strcmp (*info_ptr, "gamename") == 0) {
gamename = info_ptr[1];
}
else if (strcmp (*info_ptr, "game") == 0) {
game = info_ptr[1];
}
else if (strcmp (*info_ptr, "version") == 0) {
if (strstr (info_ptr[1], "linux")) {
s->sv_os = 'L';
} else if (strstr (info_ptr[1], "win")) {
s->sv_os = 'W';
} else if (strstr (info_ptr[1], "Mac")) {
s->sv_os = 'M';
} else {
s->sv_os = '?';
}
}
else if (!s->gametype && strcmp (*info_ptr, "g_gametype") == 0) {
s->gametype = info_ptr[1];
}
else if (s->type == MOHAA_SERVER &&
strcmp (*info_ptr, "g_gametypestring") == 0) {
s->gametype = info_ptr[1];
}
else if (s->type == WARSOW_SERVER &&
strcmp (*info_ptr, "gametype") == 0) {
s->gametype = info_ptr[1];
}
else if (s->type == TURTLEARENA_SERVER &&
strcmp (*info_ptr, "sv_gametypeName") == 0) {
s->gametype = info_ptr[1];
}
// unescape gametype (there is escape code in CoD gametype, if other games do it too, add here)
if (s->type == COD_SERVER || s->type == CODUO_SERVER) {
if (s->gametype) {
// unescape in place, for backward compatibility
gchar* tmp_gametype = g_malloc0 (sizeof (gchar) * strlen (s->gametype) + 1);
unescape_game_string(tmp_gametype, s->gametype, games[s->type].color_flags);
strcpy(s->gametype, tmp_gametype);
g_free(tmp_gametype);
}
}
else if (strcmp (*info_ptr, "g_needpass") == 0) {
n = strtol (info_ptr[1], NULL, 10);
if ((n & 1) != 0)
s->flags |= SERVER_PASSWORD;
if ((n & 2) != 0)
s->flags |= SERVER_SP_PASSWORD;
}
else if (strcmp (*info_ptr, "cheats") == 0) {
s->flags |= SERVER_CHEATS;
}
else if (g_ascii_strcasecmp (*info_ptr, "sv_privateClients") == 0) {
s->private_client = strtol (info_ptr[1], NULL, 10);
}
else if (!strcmp(*info_ptr, "sv_punkbuster") && info_ptr[1] && info_ptr[1][0] == '1') {
s->flags |= SERVER_PUNKBUSTER;
}
else if (!strcmp(*info_ptr, "protocol")) {
const char* masterprotocol = game_get_attribute(s->type, "masterprotocol");
if (masterprotocol && !strcmp(masterprotocol, "auto"))
masterprotocol = game_get_attribute(s->type, "_masterprotocol");
if (masterprotocol && strcmp(masterprotocol, info_ptr[1]))
s->flags |= SERVER_INCOMPATIBLE;
}
}
if (fs_game) {
s->game=fs_game;
}
else if (game) {
s->game=game;
// since ioq3, many mods have same declaration (ioq3), we can discriminate with game or gamename
// also, some mods are played as mod or as standalone, prefer standalone to mods when standalone available
// but recognize standalone only if standalone is configured
if (games[REACTION_SERVER].cmd && !strncmp(game, "rq3", 3)) {
s->type=REACTION_SERVER;
}
else if (games[IOURT_SERVER].cmd && !strncmp(game, "q3ut4", 5)) {
s->type=IOURT_SERVER;
}
}
else if (gamename) {
s->game=gamename;
}
// discriminate Wolfenstein: Enemy Territory and Enemy Territory: Legacy
if (s->type == WOET_SERVER) {
// play with Wolf:ET if ET:L is not installed
if (games[WOET_SERVER].cmd && !games[ETL_SERVER].cmd) {
s->type = WOET_SERVER;
}
// play with ET:L if Wolf:ET is not installed
else if (!games[WOET_SERVER].cmd && games[ETL_SERVER].cmd) {
s->type = ETL_SERVER;
}
// if both are installed
else if (games[WOET_SERVER].cmd && games[ETL_SERVER].cmd) {
// use ETL by default
s->type = ETL_SERVER;
// if etpro mod or punkbuster server, use Wolf:ET
if (s->game) {
if (strcmp (s->game, "etpro") == 0) {
s->type = WOET_SERVER;
}
}
if (s->flags) {
if (s->flags & SERVER_PUNKBUSTER) {
s->type = WOET_SERVER;
}
}
}
}
// launch Quake III Arena games hosted by OpenArena Servers with Quake III Arena if installed
// if server detected as an OpenArena server
else if (s->type == OPENARENA_SERVER) {
// if Quake III Arena is installed
if (games[Q3_SERVER].cmd) {
// if game is Quake III Arena
if (s->game) {
if (strcmp (s->game, "baseq3") == 0 || strcmp (s->game, "Quake3Arena") == 0) {
s->type = Q3_SERVER;
}
}
}
}
// detect well known OpenArena mods hosted by badly detected OpenArena servers
else if (s->type == Q3_SERVER) {
if (games[OPENARENA_SERVER].cmd) {
if (s->game) {
if (strcmp (s->game, "baseoa") == 0 || strcmp(s->game, "oaplus") == 0) {
s->type = OPENARENA_SERVER;
}
}
}
}
if (s->gametype) {
if (s->type == Q3_SERVER) {
q3_decode_gametype(s, q3a_gametype_map);
}
else if (s->type == WO_SERVER) {
q3_decode_gametype(s, wolf_gametype_map);
}
else if (s->type == EF_SERVER) {
q3_decode_gametype(s, ef_gametype_map);
}
else if (s->type == WOET_SERVER || s->type == ETL_SERVER) {
// There is a ton of mods and many of them use default gametype numbers
q3_decode_gametype_fallback(s, wolfet_gametype_map);
}
else if (s->type == JK2_SERVER) {
// There is a ton of mods and they all use default gametype numbers
q3_decode_gametype_fallback(s, jk2_gametype_map);
}
else if (s->type == JK3_SERVER) {
// There is a ton of mods and they all use default gametype numbers
q3_decode_gametype_fallback(s, jk3_gametype_map);
}
else if (s->type == IOURT_SERVER) {
q3_decode_gametype(s, iourt_gametype_map);
}
else if (s->type == WOP_SERVER) {
q3_decode_gametype(s, wop_gametype_map);
}
else if (s->type == SMOKINGUNS_SERVER) {
q3_decode_gametype(s, smokinguns_gametype_map);
}
else if (s->type == ZEQ2LITE_SERVER) {
q3_decode_gametype(s, zeq2lite_gametype_map);
}
else if (s->type == OPENARENA_SERVER) {
q3_decode_gametype(s, oa_gametype_map);
}
else if (s->type == Q3RALLY_SERVER) {
q3_decode_gametype(s, q3rally_gametype_map);
}
}
}
static void doom3_analyze_serverinfo (struct server *s) {
char **info_ptr;
char *fs_game=NULL;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0) {
s->flags |= SERVER_SPECTATE;
}
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (strcmp (*info_ptr, "fs_game") == 0) {
fs_game = info_ptr[1];
}
else if (strcmp (*info_ptr, "si_version") == 0) {
if (strstr (info_ptr[1], "linux")) {
s->sv_os = 'L';
} else if (strstr (info_ptr[1], "win")) {
s->sv_os = 'W';
} else if (strstr (info_ptr[1], "Mac")) {
s->sv_os = 'M';
} else {
s->sv_os = '?';
}
}
else if (!s->gametype && strcmp (*info_ptr, "si_gameType") == 0) {
s->gametype = info_ptr[1];
}
else if (strcmp (*info_ptr, "si_usepass") == 0
|| strcmp (*info_ptr, "si_needPass") == 0) {
int n = atoi (info_ptr[1]);
if ((n & 1) != 0)
s->flags |= SERVER_PASSWORD;
if ((n & 2) != 0)
s->flags |= SERVER_SP_PASSWORD;
}
else if (strcmp (*info_ptr, "osmask") == 0) {
char* p;
unsigned long n = strtol(info_ptr[1], &p, 0);
if (info_ptr[1] != p && (n & 4) != 4) {
s->flags |= SERVER_INCOMPATIBLE;
}
}
else if ((!strcmp(*info_ptr, "sv_punkbuster")
|| !strcmp(*info_ptr, "net_serverPunkbusterEnabled"))
&& info_ptr[1] && info_ptr[1][0] == '1') {
s->flags |= SERVER_PUNKBUSTER;
}
}
if (fs_game) {
s->game=fs_game;
}
}
static void ottd_analyze_serverinfo (struct server *s) {
char **info_ptr;
/* Clear out the flags */
s->flags = 0;
if ((games[s->type].flags & GAME_SPECTATE) != 0)
s->flags |= SERVER_SPECTATE;
for (info_ptr = s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (!strcmp(*info_ptr, "password") && strcmp(info_ptr[1], "0")) {
s->flags |= SERVER_PASSWORD;
if (games[s->type].flags & GAME_SPECTATE)
s->flags |= SERVER_SP_PASSWORD;
}
}
}
static int quake_config_is_valid (struct server *s) {
struct stat stat_buf;
char *cfgdir;
char *path;
struct game *g = &games[s->type];
if (g->main_mod && g->main_mod[0])
cfgdir = g->main_mod[0];
else
return FALSE;
if (g->cmd == NULL || g->cmd[0] == '\0') {
// %s = game name e.g. QuakeWorld
dialog_ok (NULL, _("%s command line is empty."), g->name);
return FALSE;
}
if (g->real_dir != NULL && g->real_dir[0] != '\0') {
if (stat (g->real_dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode)) {
// directory name, game name
dialog_ok (NULL, _("\"%s\" is not a directory\n"
"Please specify correct %s working directory."),
g->real_dir, g->name);
return FALSE;
}
}
path = file_in_dir (g->real_dir, cfgdir);
if (stat (path, &stat_buf) || !S_ISDIR (stat_buf.st_mode)) {
if (!g->real_dir || g->real_dir[0] == '\0') {
// game name
dialog_ok (NULL, _("Please specify correct %s working directory."),
g->name);
}
else {
dialog_ok (NULL,
// directory, subdirectory, game name
_("Directory \"%s\" doesn\'t contain \"%s\" subdirectory.\n"
"Please specify correct %s working directory."),
g->real_dir, cfgdir, g->name);
}
g_free (path);
return FALSE;
}
g_free (path);
return TRUE;
}
static int config_is_valid_generic (struct server *s) {
struct stat stat_buf;
struct game *g = &games[s->type];
if (g->cmd == NULL || g->cmd[0] == '\0') {
dialog_ok (NULL, "%s command line is empty.", g->name);
return FALSE;
}
if (g->real_dir != NULL && g->real_dir[0] != '\0') {
if (stat (g->real_dir, &stat_buf) != 0 || !S_ISDIR (stat_buf.st_mode)) {
// %s directory, game name
dialog_ok (NULL, _("\"%s\" is not a directory\n"
"Please specify correct %s working directory."),
g->real_dir, g->name);
return FALSE;
}
}
return TRUE;
}
static int complain_launch_cant_write_file(const char* file) {
return dialog_yesno (NULL, 1, _("Launch"), _("Cancel"),
//%s frontend.cfg
_("Cannot write to file \"%s\".\n\nLaunch client anyway?"), file);
}
static FILE *open_cfg (const char *filename, int private) {
FILE *f;
mode_t saved_umask;
if (private)
saved_umask = umask(0077);
f = fopen (filename, "w");
if (f)
fprintf (f, "//\n// generated by XQF, do not modify\n//\n");
if (private)
umask(saved_umask);
return f;
}
static int real_password (const char *password) {
if (!password || (password[0] == '1' && password[1] == '\0'))
return FALSE;
return TRUE;
}
static int write_passwords (const char *filename, const struct condef *con) {
FILE *f;
f = open_cfg (filename, TRUE);
if (!f)
return FALSE;
if (con->password)
fprintf (f, "password \"%s\"\n", con->password);
if (con->spectator_password)
fprintf (f, "spectator \"%s\"\n", con->spectator_password);
if (con->rcon_password)
fprintf (f, "rcon_password \"%s\"\n", con->rcon_password);
fclose (f);
return TRUE;
}
static int write_q1_vars (const struct condef *con) {
FILE *f;
int ret = TRUE;
struct game *g = &games[con->s->type];
char* file = NULL;
if (g->main_mod && g->main_mod[0])
file = g->main_mod[0];
else
return FALSE;
file = g_strjoin("/", g->real_dir, file, EXEC_CFG, NULL);
f = open_cfg (file, FALSE);
if (!f) { ret = FALSE; goto out; }
if (default_q1_name)
fprintf (f, "name \"%s\"\n", default_q1_name);
fprintf (f, "color %d %d\n", default_q1_top_color, default_q1_bottom_color);
if (games[Q1_SERVER].game_cfg)
fprintf (f, "exec \"%s\"\n", games[Q1_SERVER].game_cfg);
if (con->custom_cfg)
fprintf (f, "exec \"%s\"\n", con->custom_cfg);
fclose (f);
out:
if (!ret)
ret = complain_launch_cant_write_file(file);
g_free(file);
return ret;
}
static int write_qw_vars (const struct condef *con) {
FILE *f;
int auto_pl;
int ret = TRUE;
struct game *g = &games[con->s->type];
char* file = NULL;
if (g->main_mod && g->main_mod[0])
file = g->main_mod[0];
else
return FALSE;
file = g_strjoin("/", g->real_dir, file, EXEC_CFG, NULL);
f = open_cfg (file, FALSE);
if (!f) { ret = FALSE; goto out; }
if (default_qw_name)
fprintf (f, "name \"%s\"\n", default_qw_name);
if (default_qw_skin)
fprintf (f, "skin \"%s\"\n", default_qw_skin);
fprintf (f, "team \"%s\"\n", (default_qw_team)? default_qw_team : "");
fprintf (f, "topcolor \"%d\"\n", default_qw_top_color);
fprintf (f, "bottomcolor \"%d\"\n", default_qw_bottom_color);
switch (pushlatency_mode) {
case 1: /* automatic pushlatency */
if (con->s->ping <= 0)
auto_pl = 10; /* "min" value */
else if (con->s->ping >= 2000)
auto_pl = 1000; /* "max" value */
else {
auto_pl = con->s->ping / 2;
auto_pl = ((auto_pl + 9) / 10) * 10; /* beautify it */
}
fprintf (f, "pushlatency %d\n", -auto_pl);
break;
case 2: /* fixed value */
fprintf (f, "pushlatency %d\n", pushlatency_value);
break;
default: /* do not touch */
break;
}
fprintf (f, "rate \"%d\"\n", default_qw_rate);
fprintf (f, "cl_nodelta \"%d\"\n", default_qw_cl_nodelta);
fprintf (f, "cl_predict_players \"%d\"\n", default_qw_cl_predict);
fprintf (f, "noaim \"%d\"\n", default_noaim);
fprintf (f, "noskins \"%d\"\n", default_qw_noskins);
if (default_w_switch >= 0)
fprintf (f, "setinfo w_switch \"%d\"\n", default_w_switch);
if (default_b_switch >= 0)
fprintf (f, "setinfo b_switch \"%d\"\n", default_b_switch);
if (games[QW_SERVER].game_cfg)
fprintf (f, "exec \"%s\"\n", games[QW_SERVER].game_cfg);
if (con->custom_cfg)
fprintf (f, "exec \"%s\"\n", con->custom_cfg);
fclose (f);
out:
if (!ret)
ret = complain_launch_cant_write_file(file);
g_free(file);
return ret;
}
static int write_q2_vars (const struct condef *con) {
FILE *f;
int ret = TRUE;
struct game *g = &games[con->s->type];
char* file = NULL;
if (g->main_mod && g->main_mod[0])
file = g->main_mod[0];
else
return FALSE;
file = g_strjoin("/", g->real_dir, file, EXEC_CFG, NULL);
f = open_cfg (file, FALSE);
if (!f) { ret = FALSE; goto out; }
if (default_q2_name)
fprintf (f, "set name \"%s\"\n", default_q2_name);
if (default_q2_skin)
fprintf (f, "set skin \"%s\"\n", default_q2_skin);
fprintf (f, "set rate \"%d\"\n", default_q2_rate);
fprintf (f, "set cl_nodelta \"%d\"\n", default_q2_cl_nodelta);
fprintf (f, "set cl_predict \"%d\"\n", default_q2_cl_predict);
fprintf (f, "set cl_noskins \"%d\"\n", default_q2_noskins);
if (games[Q2_SERVER].game_cfg)
fprintf (f, "exec \"%s\"\n", games[Q2_SERVER].game_cfg);
if (con->custom_cfg)
fprintf (f, "exec \"%s\"\n", con->custom_cfg);
fclose (f);
out:
if (!ret)
ret = complain_launch_cant_write_file(file);
g_free(file);
return ret;
}
#if 0
static int is_dir (const char *dir, const char *gamedir) {
struct stat stat_buf;
char *path;
int res = FALSE;
if (dir && gamedir) {
path = file_in_dir (dir, gamedir);
res = (stat (path, &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode));
g_free (path);
}
return res;
}
#endif
static int teeworlds_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char buf[128];
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
if (con->s->port != g->default_port) {
g_snprintf(buf, 128, "connect %s:%d", con->server, con->s->port);
}
else {
g_snprintf(buf, 128, "connect %s", con->server);
}
argv[argi++] = buf;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static int q1_exec_generic (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char buf[16];
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (default_nosound)
argv[argi++] = "-nosound";
if (default_nocdaudio)
argv[argi++] = "-nocdaudio";
argv[argi++] = "+exec";
argv[argi++] = EXEC_CFG;
if (con->demo) {
argv[argi++] = "+record";
argv[argi++] = con->demo;
}
if (con->server) {
if (con->s->port != g->default_port) {
g_snprintf (buf, 16, "%d", con->s->port);
argv[argi++] = "+port";
argv[argi++] = buf;
}
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static int qw_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
char *file = NULL;
struct game *g = &games[con->s->type];
int retval=-1;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (default_nosound)
argv[argi++] = "-nosound";
if (default_nocdaudio)
argv[argi++] = "-nocdaudio";
if (con->gamedir) {
argv[argi++] = "-gamedir";
argv[argi++] = con->gamedir;
}
if (con->password || con->rcon_password
|| real_password (con->spectator_password)) {
if (g->main_mod && g->main_mod[0])
file = g_strjoin("/", g->real_dir, g->main_mod[0], PASSWORD_CFG, NULL);
if (file && !write_passwords (file, con)
&& !complain_launch_cant_write_file(file))
goto out;
argv[argi++] = "+exec";
argv[argi++] = PASSWORD_CFG;
}
else {
if (con->spectator_password) {
argv[argi++] = "+spectator";
argv[argi++] = con->spectator_password;
}
}
argv[argi++] = "+exec";
argv[argi++] = EXEC_CFG;
if (con->server) {
if (con->demo) {
argv[argi++] = "+record";
argv[argi++] = con->demo;
if (con->s->type == QW_SERVER)
argv[argi++] = con->server;
}
else {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
out:
g_free(file);
g_free(cmd);
return retval;
}
static int q2_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
char *file = NULL;
struct game *g = &games[con->s->type];
int retval=-1;
if (g->main_mod && g->main_mod[0])
file = g_strjoin("/", g->real_dir, g->main_mod[0], PASSWORD_CFG, NULL);
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (default_nosound) {
argv[argi++] = "+set";
argv[argi++] = "s_initsound";
argv[argi++] = "0";
}
argv[argi++] = "+set";
argv[argi++] = "cd_nocd";
argv[argi++] = (default_nocdaudio)? "1" : "0";
if (con->gamedir) {
argv[argi++] = "+set";
argv[argi++] = "game";
argv[argi++] = con->gamedir;
}
if (con->password || con->rcon_password
|| real_password (con->spectator_password)) {
if (file && !write_passwords (file, con)
&& !complain_launch_cant_write_file(file))
goto out;
argv[argi++] = "+exec";
argv[argi++] = PASSWORD_CFG;
}
else {
if (con->spectator_password) {
argv[argi++] = "+spectator";
argv[argi++] = con->spectator_password;
}
}
argv[argi++] = "+exec";
argv[argi++] = EXEC_CFG;
if (con->server) {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
if (con->demo) {
argv[argi++] = "+record";
argv[argi++] = con->demo;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
out:
g_free (file);
g_free (cmd);
return retval;
}
static int q2_exec_generic (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (default_nosound) {
argv[argi++] = "+set";
argv[argi++] = "s_initsound";
argv[argi++] = "0";
}
argv[argi++] = "+set";
argv[argi++] = "cd_nocd";
argv[argi++] = (default_nocdaudio)? "1" : "0";
if (con->gamedir) {
argv[argi++] = "+set";
argv[argi++] = "game";
argv[argi++] = con->gamedir;
}
if (con->server) {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
if (con->demo) {
argv[argi++] = "+record";
argv[argi++] = con->demo;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static int q3_exec (const struct condef *con, int forkit) {
char *argv[64];
int argi = 0;
int cmdi = 0;
char* cmd = NULL;
char *protocol = NULL;
char** cmdtokens = NULL;
char *punkbuster;
char* protocmdtofree = NULL;
char** additional_args = NULL;
char** memoryoptions = NULL;
int i;
struct game *g = NULL;
int retval;
int game_match_result = 0;
char *real_game_dir = NULL;
int setfs_game = 0;
int set_punkbuster = 0;
int enable_console = 0;
int pass_memory_options = 0;
// struct quake_private* pd = NULL;
if (!con) return -1;
if (!con->s) return -1;
g = &games[con->s->type];
if (!g) return -1;
// pd = (struct quake_private*)games[con->s->type].pd;
setfs_game = str2bool(game_get_attribute(g->type, "setfs_game"));
set_punkbuster = str2bool(game_get_attribute(g->type, "set_punkbuster"));
enable_console = str2bool(game_get_attribute(g->type, "enable_console"));
pass_memory_options = str2bool(game_get_attribute(g->type, "pass_memory_options"));
cmdtokens = g_strsplit(g->cmd, " ", 0);
if (cmdtokens && *cmdtokens[cmdi])
cmd=cmdtokens[cmdi++];
/*
Figure out what protocal the server is running so we can try to connect
with a specialized quake3 script. You need to name the scripts like
quake3proto48.
*/
protocol = strdup_strip(find_server_setting_for_key ("protocol", con->s->info));
if (cmd && protocol) {
char* tmp = g_strdup_printf("%sproto%s", cmd, protocol);
char* tmp_cmd = expand_tilde(tmp);
g_free(tmp);
tmp = NULL;
g_free(protocol);
protocol = NULL;
debug (1, "Check for '%s' as a command", tmp_cmd);
// Check to see if we can find that generated filename
// absolute path?
if (tmp_cmd[0] == '/') {
if (!access(tmp_cmd, X_OK)) {
cmd = protocmdtofree = strdup(tmp_cmd);
debug(1, "absolute path %s", cmd);
}
}
// no, check $PATH
else {
char* file = find_file_in_path(tmp_cmd);
if (file) {
cmd = protocmdtofree = file;
debug(1, "use file %s in path", cmd);
}
}
g_free(tmp_cmd);
}
while (cmd) {
if (*cmd) // skip empty
{
argv[argi++] = cmd;
}
cmd = cmdtokens[cmdi++];
}
if (default_nosound) {
argv[argi++] = "+set";
argv[argi++] = "s_initsound";
argv[argi++] = "0";
}
if (con->rcon_password) {
const char* pwvarname = NULL;
if ((pwvarname = game_get_attribute(con->s->type, "+rconpassword")))
argv[argi++] = (char*)pwvarname;
else
argv[argi++] = "+rconpassword";
argv[argi++] = con->rcon_password;
}
if (con->password) {
argv[argi++] = "+password";
argv[argi++] = con->password;
}
if (con->server) {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
if (con->demo) {
argv[argi++] = "+record";
argv[argi++] = con->demo;
}
if (g->game_cfg) {
argv[argi++] = "+exec";
argv[argi++] = g->game_cfg;
}
if (con->custom_cfg) {
argv[argi++] = "+exec";
argv[argi++] = con->custom_cfg;
}
/* The 1.32 release of Q3A needs +set cl_punkbuster 1 on the command line. */
punkbuster = find_server_setting_for_key ("sv_punkbuster", con->s->info);
if (punkbuster != NULL && strcmp(punkbuster, "1") == 0) {
if (set_punkbuster) {
argv[argi++] = "+set";
argv[argi++] = "cl_punkbuster";
argv[argi++] = "1";
}
else {
char* option = g_strdup_printf("/" CONFIG_FILE "/Game: %s/punkbuster dialog shown", type2id(g->type));
debug(1, "Got %s for punkbuster\n", punkbuster);
if (!config_get_bool (option)) {
dialog_ok (NULL, _("The server has Punkbuster enabled but it is not going\nto be set on the command line.\nYou may have problems connecting.\nYou can fix this in the game preferences."));
config_set_bool (option, TRUE);
}
g_free(option);
}
}
if (enable_console) {
argv[argi++] = "+set";
argv[argi++] = "com_allowConsole";
argv[argi++] = "1";
}
/*
If the s->game is set, we want to put fs_game on the command
line so that the mod is loaded when we connect.
*/
if (setfs_game && con->s->game) {
gboolean is_mainmod = FALSE;
for (i = 0; g->main_mod && g->main_mod[i]; ++i) {
if (!g_ascii_strcasecmp(con->s->game, g->main_mod[i])) {
is_mainmod = TRUE;
break;
}
}
if (!is_mainmod) {
// Look in (e.g.) ~/.q3a first
if (g->real_home) {
real_game_dir = find_game_dir(g->real_home, con->s->game, &game_match_result);
}
if (!real_game_dir) {
// Didn't find in home directory so look in real directory if defined
real_game_dir = find_game_dir(g->real_dir, con->s->game, &game_match_result);
}
argv[argi++] = "+set";
argv[argi++] = "fs_game";
argv[argi++] = real_game_dir?real_game_dir:con->s->game;
}
}
if (pass_memory_options == TRUE) {
memoryoptions = g_new0(char*, 4); // must be null terminated => max three entries
argv[argi++] = "+set";
argv[argi++] = "com_hunkmegs";
argv[argi++] = memoryoptions[0] = strdup(game_get_attribute(g->type, "com_hunkmegs"));
argv[argi++] = "+set";
argv[argi++] = "com_zonemegs";
argv[argi++] = memoryoptions[1] = strdup(game_get_attribute(g->type, "com_zonemegs"));
argv[argi++] = "+set";
argv[argi++] = "com_soundmegs";
argv[argi++] = memoryoptions[2] = strdup(game_get_attribute(g->type, "com_soundmegs"));
}
// Append additional args if needed
i = 0;
additional_args = get_custom_arguments(g->type, con->s->game);
while (additional_args && additional_args[i]) {
argv[argi++] = additional_args[i];
i++;
}
argv[argi] = NULL;
debug(1, "argument count %d", argi);
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (real_game_dir);
g_free (protocmdtofree);
g_strfreev(additional_args);
g_strfreev(cmdtokens);
g_strfreev(memoryoptions);
return retval;
}
static int hl_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
// char** additional_args = NULL;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->gamedir) {
argv[argi++] = "-game";
argv[argi++] = con->gamedir;
}
if (con->server) {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
if (con->password) {
argv[argi++] = "+password";
argv[argi++] = con->password;
}
if (con->rcon_password) {
argv[argi++] = "+rcon_password";
argv[argi++] = con->rcon_password;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static int ut_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
char* hostport=NULL;
char* real_server=NULL;
char** additional_args = NULL;
char **info_ptr;
int i;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
// Pass server IP address first otherwise it won't work.
// Make sure ut/ut script (from installed game) contains
// exec "./ut-bin" $* -log and not -log $* at the end
// otherwise XQF can not connect via the command line!
if (g->flags & GAME_LAUNCH_HOSTPORT) {
// go through all server rules
for (info_ptr = con->s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (!strcmp (*info_ptr, "hostport")) {
hostport=info_ptr[1];
}
}
}
if (con->server) {
// gamespy port can be different from game port
if (hostport) {
real_server = g_strdup_printf ("%s:%s", inet_ntoa (con->s->host->ip), hostport);
}
else
real_server = strdup(con->server);
if (con->spectate) {
char* tmp = NULL;
if (real_password(con->spectator_password))
tmp = g_strdup_printf("%s?spectatoronly=true?password=%s", real_server, con->spectator_password);
else
tmp = g_strdup_printf("%s?spectatoronly=true", real_server);
g_free(real_server);
real_server=tmp;
}
// Add password if exists
if (con->password) {
char* tmp = g_strdup_printf("%s?password=%s", real_server, con->password);
g_free(real_server);
real_server=tmp;
}
}
if (con->s) {
// Append additional args if needed
i = 0;
additional_args = get_custom_arguments(con->s->type, con->s->game);
// if (!(additional_args && additional_args[i]))
argv[argi++] = real_server;
while (additional_args && additional_args[i]) {
argv[argi++] = additional_args[i];
/*
// append first argument to server address
if (i == 0) {
tmp = g_strconcat(real_server, additional_args[i], NULL);
g_free(real_server);
real_server=tmp;
argv[argi++] = real_server;
}
else {
argv[argi++] = additional_args[i];
}
*/
i++;
}
}
if (default_nosound) {
argv[argi++] = "-nosound";
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
g_free (real_server);
if (additional_args) g_strfreev(additional_args);
return retval;
}
static int savage_exec(const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
char* connect_arg = NULL;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
argv[argi++] = "set";
argv[argi++] = "autoexec";
connect_arg = g_strdup_printf("connect %s", con->server);
if (con->password) {
char* tmp = connect_arg;
connect_arg = g_strdup_printf("%s; set cl_password %s", connect_arg, con->password);
g_free(tmp);
}
if (con->rcon_password) {
char* tmp = connect_arg;
connect_arg = g_strdup_printf("%s; set cl_adminpassword %s", connect_arg, con->rcon_password);
g_free(tmp);
}
argv[argi++] = connect_arg;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
g_free (connect_arg);
return retval;
return 0;
}
// this one just passes the ip address as first parameter
static int exec_generic (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
// Serious Sam: only supports +connect
static int ssam_exec(const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
argv[argi++] = "+connect";
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
// Netpanzer: only supports -c
static int netpanzer_exec(const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
argv[argi++] = "-c";
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
// launch any game that uses the gamespy protocol
// the first argument is the content of gamename field (may be empty),
// the second one the ip of the server
static int gamespy_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = NULL;
int retval;
char **info_ptr;
char* gamename=NULL;
char* hostport=NULL;
char* real_server=NULL;
if (!con || !con->s)
return 1;
g = &games[con->s->type];
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
// go through all server rules
for (info_ptr = con->s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (!strcmp (*info_ptr, "gamename")) {
gamename = strdup_strip(info_ptr[1]);
}
else if (!strcmp (*info_ptr, "hostport")) {
hostport=info_ptr[1];
}
}
if (gamename)
argv[argi++] = gamename;
else
argv[argi++] = "";
if (con->server) {
// gamespy port can be different from game port
if (hostport) {
real_server = g_strdup_printf ("%s:%s", inet_ntoa (con->s->host->ip), hostport);
argv[argi++] = real_server;
}
else {
argv[argi++] = con->server;
}
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
g_free (real_server);
g_free (gamename);
return retval;
}
static int t2_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
if (default_t2_name && *default_t2_name) {
argv[argi++] = "-login";
argv[argi++] = default_t2_name;
}
argv[argi++] = "-online";
argv[argi++] = "-connect";
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static int bf1942_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = NULL;
int retval;
char **info_ptr;
char* gameid=NULL;
char* hostport=NULL;
char* real_server=NULL;
if (!con || !con->s)
return 1;
g = &games[con->s->type];
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
argv[argi++] = "+restart";
argv[argi++] = "1";
// go through all server rules
for (info_ptr = con->s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (!strcmp (*info_ptr, "gameid")) {
gameid=info_ptr[1];
}
else if (!strcmp (*info_ptr, "hostport")) {
hostport=info_ptr[1];
}
}
if (gameid) {
argv[argi++] = "+game";
argv[argi++] = gameid;
}
// argv[argi++] = strdup_strip (gameid);
if (con->server) {
argv[argi++] = "+joinServer";
// gamespy port can be different from game port
if (hostport) {
real_server = g_strdup_printf ("%s:%s", inet_ntoa (con->s->host->ip), hostport);
argv[argi++] = real_server;
}
else {
argv[argi++] = con->server;
}
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
g_free (real_server);
return retval;
}
static int descent3_exec (const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = NULL;
int retval;
char **info_ptr;
char* hostport=NULL;
char* real_server=NULL;
if (!con || !con->s)
return 1;
g = &games[con->s->type];
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
// go through all server rules
for (info_ptr = con->s->info; info_ptr && *info_ptr; info_ptr += 2) {
if (!strcmp (*info_ptr, "hostport")) {
hostport=info_ptr[1];
}
}
if (con->server) {
argv[argi++] = "--directip";
// gamespy port can be different from game port
if (hostport) {
real_server = g_strdup_printf ("%s:%s", inet_ntoa (con->s->host->ip), hostport);
argv[argi++] = real_server;
}
else {
argv[argi++] = con->server;
}
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
g_free (real_server);
return retval;
}
// OTTD: only supports -n
static int ottd_exec(const struct condef *con, int forkit) {
char *argv[32];
int argi = 0;
char *cmd;
struct game *g = &games[con->s->type];
int retval;
cmd = strdup_strip (g->cmd);
argv[argi++] = strtok (cmd, delim);
while ((argv[argi] = strtok (NULL, delim)) != NULL)
argi++;
if (con->server) {
argv[argi++] = "-n";
argv[argi++] = con->server;
}
argv[argi] = NULL;
retval = client_launch_exec (forkit, g->real_dir, argv, con->s);
g_free (cmd);
return retval;
}
static char *dir_custom_cfg_filter (const char *dir, const char *str) {
static const char *cfgext[] = { ".cfg", ".scr", ".rc", NULL };
const char **ext;
size_t len;
if (!str)
return NULL;
len = strlen (str);
for (ext = cfgext; *ext; ext++) {
if (len > strlen (*ext) &&
strcasecmp (str + len - strlen (*ext), *ext) == 0) {
return g_strdup (str);
}
}
return NULL;
}
static GList *custom_cfg_filter (GList *list) {
GList *tmp;
GList *res = NULL;
char *str;
for (tmp = list; tmp; tmp = tmp->next) {
str = (char *) tmp->data;
if (strcmp (str, EXEC_CFG) && strcmp (str, PASSWORD_CFG) &&
strcmp (str, "__qf__.cfg") && strcasecmp (str, "config.cfg") &&
strcasecmp (str, "server.cfg") && strcasecmp (str, "autoexec.cfg") &&
strcasecmp (str, "quake.rc") && strcasecmp (str, "q3config.cfg")) {
res = g_list_prepend (res, str);
}
else {
g_free (str);
}
}
g_list_free (list);
res = g_list_reverse (res);
return res;
}
static GList *quake_custom_cfgs (struct game* this, const char *path, const char *mod) {
GList *cfgs = NULL;
const char* dirs[2] = {0};
unsigned d, i;
debug(4, "%s, %s", path, mod);
dirs[0] = path?path:this->real_dir;
dirs[1] = this->real_home;
for (d = 0; d < sizeof(dirs)/sizeof(dirs[0]); ++d) {
if (!(dirs[d] && *dirs[d]))
continue;
for (i = 0; this->main_mod && this->main_mod[i]; ++i) {
char* dir = file_in_dir (dirs[d], this->main_mod[i]);
GList* tmp = dir_to_list (dir, dir_custom_cfg_filter);
cfgs = merge_sorted_string_lists (cfgs, tmp);
g_free (dir);
}
if (mod) {
char* dir = file_in_dir (dirs[d], mod);
GList* tmp = dir_to_list (dir, dir_custom_cfg_filter);
cfgs = merge_sorted_string_lists (cfgs, tmp);
g_free (dir);
}
}
cfgs = custom_cfg_filter (cfgs);
return cfgs;
}
static void quake_save_server_rules (FILE *f, struct server *s) {
char **info;
if (!s || !s->info)
return;
info = s->info;
while (info[0]) {
if (info[1])
fprintf (f, "%s=%s", info[0], info[1]);
else
fprintf (f, "%s", info[0]);
info += 2;
if (info[0])
fputc (QSTAT_DELIM, f);
}
fputc ('\n', f);
}
static void save_server_info (FILE *f, struct server *s) {
struct player *p;
GSList *list;
if (!s->name && !s->map && !s->players && !s->info && s->ping < 0)
return;
fprintf (f, "%ld %ld\n", s->refreshed, s->last_answer);
switch (s->type) {
/* POQS */
case Q1_SERVER:
case H2_SERVER:
fprintf (f,
"%s%s%s" QSTAT_DELIM_STR
"%s:%d" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%s:%d" QSTAT_DELIM_STR
"0" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d\n",
games[s->type].id,
s->server_query_type != UNKNOWN_SERVER? "|" : "",
s->server_query_type != UNKNOWN_SERVER? games[s->server_query_type].id : "",
inet_ntoa (s->host->ip), s->port,
(s->name)? s->name : "",
inet_ntoa (s->host->ip), s->port,
(s->map)? s->map : "",
s->maxplayers,
s->curplayers,
s->ping,
s->retries);
break;
/* QW, Q2 */
default:
fprintf (f,
"%s%s%s" QSTAT_DELIM_STR
"%s:%d" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d\n",
games[s->type].id,
s->server_query_type != UNKNOWN_SERVER? "|" : "",
s->server_query_type != UNKNOWN_SERVER? games[s->server_query_type].id : "",
inet_ntoa (s->host->ip), s->port,
(s->name)? s->name : "",
(s->map)? s->map : "",
s->maxplayers,
s->curplayers,
s->ping,
s->retries);
break;
} /* switch (s->type) */
quake_save_server_rules (f, s);
if (default_save_plrinfo) {
for (list = s->players; list; list = list->next) {
p = (struct player *) list->data;
switch (s->type) {
/* POQS */
case Q1_SERVER:
case H2_SERVER:
fprintf (f,
"0" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d\n",
(p->name)? p->name : "",
p->frags,
p->time,
p->shirt,
p->pants);
break;
/* QW */
case QW_SERVER:
case HW_SERVER:
fprintf (f,
"0" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%s\n",
(p->name)? p->name : "",
p->frags,
p->time,
p->shirt,
p->pants,
p->ping,
(p->skin)? p->skin : "");
break;
case UN_SERVER:
case UT2_SERVER:
case UT2004_SERVER:
case RUNE_SERVER:
case POSTAL2_SERVER:
case AAO_SERVER:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"\n",
(p->name)? p->name : "",
p->frags,
p->ping,
p->pants,
(p->skin)? p->skin : "",
(p->model)? p->model : "");
break;
/* Q2, etc... */
case T2_SERVER:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"%s" QSTAT_DELIM_STR
"\n", //tribe tag not supported yet
(p->name)? p->name : "",
p->frags,
0, // team number not supported yet
"x", // team name not supported yet
(p->model)? p->model : ""); // player_type
break;
case DESCENT3_SERVER:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"\n",
p->name?p->name:"",
p->frags,
0, // deaths
p->ping);
break;
case Q4_SERVER:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%s\n",
(p->name)? p->name : "",
p->frags,
p->ping,
p->model?p->model:"0");
break;
case HL_SERVER:
case HL_SERVER_OLD:
case HL2_SERVER:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d\n",
(p->name)? p->name : "",
p->frags,
p->time);
break;
default:
fprintf (f,
"%s" QSTAT_DELIM_STR
"%d" QSTAT_DELIM_STR
"%d\n",
(p->name)? p->name : "",
p->frags,
p->ping);
break;
} /* switch (s->type) */
} /* for (list = s->players ... */
} /* if (default_save_plrinfo) */
fputc ('\n', f);
}
// fetch additional arguments when launching server of type 'type' and it's
// game matches 'gamestring'
// returns a newly-allocated array of strings. Use g_strfreev() to free it.
// TODO use struct server instead of char* to be able to match any variable
char **get_custom_arguments(enum server_type type, const char *gamestring) {
struct game *g = &games[type];
char *arg = NULL;
int j;
char conf[15];
char *token[2];
// int n;
char ** ret = NULL;
GSList *temp;
if (!gamestring) return NULL;
temp = g_slist_nth(g->custom_args, 0);
j = 0;
while (temp) {
g_snprintf (conf, 15, "custom_arg%d", j);
arg = g_strdup((char *) temp->data);
// n = tokenize (arg, token, 2, ",");
tokenize (arg, token, 2, ",");
if (!(strcasecmp(token[0], gamestring))) {
ret = g_strsplit(token[1], " ", 0);
debug(1, "found entry for:%s. Returning argument:%s\n", gamestring, token[1]);
g_free(arg);
return ret;
}
temp = g_slist_next(temp);
}
debug(1, "get_custom_arguments: Didn't find an entry for %s", gamestring);
g_free(arg);
return NULL;
}
/** map functions */
static void quake_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_quake_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_quake_maps(pd->maphash, games[type].real_home);
}
}
static void q3_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_q3_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_q3_maps(pd->maphash, games[type].real_home);
}
}
static void unvanquished_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_unvanquished_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_unvanquished_maps(pd->maphash, games[type].real_home);
}
}
static void xonotic_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_xonotic_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_xonotic_maps(pd->maphash, games[type].real_home);
}
}
static void doom3_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_doom3_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_doom3_maps(pd->maphash, games[type].real_home);
}
}
static void quake4_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_quake4_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_quake4_maps(pd->maphash, games[type].real_home);
}
}
static void etqw_init_maps(enum server_type type) {
struct quake_private* pd = NULL;
pd = (struct quake_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
q3_clear_maps(pd->maphash); // important to avoid memleak when called second time
pd->maphash = q3_init_maphash();
find_etqw_maps(pd->maphash, games[type].real_dir);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_etqw_maps(pd->maphash, games[type].real_home);
}
}
static void unreal_init_maps(enum server_type type) {
struct unreal_private* pd = NULL;
pd = (struct unreal_private*)games[type].pd;
g_return_if_fail(pd!=NULL);
ut_clear_maps(pd->maphash);
pd->maphash = ut_init_maphash();
find_ut_maps_dir(pd->maphash, games[type].real_dir, pd->suffix);
if (games[type].real_home && access(games[type].real_home, R_OK) == 0) {
find_ut_maps_dir(pd->maphash, games[type].real_home, pd->suffix);
}
}
static gboolean quake_has_map(struct server* s) {
struct quake_private* pd = NULL;
GHashTable* hash = NULL;
g_return_val_if_fail(s!=NULL, TRUE);
pd = (struct quake_private*)games[s->type].pd;
g_return_val_if_fail(pd!=NULL, TRUE);
hash = (GHashTable*)pd->maphash;
if (!hash) return TRUE;
if (!s->map)
return FALSE;
return q3_lookup_map(hash, s->map);
}
static gboolean doom3_has_map(struct server* s) {
struct quake_private* pd = NULL;
GHashTable* hash = NULL;
g_return_val_if_fail(s!=NULL, TRUE);
pd = (struct quake_private*)games[s->type].pd;
g_return_val_if_fail(pd!=NULL, TRUE);
hash = (GHashTable*)pd->maphash;
if (!hash) return TRUE;
if (!s->map)
return FALSE;
return doom3_lookup_map(hash, s->map);
}
static gboolean quake4_has_map(struct server* s) {
return doom3_has_map(s);
}
static size_t q3_get_mapshot(struct server* s, guchar** buf) {
struct quake_private* pd = NULL;
GHashTable* hash = NULL;
g_return_val_if_fail(s!=NULL, TRUE);
pd = (struct quake_private*)games[s->type].pd;
g_return_val_if_fail(pd!=NULL, TRUE);
hash = (GHashTable*)pd->maphash;
if (!hash) return TRUE;
if (!s->map)
return FALSE;
return q3_lookup_mapshot(hash, s->map, buf);
}
static size_t doom3_get_mapshot(struct server* s, guchar** buf) {
struct quake_private* pd = NULL;
GHashTable* hash = NULL;
g_return_val_if_fail(s!=NULL, TRUE);
pd = (struct quake_private*)games[s->type].pd;
g_return_val_if_fail(pd!=NULL, TRUE);
hash = (GHashTable*)pd->maphash;
if (!hash) return TRUE;
if (!s->map)
return FALSE;
return doom3_lookup_mapshot(hash, s->map, buf);
}
static size_t quake4_get_mapshot(struct server* s, guchar** buf) {
return doom3_get_mapshot(s, buf);
}
static gboolean unreal_has_map(struct server* s) {
struct unreal_private* pd = NULL;
GHashTable* hash = NULL;
g_return_val_if_fail(s!=NULL, TRUE);
pd = (struct unreal_private*)games[s->type].pd;
g_return_val_if_fail(pd!=NULL, TRUE);
hash = (GHashTable*)pd->maphash;
if (!hash) return TRUE;
if (!s->map)
return FALSE;
return ut_lookup_map(hash, s->map);
}