ioq3/code/q3_ui/ui_servers2.c

1660 lines
45 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
/*
=======================================================================
MULTIPLAYER MENU (SERVER BROWSER)
=======================================================================
*/
#include "ui_local.h"
#define MAX_GLOBALSERVERS 128
#define MAX_PINGREQUESTS 32
#define MAX_ADDRESSLENGTH 64
#define MAX_HOSTNAMELENGTH 22
#define MAX_MAPNAMELENGTH 16
#define MAX_LISTBOXITEMS 128
#define MAX_LOCALSERVERS 128
#define MAX_STATUSLENGTH 64
#define MAX_LEAGUELENGTH 28
#define MAX_LISTBOXWIDTH 68
#define ART_BACK0 "menu/art/back_0"
#define ART_BACK1 "menu/art/back_1"
#define ART_CREATE0 "menu/art/create_0"
#define ART_CREATE1 "menu/art/create_1"
#define ART_SPECIFY0 "menu/art/specify_0"
#define ART_SPECIFY1 "menu/art/specify_1"
#define ART_REFRESH0 "menu/art/refresh_0"
#define ART_REFRESH1 "menu/art/refresh_1"
#define ART_CONNECT0 "menu/art/fight_0"
#define ART_CONNECT1 "menu/art/fight_1"
#define ART_ARROWS0 "menu/art/arrows_vert_0"
#define ART_ARROWS_UP "menu/art/arrows_vert_top"
#define ART_ARROWS_DOWN "menu/art/arrows_vert_bot"
#define ART_UNKNOWNMAP "menu/art/unknownmap"
#define ART_REMOVE0 "menu/art/delete_0"
#define ART_REMOVE1 "menu/art/delete_1"
#define ART_PUNKBUSTER "menu/art/pblogo"
#define ID_MASTER 10
#define ID_GAMETYPE 11
#define ID_SORTKEY 12
#define ID_SHOW_FULL 13
#define ID_SHOW_EMPTY 14
#define ID_LIST 15
#define ID_SCROLL_UP 16
#define ID_SCROLL_DOWN 17
#define ID_BACK 18
#define ID_REFRESH 19
#define ID_SPECIFY 20
#define ID_CREATE 21
#define ID_CONNECT 22
#define ID_REMOVE 23
#define ID_PUNKBUSTER 24
#define GR_LOGO 30
#define GR_LETTERS 31
#define UIAS_LOCAL 0
#define UIAS_GLOBAL1 1
#define UIAS_GLOBAL2 2
#define UIAS_GLOBAL3 3
#define UIAS_GLOBAL4 4
#define UIAS_GLOBAL5 5
#define UIAS_FAVORITES 6
#define SORT_HOST 0
#define SORT_MAP 1
#define SORT_CLIENTS 2
#define SORT_GAME 3
#define SORT_PING 4
#define GAMES_ALL 0
#define GAMES_FFA 1
#define GAMES_TEAMPLAY 2
#define GAMES_TOURNEY 3
#define GAMES_CTF 4
static const char *master_items[] = {
"Local",
"Internet1",
"Internet2",
"Internet3",
"Internet4",
"Internet5",
"Favorites",
NULL
};
static const char *servertype_items[] = {
"All",
"Free For All",
"Team Deathmatch",
"Tournament",
"Capture the Flag",
NULL
};
static const char *sortkey_items[] = {
"Server Name",
"Map Name",
"Open Player Spots",
"Game Type",
"Ping Time",
NULL
};
static char* gamenames[] = {
"DM ", // deathmatch
"1v1", // tournament
"SP ", // single player
"Team DM", // team deathmatch
"CTF", // capture the flag
"One Flag CTF", // one flag ctf
"OverLoad", // Overload
"Harvester", // Harvester
"Rocket Arena 3", // Rocket Arena 3
"Q3F", // Q3F
"Urban Terror", // Urban Terror
"OSP", // Orange Smoothie Productions
"???", // unknown
NULL
};
static char* netnames[] = {
"??? ",
"UDP ",
"UDP6",
NULL
};
static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
const char* punkbuster_items[] = {
"Disabled",
"Enabled",
NULL
};
const char* punkbuster_msg[] = {
"PunkBuster will be",
"disabled the next time",
"Quake III Arena",
"is started.",
NULL
};
typedef struct {
char adrstr[MAX_ADDRESSLENGTH];
int start;
} pinglist_t;
typedef struct servernode_s {
char adrstr[MAX_ADDRESSLENGTH];
char hostname[MAX_HOSTNAMELENGTH+3];
char mapname[MAX_MAPNAMELENGTH];
int numclients;
int maxclients;
int pingtime;
int gametype;
char gamename[12];
int nettype;
int minPing;
int maxPing;
qboolean bPB;
} servernode_t;
typedef struct {
char buff[MAX_LISTBOXWIDTH];
servernode_t* servernode;
} table_t;
typedef struct {
menuframework_s menu;
menutext_s banner;
menulist_s master;
menulist_s gametype;
menulist_s sortkey;
menuradiobutton_s showfull;
menuradiobutton_s showempty;
menulist_s list;
menubitmap_s mappic;
menubitmap_s arrows;
menubitmap_s up;
menubitmap_s down;
menutext_s status;
menutext_s statusbar;
menubitmap_s remove;
menubitmap_s back;
menubitmap_s refresh;
menubitmap_s specify;
menubitmap_s create;
menubitmap_s go;
pinglist_t pinglist[MAX_PINGREQUESTS];
table_t table[MAX_LISTBOXITEMS];
char* items[MAX_LISTBOXITEMS];
int numqueriedservers;
int *numservers;
servernode_t *serverlist;
int currentping;
qboolean refreshservers;
int nextpingtime;
int maxservers;
int refreshtime;
char favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
int numfavoriteaddresses;
menulist_s punkbuster;
menubitmap_s pblogo;
} arenaservers_t;
static arenaservers_t g_arenaservers;
static servernode_t g_globalserverlist[MAX_GLOBALSERVERS];
static int g_numglobalservers;
static servernode_t g_localserverlist[MAX_LOCALSERVERS];
static int g_numlocalservers;
static servernode_t g_favoriteserverlist[MAX_FAVORITESERVERS];
static int g_numfavoriteservers;
static int g_servertype;
static int g_gametype;
static int g_sortkey;
static int g_emptyservers;
static int g_fullservers;
/*
=================
ArenaServers_MaxPing
=================
*/
static int ArenaServers_MaxPing( void ) {
int maxPing;
maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
if( maxPing < 100 ) {
maxPing = 100;
}
return maxPing;
}
/*
=================
ArenaServers_Compare
=================
*/
static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
float f1;
float f2;
servernode_t* t1;
servernode_t* t2;
t1 = (servernode_t *)arg1;
t2 = (servernode_t *)arg2;
switch( g_sortkey ) {
case SORT_HOST:
return Q_stricmp( t1->hostname, t2->hostname );
case SORT_MAP:
return Q_stricmp( t1->mapname, t2->mapname );
case SORT_CLIENTS:
f1 = t1->maxclients - t1->numclients;
if( f1 < 0 ) {
f1 = 0;
}
f2 = t2->maxclients - t2->numclients;
if( f2 < 0 ) {
f2 = 0;
}
if( f1 < f2 ) {
return 1;
}
if( f1 == f2 ) {
return 0;
}
return -1;
case SORT_GAME:
if( t1->gametype < t2->gametype ) {
return -1;
}
if( t1->gametype == t2->gametype ) {
return 0;
}
return 1;
case SORT_PING:
if( t1->pingtime < t2->pingtime ) {
return -1;
}
if( t1->pingtime > t2->pingtime ) {
return 1;
}
return Q_stricmp( t1->hostname, t2->hostname );
}
return 0;
}
/*
=================
ArenaServers_SourceForLAN
Convert ui's g_servertype to AS_* used by trap calls.
=================
*/
int ArenaServers_SourceForLAN(void) {
switch( g_servertype ) {
default:
case UIAS_LOCAL:
return AS_LOCAL;
case UIAS_GLOBAL1:
case UIAS_GLOBAL2:
case UIAS_GLOBAL3:
case UIAS_GLOBAL4:
case UIAS_GLOBAL5:
return AS_GLOBAL;
case UIAS_FAVORITES:
return AS_FAVORITES;
}
}
/*
=================
ArenaServers_Go
=================
*/
static void ArenaServers_Go( void ) {
servernode_t* servernode;
servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
if( servernode ) {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
}
}
/*
=================
ArenaServers_UpdatePicture
=================
*/
static void ArenaServers_UpdatePicture( void ) {
static char picname[64];
servernode_t* servernodeptr;
if( !g_arenaservers.list.numitems ) {
g_arenaservers.mappic.generic.name = NULL;
}
else {
servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
g_arenaservers.mappic.generic.name = picname;
}
// force shader update during draw
g_arenaservers.mappic.shader = 0;
}
/*
=================
ArenaServers_UpdateMenu
=================
*/
static void ArenaServers_UpdateMenu( void ) {
int i;
int j;
int count;
char* buff;
servernode_t* servernodeptr;
table_t* tableptr;
char *pingColor;
if( g_arenaservers.numqueriedservers > 0 ) {
// servers found
if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
// show progress
Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
g_arenaservers.statusbar.string = "Press SPACE to stop";
qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
}
else {
// all servers pinged - enable controls
g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
g_arenaservers.list.generic.flags &= ~QMF_GRAYED;
g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
g_arenaservers.go.generic.flags &= ~QMF_GRAYED;
g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
// update status bar
if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
g_arenaservers.statusbar.string = quake3worldMessage;
}
else {
g_arenaservers.statusbar.string = "";
}
}
}
else {
// no servers found
if( g_arenaservers.refreshservers ) {
strcpy( g_arenaservers.status.string,"Scanning For Servers." );
g_arenaservers.statusbar.string = "Press SPACE to stop";
// disable controls during refresh
g_arenaservers.master.generic.flags |= QMF_GRAYED;
g_arenaservers.gametype.generic.flags |= QMF_GRAYED;
g_arenaservers.sortkey.generic.flags |= QMF_GRAYED;
g_arenaservers.showempty.generic.flags |= QMF_GRAYED;
g_arenaservers.showfull.generic.flags |= QMF_GRAYED;
g_arenaservers.list.generic.flags |= QMF_GRAYED;
g_arenaservers.refresh.generic.flags |= QMF_GRAYED;
g_arenaservers.go.generic.flags |= QMF_GRAYED;
g_arenaservers.punkbuster.generic.flags |= QMF_GRAYED;
}
else {
if( g_arenaservers.numqueriedservers < 0 ) {
strcpy(g_arenaservers.status.string,"No Response From Master Server." );
}
else {
strcpy(g_arenaservers.status.string,"No Servers Found." );
}
// update status bar
if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
g_arenaservers.statusbar.string = quake3worldMessage;
}
else {
g_arenaservers.statusbar.string = "";
}
// end of refresh - set control state
g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
g_arenaservers.list.generic.flags |= QMF_GRAYED;
g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
g_arenaservers.go.generic.flags |= QMF_GRAYED;
g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
}
// zero out list box
g_arenaservers.list.numitems = 0;
g_arenaservers.list.curvalue = 0;
g_arenaservers.list.top = 0;
// update picture
ArenaServers_UpdatePicture();
return;
}
// build list box strings - apply culling filters
servernodeptr = g_arenaservers.serverlist;
count = *g_arenaservers.numservers;
for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
tableptr = &g_arenaservers.table[j];
tableptr->servernode = servernodeptr;
buff = tableptr->buff;
// can only cull valid results
if( !g_emptyservers && !servernodeptr->numclients ) {
continue;
}
if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
continue;
}
switch( g_gametype ) {
case GAMES_ALL:
break;
case GAMES_FFA:
if( servernodeptr->gametype != GT_FFA ) {
continue;
}
break;
case GAMES_TEAMPLAY:
if( servernodeptr->gametype != GT_TEAM ) {
continue;
}
break;
case GAMES_TOURNEY:
if( servernodeptr->gametype != GT_TOURNAMENT ) {
continue;
}
break;
case GAMES_CTF:
if( servernodeptr->gametype != GT_CTF ) {
continue;
}
break;
}
if( servernodeptr->pingtime < servernodeptr->minPing ) {
pingColor = S_COLOR_BLUE;
}
else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
pingColor = S_COLOR_BLUE;
}
else if( servernodeptr->pingtime < 200 ) {
pingColor = S_COLOR_GREEN;
}
else if( servernodeptr->pingtime < 400 ) {
pingColor = S_COLOR_YELLOW;
}
else {
pingColor = S_COLOR_RED;
}
Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d " S_COLOR_YELLOW "%s",
servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
servernodeptr->maxclients, servernodeptr->gamename,
netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
j++;
}
g_arenaservers.list.numitems = j;
g_arenaservers.list.curvalue = 0;
g_arenaservers.list.top = 0;
// update picture
ArenaServers_UpdatePicture();
}
/*
=================
ArenaServers_Remove
=================
*/
static void ArenaServers_Remove( void )
{
int i;
servernode_t* servernodeptr;
table_t* tableptr;
if (!g_arenaservers.list.numitems)
return;
// remove selected item from display list
// items are in scattered order due to sort and cull
// perform delete on list box contents, resync all lists
tableptr = &g_arenaservers.table[g_arenaservers.list.curvalue];
servernodeptr = tableptr->servernode;
// find address in master list
for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
{
if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
{
// delete address from master list
if (i < g_arenaservers.numfavoriteaddresses-1)
{
// shift items up
memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)* MAX_ADDRESSLENGTH );
}
g_arenaservers.numfavoriteaddresses--;
memset( &g_arenaservers.favoriteaddresses[g_arenaservers.numfavoriteaddresses], 0, MAX_ADDRESSLENGTH );
break;
}
}
// find address in server list
for (i=0; i<g_numfavoriteservers; i++)
{
if (&g_favoriteserverlist[i] == servernodeptr)
{
// delete address from server list
if (i < g_numfavoriteservers-1)
{
// shift items up
memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
}
g_numfavoriteservers--;
memset( &g_favoriteserverlist[ g_numfavoriteservers ], 0, sizeof(servernode_t));
break;
}
}
g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
g_arenaservers.currentping = g_arenaservers.numfavoriteaddresses;
}
/*
=================
ArenaServers_Insert
=================
*/
static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
{
servernode_t* servernodeptr;
char* s;
int i;
if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != UIAS_FAVORITES))
{
// slow global or local servers do not get entered
return;
}
if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
// list full;
servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
} else {
// next slot
servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
(*g_arenaservers.numservers)++;
}
Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
Q_CleanStr( servernodeptr->hostname );
Q_strupr( servernodeptr->hostname );
Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
Q_CleanStr( servernodeptr->mapname );
Q_strupr( servernodeptr->mapname );
servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
servernodeptr->pingtime = pingtime;
servernodeptr->minPing = atoi( Info_ValueForKey( info, "minPing") );
servernodeptr->maxPing = atoi( Info_ValueForKey( info, "maxPing") );
servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
/*
s = Info_ValueForKey( info, "nettype" );
for (i=0; ;i++)
{
if (!netnames[i])
{
servernodeptr->nettype = 0;
break;
}
else if (!Q_stricmp( netnames[i], s ))
{
servernodeptr->nettype = i;
break;
}
}
*/
servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
if (servernodeptr->nettype < 0 || servernodeptr->nettype >= ARRAY_LEN(netnames) - 1) {
servernodeptr->nettype = 0;
}
s = Info_ValueForKey( info, "game");
i = atoi( Info_ValueForKey( info, "gametype") );
if( i < 0 ) {
i = 0;
}
else if( i > 11 ) {
i = 12;
}
if( *s ) {
servernodeptr->gametype = i;//-1;
Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
}
else {
servernodeptr->gametype = i;
Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
}
}
/*
=================
ArenaServers_InsertFavorites
Insert nonresponsive address book entries into display lists.
=================
*/
void ArenaServers_InsertFavorites( void )
{
int i;
int j;
char info[MAX_INFO_STRING];
// resync existing results with new or deleted cvars
info[0] = '\0';
Info_SetValueForKey( info, "hostname", "No Response" );
for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
{
// find favorite address in refresh list
for (j=0; j<g_numfavoriteservers; j++)
if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
break;
if ( j >= g_numfavoriteservers)
{
// not in list, add it
ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
}
}
}
/*
=================
ArenaServers_LoadFavorites
Load cvar address book entries into local lists.
=================
*/
void ArenaServers_LoadFavorites( void )
{
int i;
int j;
int numtempitems;
char adrstr[MAX_ADDRESSLENGTH];
servernode_t templist[MAX_FAVORITESERVERS];
qboolean found;
found = qfalse;
// copy the old
memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
numtempitems = g_numfavoriteservers;
// clear the current for sync
memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
g_numfavoriteservers = 0;
// resync existing results with new or deleted cvars
for (i=0; i<MAX_FAVORITESERVERS; i++)
{
trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
if (!adrstr[0])
continue;
// quick sanity check to avoid slow domain name resolving
// first character must be numeric
if (adrstr[0] < '0' || adrstr[0] > '9')
continue;
// favorite server addresses must be maintained outside refresh list
// this mimics local and global netadr's stored in client
// these can be fetched to fill ping list
strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );
// find this server in the old list
for (j=0; j<numtempitems; j++)
if (!Q_stricmp( templist[j].adrstr, adrstr ))
break;
if (j < numtempitems)
{
// found server - add exisiting results
memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
found = qtrue;
}
else
{
// add new server
Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
}
g_numfavoriteservers++;
}
g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
if (!found)
{
// no results were found, reset server list
// list will be automatically refreshed when selected
g_numfavoriteservers = 0;
}
}
/*
=================
ArenaServers_StopRefresh
=================
*/
static void ArenaServers_StopRefresh( void )
{
if (!g_arenaservers.refreshservers)
// not currently refreshing
return;
g_arenaservers.refreshservers = qfalse;
if (g_servertype == UIAS_FAVORITES)
{
// nonresponsive favorites must be shown
ArenaServers_InsertFavorites();
}
// final tally
if (g_arenaservers.numqueriedservers >= 0)
{
g_arenaservers.currentping = *g_arenaservers.numservers;
g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
}
// sort
qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
ArenaServers_UpdateMenu();
}
/*
=================
ArenaServers_DoRefresh
=================
*/
static void ArenaServers_DoRefresh( void )
{
int i;
int j;
int time;
int maxPing;
char adrstr[MAX_ADDRESSLENGTH];
char info[MAX_INFO_STRING];
if (uis.realtime < g_arenaservers.refreshtime)
{
if (g_servertype != UIAS_FAVORITES) {
if (g_servertype == UIAS_LOCAL) {
if (!trap_LAN_GetServerCount(AS_LOCAL)) {
return;
}
}
if (trap_LAN_GetServerCount(ArenaServers_SourceForLAN()) < 0) {
// still waiting for response
return;
}
}
}
if (uis.realtime < g_arenaservers.nextpingtime)
{
// wait for time trigger
return;
}
// trigger at 10Hz intervals
g_arenaservers.nextpingtime = uis.realtime + 10;
// process ping results
maxPing = ArenaServers_MaxPing();
for (i=0; i<MAX_PINGREQUESTS; i++)
{
trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
if (!adrstr[0])
{
// ignore empty or pending pings
continue;
}
// find ping result in our local list
for (j=0; j<MAX_PINGREQUESTS; j++)
if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
break;
if (j < MAX_PINGREQUESTS)
{
// found it
if (!time)
{
time = uis.realtime - g_arenaservers.pinglist[j].start;
if (time < maxPing)
{
// still waiting
continue;
}
}
if (time > maxPing)
{
// stale it out
info[0] = '\0';
time = maxPing;
}
else
{
trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
}
// insert ping results
ArenaServers_Insert( adrstr, info, time );
// clear this query from internal list
g_arenaservers.pinglist[j].adrstr[0] = '\0';
}
// clear this query from external list
trap_LAN_ClearPing( i );
}
// get results of servers query
// counts can increase as servers respond
if (g_servertype == UIAS_FAVORITES) {
g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
} else {
g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(ArenaServers_SourceForLAN());
}
// if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
// g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
// send ping requests in reasonable bursts
// iterate ping through all found servers
for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
{
if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
{
// ping queue is full
break;
}
// find empty slot
for (j=0; j<MAX_PINGREQUESTS; j++)
if (!g_arenaservers.pinglist[j].adrstr[0])
break;
if (j >= MAX_PINGREQUESTS)
// no empty slots available yet - wait for timeout
break;
// get an address to ping
if (g_servertype == UIAS_FAVORITES) {
strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );
} else {
trap_LAN_GetServerAddressString(ArenaServers_SourceForLAN(), g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
}
strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
g_arenaservers.pinglist[j].start = uis.realtime;
trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr ) );
// advance to next server
g_arenaservers.currentping++;
}
if (!trap_LAN_GetPingQueueCount())
{
// all pings completed
ArenaServers_StopRefresh();
return;
}
// update the user interface with ping status
ArenaServers_UpdateMenu();
}
/*
=================
ArenaServers_StartRefresh
=================
*/
static void ArenaServers_StartRefresh( void )
{
int i;
char myargs[32], protocol[32];
memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
for (i=0; i<MAX_PINGREQUESTS; i++)
{
g_arenaservers.pinglist[i].adrstr[0] = '\0';
trap_LAN_ClearPing( i );
}
g_arenaservers.refreshservers = qtrue;
g_arenaservers.currentping = 0;
g_arenaservers.nextpingtime = 0;
*g_arenaservers.numservers = 0;
g_arenaservers.numqueriedservers = 0;
// allow max 5 seconds for responses
g_arenaservers.refreshtime = uis.realtime + 5000;
// place menu in zeroed state
ArenaServers_UpdateMenu();
if( g_servertype == UIAS_LOCAL ) {
trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
return;
}
if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
switch( g_arenaservers.gametype.curvalue ) {
default:
case GAMES_ALL:
myargs[0] = 0;
break;
case GAMES_FFA:
strcpy( myargs, " ffa" );
break;
case GAMES_TEAMPLAY:
strcpy( myargs, " team" );
break;
case GAMES_TOURNEY:
strcpy( myargs, " tourney" );
break;
case GAMES_CTF:
strcpy( myargs, " ctf" );
break;
}
if (g_emptyservers) {
strcat(myargs, " empty");
}
if (g_fullservers) {
strcat(myargs, " full");
}
protocol[0] = '\0';
trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) );
if (strlen(protocol)) {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - 1, protocol, myargs ));
}
else {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - 1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
}
}
}
/*
=================
ArenaServers_SaveChanges
=================
*/
void ArenaServers_SaveChanges( void )
{
int i;
for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );
for (; i<MAX_FAVORITESERVERS; i++)
trap_Cvar_Set( va("server%d",i+1), "" );
}
/*
=================
ArenaServers_Sort
=================
*/
void ArenaServers_Sort( int type ) {
if( g_sortkey == type ) {
return;
}
g_sortkey = type;
qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
}
/*
=================
ArenaServers_SetType
=================
*/
int ArenaServers_SetType( int type )
{
if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
{
char masterstr[2], cvarname[sizeof("sv_master1")];
while(type <= UIAS_GLOBAL5)
{
Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type);
trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr));
if(*masterstr)
break;
type++;
}
}
g_servertype = type;
switch( type ) {
default:
case UIAS_LOCAL:
g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
g_arenaservers.serverlist = g_localserverlist;
g_arenaservers.numservers = &g_numlocalservers;
g_arenaservers.maxservers = MAX_LOCALSERVERS;
break;
case UIAS_GLOBAL1:
case UIAS_GLOBAL2:
case UIAS_GLOBAL3:
case UIAS_GLOBAL4:
case UIAS_GLOBAL5:
g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
g_arenaservers.serverlist = g_globalserverlist;
g_arenaservers.numservers = &g_numglobalservers;
g_arenaservers.maxservers = MAX_GLOBALSERVERS;
break;
case UIAS_FAVORITES:
g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
g_arenaservers.serverlist = g_favoriteserverlist;
g_arenaservers.numservers = &g_numfavoriteservers;
g_arenaservers.maxservers = MAX_FAVORITESERVERS;
break;
}
if( !*g_arenaservers.numservers ) {
ArenaServers_StartRefresh();
}
else {
// avoid slow operation, use existing results
g_arenaservers.currentping = *g_arenaservers.numservers;
g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
ArenaServers_UpdateMenu();
strcpy(g_arenaservers.status.string,"hit refresh to update");
}
return type;
}
/*
=================
PunkBuster_Confirm
=================
*/
static void Punkbuster_ConfirmEnable( qboolean result ) {
if (result)
{
trap_SetPbClStatus(1);
}
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
}
static void Punkbuster_ConfirmDisable( qboolean result ) {
if (result)
{
trap_SetPbClStatus(0);
UI_Message( punkbuster_msg );
}
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
}
/*
=================
ArenaServers_Event
=================
*/
static void ArenaServers_Event( void* ptr, int event ) {
int id;
id = ((menucommon_s*)ptr)->id;
if( event != QM_ACTIVATED && id != ID_LIST ) {
return;
}
switch( id ) {
case ID_MASTER:
g_arenaservers.master.curvalue = ArenaServers_SetType(g_arenaservers.master.curvalue);
trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue);
break;
case ID_GAMETYPE:
trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
g_gametype = g_arenaservers.gametype.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_SORTKEY:
trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
ArenaServers_UpdateMenu();
break;
case ID_SHOW_FULL:
trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
g_fullservers = g_arenaservers.showfull.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_SHOW_EMPTY:
trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
g_emptyservers = g_arenaservers.showempty.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_LIST:
if( event == QM_GOTFOCUS ) {
ArenaServers_UpdatePicture();
}
break;
case ID_SCROLL_UP:
ScrollList_Key( &g_arenaservers.list, K_UPARROW );
break;
case ID_SCROLL_DOWN:
ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
break;
case ID_BACK:
ArenaServers_StopRefresh();
ArenaServers_SaveChanges();
UI_PopMenu();
break;
case ID_REFRESH:
ArenaServers_StartRefresh();
break;
case ID_SPECIFY:
UI_SpecifyServerMenu();
break;
case ID_CREATE:
UI_StartServerMenu( qtrue );
break;
case ID_CONNECT:
ArenaServers_Go();
break;
case ID_REMOVE:
ArenaServers_Remove();
ArenaServers_UpdateMenu();
break;
case ID_PUNKBUSTER:
if (g_arenaservers.punkbuster.curvalue)
{
UI_ConfirmMenu_Style( "Enable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmEnable );
}
else
{
UI_ConfirmMenu_Style( "Disable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmDisable );
}
break;
}
}
/*
=================
ArenaServers_MenuDraw
=================
*/
static void ArenaServers_MenuDraw( void )
{
if (g_arenaservers.refreshservers)
ArenaServers_DoRefresh();
Menu_Draw( &g_arenaservers.menu );
}
/*
=================
ArenaServers_MenuKey
=================
*/
static sfxHandle_t ArenaServers_MenuKey( int key ) {
if( key == K_SPACE && g_arenaservers.refreshservers ) {
ArenaServers_StopRefresh();
return menu_move_sound;
}
if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == UIAS_FAVORITES ) &&
( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
ArenaServers_Remove();
ArenaServers_UpdateMenu();
return menu_move_sound;
}
if( key == K_MOUSE2 || key == K_ESCAPE ) {
ArenaServers_StopRefresh();
ArenaServers_SaveChanges();
}
return Menu_DefaultKey( &g_arenaservers.menu, key );
}
/*
=================
ArenaServers_MenuInit
=================
*/
static void ArenaServers_MenuInit( void ) {
int i;
int y;
static char statusbuffer[MAX_STATUSLENGTH];
// zero set all our globals
memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
ArenaServers_Cache();
g_arenaservers.menu.fullscreen = qtrue;
g_arenaservers.menu.wrapAround = qtrue;
g_arenaservers.menu.draw = ArenaServers_MenuDraw;
g_arenaservers.menu.key = ArenaServers_MenuKey;
g_arenaservers.banner.generic.type = MTYPE_BTEXT;
g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
g_arenaservers.banner.generic.x = 320;
g_arenaservers.banner.generic.y = 16;
g_arenaservers.banner.string = "ARENA SERVERS";
g_arenaservers.banner.style = UI_CENTER;
g_arenaservers.banner.color = color_white;
y = 80;
g_arenaservers.master.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.master.generic.name = "Servers:";
g_arenaservers.master.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.master.generic.callback = ArenaServers_Event;
g_arenaservers.master.generic.id = ID_MASTER;
g_arenaservers.master.generic.x = 320;
g_arenaservers.master.generic.y = y;
g_arenaservers.master.itemnames = master_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.gametype.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.gametype.generic.name = "Game Type:";
g_arenaservers.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.gametype.generic.callback = ArenaServers_Event;
g_arenaservers.gametype.generic.id = ID_GAMETYPE;
g_arenaservers.gametype.generic.x = 320;
g_arenaservers.gametype.generic.y = y;
g_arenaservers.gametype.itemnames = servertype_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.sortkey.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.sortkey.generic.name = "Sort By:";
g_arenaservers.sortkey.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.sortkey.generic.callback = ArenaServers_Event;
g_arenaservers.sortkey.generic.id = ID_SORTKEY;
g_arenaservers.sortkey.generic.x = 320;
g_arenaservers.sortkey.generic.y = y;
g_arenaservers.sortkey.itemnames = sortkey_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.showfull.generic.type = MTYPE_RADIOBUTTON;
g_arenaservers.showfull.generic.name = "Show Full:";
g_arenaservers.showfull.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.showfull.generic.callback = ArenaServers_Event;
g_arenaservers.showfull.generic.id = ID_SHOW_FULL;
g_arenaservers.showfull.generic.x = 320;
g_arenaservers.showfull.generic.y = y;
y += SMALLCHAR_HEIGHT;
g_arenaservers.showempty.generic.type = MTYPE_RADIOBUTTON;
g_arenaservers.showempty.generic.name = "Show Empty:";
g_arenaservers.showempty.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.showempty.generic.callback = ArenaServers_Event;
g_arenaservers.showempty.generic.id = ID_SHOW_EMPTY;
g_arenaservers.showempty.generic.x = 320;
g_arenaservers.showempty.generic.y = y;
y += 3 * SMALLCHAR_HEIGHT;
g_arenaservers.list.generic.type = MTYPE_SCROLLLIST;
g_arenaservers.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;
g_arenaservers.list.generic.id = ID_LIST;
g_arenaservers.list.generic.callback = ArenaServers_Event;
g_arenaservers.list.generic.x = 72;
g_arenaservers.list.generic.y = y;
g_arenaservers.list.width = MAX_LISTBOXWIDTH;
g_arenaservers.list.height = 11;
g_arenaservers.list.itemnames = (const char **)g_arenaservers.items;
for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
g_arenaservers.items[i] = g_arenaservers.table[i].buff;
}
g_arenaservers.mappic.generic.type = MTYPE_BITMAP;
g_arenaservers.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.mappic.generic.x = 72;
g_arenaservers.mappic.generic.y = 80;
g_arenaservers.mappic.width = 128;
g_arenaservers.mappic.height = 96;
g_arenaservers.mappic.errorpic = ART_UNKNOWNMAP;
g_arenaservers.arrows.generic.type = MTYPE_BITMAP;
g_arenaservers.arrows.generic.name = ART_ARROWS0;
g_arenaservers.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.arrows.generic.callback = ArenaServers_Event;
g_arenaservers.arrows.generic.x = 512+48;
g_arenaservers.arrows.generic.y = 240-64+16;
g_arenaservers.arrows.width = 64;
g_arenaservers.arrows.height = 128;
g_arenaservers.up.generic.type = MTYPE_BITMAP;
g_arenaservers.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
g_arenaservers.up.generic.callback = ArenaServers_Event;
g_arenaservers.up.generic.id = ID_SCROLL_UP;
g_arenaservers.up.generic.x = 512+48;
g_arenaservers.up.generic.y = 240-64+16;
g_arenaservers.up.width = 64;
g_arenaservers.up.height = 64;
g_arenaservers.up.focuspic = ART_ARROWS_UP;
g_arenaservers.down.generic.type = MTYPE_BITMAP;
g_arenaservers.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
g_arenaservers.down.generic.callback = ArenaServers_Event;
g_arenaservers.down.generic.id = ID_SCROLL_DOWN;
g_arenaservers.down.generic.x = 512+48;
g_arenaservers.down.generic.y = 240+16;
g_arenaservers.down.width = 64;
g_arenaservers.down.height = 64;
g_arenaservers.down.focuspic = ART_ARROWS_DOWN;
y = 376;
g_arenaservers.status.generic.type = MTYPE_TEXT;
g_arenaservers.status.generic.x = 320;
g_arenaservers.status.generic.y = y;
g_arenaservers.status.string = statusbuffer;
g_arenaservers.status.style = UI_CENTER|UI_SMALLFONT;
g_arenaservers.status.color = menu_text_color;
y += SMALLCHAR_HEIGHT;
g_arenaservers.statusbar.generic.type = MTYPE_TEXT;
g_arenaservers.statusbar.generic.x = 320;
g_arenaservers.statusbar.generic.y = y;
g_arenaservers.statusbar.string = "";
g_arenaservers.statusbar.style = UI_CENTER|UI_SMALLFONT;
g_arenaservers.statusbar.color = text_color_normal;
g_arenaservers.remove.generic.type = MTYPE_BITMAP;
g_arenaservers.remove.generic.name = ART_REMOVE0;
g_arenaservers.remove.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.remove.generic.callback = ArenaServers_Event;
g_arenaservers.remove.generic.id = ID_REMOVE;
g_arenaservers.remove.generic.x = 450;
g_arenaservers.remove.generic.y = 86;
g_arenaservers.remove.width = 96;
g_arenaservers.remove.height = 48;
g_arenaservers.remove.focuspic = ART_REMOVE1;
g_arenaservers.back.generic.type = MTYPE_BITMAP;
g_arenaservers.back.generic.name = ART_BACK0;
g_arenaservers.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.back.generic.callback = ArenaServers_Event;
g_arenaservers.back.generic.id = ID_BACK;
g_arenaservers.back.generic.x = 0;
g_arenaservers.back.generic.y = 480-64;
g_arenaservers.back.width = 128;
g_arenaservers.back.height = 64;
g_arenaservers.back.focuspic = ART_BACK1;
g_arenaservers.specify.generic.type = MTYPE_BITMAP;
g_arenaservers.specify.generic.name = ART_SPECIFY0;
g_arenaservers.specify.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.specify.generic.callback = ArenaServers_Event;
g_arenaservers.specify.generic.id = ID_SPECIFY;
g_arenaservers.specify.generic.x = 128;
g_arenaservers.specify.generic.y = 480-64;
g_arenaservers.specify.width = 128;
g_arenaservers.specify.height = 64;
g_arenaservers.specify.focuspic = ART_SPECIFY1;
g_arenaservers.refresh.generic.type = MTYPE_BITMAP;
g_arenaservers.refresh.generic.name = ART_REFRESH0;
g_arenaservers.refresh.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.refresh.generic.callback = ArenaServers_Event;
g_arenaservers.refresh.generic.id = ID_REFRESH;
g_arenaservers.refresh.generic.x = 256;
g_arenaservers.refresh.generic.y = 480-64;
g_arenaservers.refresh.width = 128;
g_arenaservers.refresh.height = 64;
g_arenaservers.refresh.focuspic = ART_REFRESH1;
g_arenaservers.create.generic.type = MTYPE_BITMAP;
g_arenaservers.create.generic.name = ART_CREATE0;
g_arenaservers.create.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.create.generic.callback = ArenaServers_Event;
g_arenaservers.create.generic.id = ID_CREATE;
g_arenaservers.create.generic.x = 384;
g_arenaservers.create.generic.y = 480-64;
g_arenaservers.create.width = 128;
g_arenaservers.create.height = 64;
g_arenaservers.create.focuspic = ART_CREATE1;
g_arenaservers.go.generic.type = MTYPE_BITMAP;
g_arenaservers.go.generic.name = ART_CONNECT0;
g_arenaservers.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.go.generic.callback = ArenaServers_Event;
g_arenaservers.go.generic.id = ID_CONNECT;
g_arenaservers.go.generic.x = 640;
g_arenaservers.go.generic.y = 480-64;
g_arenaservers.go.width = 128;
g_arenaservers.go.height = 64;
g_arenaservers.go.focuspic = ART_CONNECT1;
g_arenaservers.punkbuster.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.punkbuster.generic.name = "Punkbuster:";
g_arenaservers.punkbuster.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.punkbuster.generic.callback = ArenaServers_Event;
g_arenaservers.punkbuster.generic.id = ID_PUNKBUSTER;
g_arenaservers.punkbuster.generic.x = 480+32;
g_arenaservers.punkbuster.generic.y = 144;
g_arenaservers.punkbuster.itemnames = punkbuster_items;
g_arenaservers.pblogo.generic.type = MTYPE_BITMAP;
g_arenaservers.pblogo.generic.name = ART_PUNKBUSTER;
g_arenaservers.pblogo.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.pblogo.generic.x = 526;
g_arenaservers.pblogo.generic.y = 176;
g_arenaservers.pblogo.width = 32;
g_arenaservers.pblogo.height = 16;
g_arenaservers.pblogo.errorpic = ART_UNKNOWNMAP;
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
ArenaServers_LoadFavorites();
g_arenaservers.master.curvalue = g_servertype = Com_Clamp( 0, 6, ui_browserMaster.integer );
g_gametype = Com_Clamp( 0, 4, ui_browserGameType.integer );
g_arenaservers.gametype.curvalue = g_gametype;
g_sortkey = Com_Clamp( 0, 4, ui_browserSortKey.integer );
g_arenaservers.sortkey.curvalue = g_sortkey;
g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
g_arenaservers.showfull.curvalue = g_fullservers;
g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
g_arenaservers.showempty.curvalue = g_emptyservers;
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
// force to initial state and refresh
g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype);
trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
}
/*
=================
ArenaServers_Cache
=================
*/
void ArenaServers_Cache( void ) {
trap_R_RegisterShaderNoMip( ART_BACK0 );
trap_R_RegisterShaderNoMip( ART_BACK1 );
trap_R_RegisterShaderNoMip( ART_CREATE0 );
trap_R_RegisterShaderNoMip( ART_CREATE1 );
trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
trap_R_RegisterShaderNoMip( ART_REFRESH0 );
trap_R_RegisterShaderNoMip( ART_REFRESH1 );
trap_R_RegisterShaderNoMip( ART_CONNECT0 );
trap_R_RegisterShaderNoMip( ART_CONNECT1 );
trap_R_RegisterShaderNoMip( ART_ARROWS0 );
trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
}
/*
=================
UI_ArenaServersMenu
=================
*/
void UI_ArenaServersMenu( void ) {
ArenaServers_MenuInit();
UI_PushMenu( &g_arenaservers.menu );
}