diff options
Diffstat (limited to 'code/q3_ui/ui_servers2.c')
-rw-r--r-- | code/q3_ui/ui_servers2.c | 1643 |
1 files changed, 1643 insertions, 0 deletions
diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c new file mode 100644 index 0000000..6c7cf16 --- /dev/null +++ b/code/q3_ui/ui_servers2.c @@ -0,0 +1,1643 @@ +/* +=========================================================================== +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_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 %3s %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")); + + 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 emptyinfo[MAX_INFO_STRING]; + char adrstr[MAX_ADDRESSLENGTH]; + servernode_t templist[MAX_FAVORITESERVERS]; + qboolean found; + + found = qfalse; + emptyinfo[0] = '\0'; + + // 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(g_servertype)) { + return; + } + } + if (trap_LAN_GetServerCount(g_servertype) < 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(g_servertype); + } + +// 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(g_servertype, 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; + int value; + 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_servertype = Com_Clamp( 0, 3, ui_browserMaster.integer ); + // hack to get rid of MPlayer stuff + value = g_servertype; + if (value >= 1) + value--; + g_arenaservers.master.curvalue = value; + + 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 ); +} |