Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ui_servers2.c (44986B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 /*
     24 =======================================================================
     25 
     26 MULTIPLAYER MENU (SERVER BROWSER)
     27 
     28 =======================================================================
     29 */
     30 
     31 
     32 #include "ui_local.h"
     33 
     34 
     35 #define MAX_GLOBALSERVERS		128
     36 #define MAX_PINGREQUESTS		32
     37 #define MAX_ADDRESSLENGTH		64
     38 #define MAX_HOSTNAMELENGTH		22
     39 #define MAX_MAPNAMELENGTH		16
     40 #define MAX_LISTBOXITEMS		128
     41 #define MAX_LOCALSERVERS		128
     42 #define MAX_STATUSLENGTH		64
     43 #define MAX_LEAGUELENGTH		28
     44 #define MAX_LISTBOXWIDTH		68
     45 
     46 #define ART_BACK0				"menu/art/back_0"
     47 #define ART_BACK1				"menu/art/back_1"
     48 #define ART_CREATE0				"menu/art/create_0"
     49 #define ART_CREATE1				"menu/art/create_1"
     50 #define ART_SPECIFY0			"menu/art/specify_0"
     51 #define ART_SPECIFY1			"menu/art/specify_1"
     52 #define ART_REFRESH0			"menu/art/refresh_0"
     53 #define ART_REFRESH1			"menu/art/refresh_1"
     54 #define ART_CONNECT0			"menu/art/fight_0"
     55 #define ART_CONNECT1			"menu/art/fight_1"
     56 #define ART_ARROWS0				"menu/art/arrows_vert_0"
     57 #define ART_ARROWS_UP			"menu/art/arrows_vert_top"
     58 #define ART_ARROWS_DOWN			"menu/art/arrows_vert_bot"
     59 #define ART_UNKNOWNMAP			"menu/art/unknownmap"
     60 #define ART_REMOVE0				"menu/art/delete_0"
     61 #define ART_REMOVE1				"menu/art/delete_1"
     62 #define ART_PUNKBUSTER		"menu/art/pblogo"
     63 
     64 #define ID_MASTER			10
     65 #define ID_GAMETYPE			11
     66 #define ID_SORTKEY			12
     67 #define ID_SHOW_FULL		13
     68 #define ID_SHOW_EMPTY		14
     69 #define ID_LIST				15
     70 #define ID_SCROLL_UP		16
     71 #define ID_SCROLL_DOWN		17
     72 #define ID_BACK				18
     73 #define ID_REFRESH			19
     74 #define ID_SPECIFY			20
     75 #define ID_CREATE			21
     76 #define ID_CONNECT			22
     77 #define ID_REMOVE			23
     78 #define ID_PUNKBUSTER 24
     79 
     80 #define GR_LOGO				30
     81 #define GR_LETTERS			31
     82 
     83 #define AS_LOCAL			0
     84 #define AS_MPLAYER			1
     85 #define AS_GLOBAL			2
     86 #define AS_FAVORITES		3
     87 
     88 #define SORT_HOST			0
     89 #define SORT_MAP			1
     90 #define SORT_CLIENTS		2
     91 #define SORT_GAME			3
     92 #define SORT_PING			4
     93 
     94 #define GAMES_ALL			0
     95 #define GAMES_FFA			1
     96 #define GAMES_TEAMPLAY		2
     97 #define GAMES_TOURNEY		3
     98 #define GAMES_CTF			4
     99 
    100 static const char *master_items[] = {
    101 	"Local",
    102 	"Internet",
    103 	"Favorites",
    104 	0
    105 };
    106 
    107 static const char *servertype_items[] = {
    108 	"All",
    109 	"Free For All",
    110 	"Team Deathmatch",
    111 	"Tournament",
    112 	"Capture the Flag",
    113 	0
    114 };
    115 
    116 static const char *sortkey_items[] = {
    117 	"Server Name",
    118 	"Map Name",
    119 	"Open Player Spots",
    120 	"Game Type",
    121 	"Ping Time",
    122 	0
    123 };
    124 
    125 static char* gamenames[] = {
    126 	"DM ",	// deathmatch
    127 	"1v1",	// tournament
    128 	"SP ",	// single player
    129 	"Team DM",	// team deathmatch
    130 	"CTF",	// capture the flag
    131 	"One Flag CTF",		// one flag ctf
    132 	"OverLoad",				// Overload
    133 	"Harvester",			// Harvester
    134 	"Rocket Arena 3",	// Rocket Arena 3
    135 	"Q3F",						// Q3F
    136 	"Urban Terror",		// Urban Terror
    137 	"OSP",						// Orange Smoothie Productions
    138 	"???",			// unknown
    139 	0
    140 };
    141 
    142 static char* netnames[] = {
    143 	"???",
    144 	"UDP",
    145 	"IPX",
    146 	NULL
    147 };
    148 
    149 static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
    150 
    151 const char* punkbuster_items[] = {
    152 	"Disabled",
    153 	"Enabled",
    154 	NULL
    155 };
    156 
    157 const char* punkbuster_msg[] = {
    158 	"PunkBuster will be",
    159 	"disabled the next time",
    160 	"Quake III Arena",
    161 	"is started.",
    162 	NULL
    163 };
    164 
    165 typedef struct {
    166 	char	adrstr[MAX_ADDRESSLENGTH];
    167 	int		start;
    168 } pinglist_t;
    169 
    170 typedef struct servernode_s {
    171 	char	adrstr[MAX_ADDRESSLENGTH];
    172 	char	hostname[MAX_HOSTNAMELENGTH+3];
    173 	char	mapname[MAX_MAPNAMELENGTH];
    174 	int		numclients;
    175 	int		maxclients;
    176 	int		pingtime;
    177 	int		gametype;
    178 	char	gamename[12];
    179 	int		nettype;
    180 	int		minPing;
    181 	int		maxPing;
    182 	qboolean bPB;
    183 
    184 } servernode_t; 
    185 
    186 typedef struct {
    187 	char			buff[MAX_LISTBOXWIDTH];
    188 	servernode_t*	servernode;
    189 } table_t;
    190 
    191 typedef struct {
    192 	menuframework_s		menu;
    193 
    194 	menutext_s			banner;
    195 
    196 	menulist_s			master;
    197 	menulist_s			gametype;
    198 	menulist_s			sortkey;
    199 	menuradiobutton_s	showfull;
    200 	menuradiobutton_s	showempty;
    201 
    202 	menulist_s			list;
    203 	menubitmap_s		mappic;
    204 	menubitmap_s		arrows;
    205 	menubitmap_s		up;
    206 	menubitmap_s		down;
    207 	menutext_s			status;
    208 	menutext_s			statusbar;
    209 
    210 	menubitmap_s		remove;
    211 	menubitmap_s		back;
    212 	menubitmap_s		refresh;
    213 	menubitmap_s		specify;
    214 	menubitmap_s		create;
    215 	menubitmap_s		go;
    216 
    217 	pinglist_t			pinglist[MAX_PINGREQUESTS];
    218 	table_t				table[MAX_LISTBOXITEMS];
    219 	char*				items[MAX_LISTBOXITEMS];
    220 	int					numqueriedservers;
    221 	int					*numservers;
    222 	servernode_t		*serverlist;	
    223 	int					currentping;
    224 	qboolean			refreshservers;
    225 	int					nextpingtime;
    226 	int					maxservers;
    227 	int					refreshtime;
    228 	char				favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
    229 	int					numfavoriteaddresses;
    230 
    231 	menulist_s		punkbuster;
    232 	menubitmap_s	pblogo;
    233 } arenaservers_t;
    234 
    235 static arenaservers_t	g_arenaservers;
    236 
    237 
    238 static servernode_t		g_globalserverlist[MAX_GLOBALSERVERS];
    239 static int				g_numglobalservers;
    240 static servernode_t		g_localserverlist[MAX_LOCALSERVERS];
    241 static int				g_numlocalservers;
    242 static servernode_t		g_favoriteserverlist[MAX_FAVORITESERVERS];
    243 static int				g_numfavoriteservers;
    244 static servernode_t		g_mplayerserverlist[MAX_GLOBALSERVERS];
    245 static int				g_nummplayerservers;
    246 static int				g_servertype;
    247 static int				g_gametype;
    248 static int				g_sortkey;
    249 static int				g_emptyservers;
    250 static int				g_fullservers;
    251 
    252 
    253 /*
    254 =================
    255 ArenaServers_MaxPing
    256 =================
    257 */
    258 static int ArenaServers_MaxPing( void ) {
    259 	int		maxPing;
    260 
    261 	maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
    262 	if( maxPing < 100 ) {
    263 		maxPing = 100;
    264 	}
    265 	return maxPing;
    266 }
    267 
    268 
    269 /*
    270 =================
    271 ArenaServers_Compare
    272 =================
    273 */
    274 static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
    275 	float			f1;
    276 	float			f2;
    277 	servernode_t*	t1;
    278 	servernode_t*	t2;
    279 
    280 	t1 = (servernode_t *)arg1;
    281 	t2 = (servernode_t *)arg2;
    282 
    283 	switch( g_sortkey ) {
    284 	case SORT_HOST:
    285 		return Q_stricmp( t1->hostname, t2->hostname );
    286 
    287 	case SORT_MAP:
    288 		return Q_stricmp( t1->mapname, t2->mapname );
    289 
    290 	case SORT_CLIENTS:
    291 		f1 = t1->maxclients - t1->numclients;
    292 		if( f1 < 0 ) {
    293 			f1 = 0;
    294 		}
    295 
    296 		f2 = t2->maxclients - t2->numclients;
    297 		if( f2 < 0 ) {
    298 			f2 = 0;
    299 		}
    300 
    301 		if( f1 < f2 ) {
    302 			return 1;
    303 		}
    304 		if( f1 == f2 ) {
    305 			return 0;
    306 		}
    307 		return -1;
    308 
    309 	case SORT_GAME:
    310 		if( t1->gametype < t2->gametype ) {
    311 			return -1;
    312 		}
    313 		if( t1->gametype == t2->gametype ) {
    314 			return 0;
    315 		}
    316 		return 1;
    317 
    318 	case SORT_PING:
    319 		if( t1->pingtime < t2->pingtime ) {
    320 			return -1;
    321 		}
    322 		if( t1->pingtime > t2->pingtime ) {
    323 			return 1;
    324 		}
    325 		return Q_stricmp( t1->hostname, t2->hostname );
    326 	}
    327 
    328 	return 0;
    329 }
    330 
    331 
    332 /*
    333 =================
    334 ArenaServers_Go
    335 =================
    336 */
    337 static void ArenaServers_Go( void ) {
    338 	servernode_t*	servernode;
    339 
    340 	servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
    341 	if( servernode ) {
    342 		trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
    343 	}
    344 }
    345 
    346 
    347 /*
    348 =================
    349 ArenaServers_UpdatePicture
    350 =================
    351 */
    352 static void ArenaServers_UpdatePicture( void ) {
    353 	static char		picname[64];
    354 	servernode_t*	servernodeptr;
    355 
    356 	if( !g_arenaservers.list.numitems ) {
    357 		g_arenaservers.mappic.generic.name = NULL;
    358 	}
    359 	else {
    360 		servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
    361 		Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
    362 		g_arenaservers.mappic.generic.name = picname;
    363 	
    364 	}
    365 
    366 	// force shader update during draw
    367 	g_arenaservers.mappic.shader = 0;
    368 }
    369 
    370 
    371 /*
    372 =================
    373 ArenaServers_UpdateMenu
    374 =================
    375 */
    376 static void ArenaServers_UpdateMenu( void ) {
    377 	int				i;
    378 	int				j;
    379 	int				count;
    380 	char*			buff;
    381 	servernode_t*	servernodeptr;
    382 	table_t*		tableptr;
    383 	char			*pingColor;
    384 
    385 	if( g_arenaservers.numqueriedservers > 0 ) {
    386 		// servers found
    387 		if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
    388 			// show progress
    389 			Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
    390 			g_arenaservers.statusbar.string  = "Press SPACE to stop";
    391 			qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
    392 		}
    393 		else {
    394 			// all servers pinged - enable controls
    395 			g_arenaservers.master.generic.flags		&= ~QMF_GRAYED;
    396 			g_arenaservers.gametype.generic.flags	&= ~QMF_GRAYED;
    397 			g_arenaservers.sortkey.generic.flags	&= ~QMF_GRAYED;
    398 			g_arenaservers.showempty.generic.flags	&= ~QMF_GRAYED;
    399 			g_arenaservers.showfull.generic.flags	&= ~QMF_GRAYED;
    400 			g_arenaservers.list.generic.flags		&= ~QMF_GRAYED;
    401 			g_arenaservers.refresh.generic.flags	&= ~QMF_GRAYED;
    402 			g_arenaservers.go.generic.flags			&= ~QMF_GRAYED;
    403 			g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
    404 
    405 			// update status bar
    406 			if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
    407 				g_arenaservers.statusbar.string = quake3worldMessage;
    408 			}
    409 			else {
    410 				g_arenaservers.statusbar.string = "";
    411 			}
    412 
    413 		}
    414 	}
    415 	else {
    416 		// no servers found
    417 		if( g_arenaservers.refreshservers ) {
    418 			strcpy( g_arenaservers.status.string,"Scanning For Servers." );
    419 			g_arenaservers.statusbar.string = "Press SPACE to stop";
    420 
    421 			// disable controls during refresh
    422 			g_arenaservers.master.generic.flags		|= QMF_GRAYED;
    423 			g_arenaservers.gametype.generic.flags	|= QMF_GRAYED;
    424 			g_arenaservers.sortkey.generic.flags	|= QMF_GRAYED;
    425 			g_arenaservers.showempty.generic.flags	|= QMF_GRAYED;
    426 			g_arenaservers.showfull.generic.flags	|= QMF_GRAYED;
    427 			g_arenaservers.list.generic.flags		|= QMF_GRAYED;
    428 			g_arenaservers.refresh.generic.flags	|= QMF_GRAYED;
    429 			g_arenaservers.go.generic.flags			|= QMF_GRAYED;
    430 			g_arenaservers.punkbuster.generic.flags |= QMF_GRAYED;
    431 		}
    432 		else {
    433 			if( g_arenaservers.numqueriedservers < 0 ) {
    434 				strcpy(g_arenaservers.status.string,"No Response From Master Server." );
    435 			}
    436 			else {
    437 				strcpy(g_arenaservers.status.string,"No Servers Found." );
    438 			}
    439 
    440 			// update status bar
    441 			if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
    442 				g_arenaservers.statusbar.string = quake3worldMessage;
    443 			}
    444 			else {
    445 				g_arenaservers.statusbar.string = "";
    446 			}
    447 
    448 			// end of refresh - set control state
    449 			g_arenaservers.master.generic.flags		&= ~QMF_GRAYED;
    450 			g_arenaservers.gametype.generic.flags	&= ~QMF_GRAYED;
    451 			g_arenaservers.sortkey.generic.flags	&= ~QMF_GRAYED;
    452 			g_arenaservers.showempty.generic.flags	&= ~QMF_GRAYED;
    453 			g_arenaservers.showfull.generic.flags	&= ~QMF_GRAYED;
    454 			g_arenaservers.list.generic.flags		|= QMF_GRAYED;
    455 			g_arenaservers.refresh.generic.flags	&= ~QMF_GRAYED;
    456 			g_arenaservers.go.generic.flags			|= QMF_GRAYED;
    457 			g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
    458 		}
    459 
    460 		// zero out list box
    461 		g_arenaservers.list.numitems = 0;
    462 		g_arenaservers.list.curvalue = 0;
    463 		g_arenaservers.list.top      = 0;
    464 
    465 		// update picture
    466 		ArenaServers_UpdatePicture();
    467 		return;
    468 	}
    469 
    470 	// build list box strings - apply culling filters
    471 	servernodeptr = g_arenaservers.serverlist;
    472 	count         = *g_arenaservers.numservers;
    473 	for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
    474 		tableptr = &g_arenaservers.table[j];
    475 		tableptr->servernode = servernodeptr;
    476 		buff = tableptr->buff;
    477 
    478 		// can only cull valid results
    479 		if( !g_emptyservers && !servernodeptr->numclients ) {
    480 			continue;
    481 		}
    482 
    483 		if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
    484 			continue;
    485 		}
    486 
    487 		switch( g_gametype ) {
    488 		case GAMES_ALL:
    489 			break;
    490 
    491 		case GAMES_FFA:
    492 			if( servernodeptr->gametype != GT_FFA ) {
    493 				continue;
    494 			}
    495 			break;
    496 
    497 		case GAMES_TEAMPLAY:
    498 			if( servernodeptr->gametype != GT_TEAM ) {
    499 				continue;
    500 			}
    501 			break;
    502 
    503 		case GAMES_TOURNEY:
    504 			if( servernodeptr->gametype != GT_TOURNAMENT ) {
    505 				continue;
    506 			}
    507 			break;
    508 
    509 		case GAMES_CTF:
    510 			if( servernodeptr->gametype != GT_CTF ) {
    511 				continue;
    512 			}
    513 			break;
    514 		}
    515 
    516 		if( servernodeptr->pingtime < servernodeptr->minPing ) {
    517 			pingColor = S_COLOR_BLUE;
    518 		}
    519 		else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
    520 			pingColor = S_COLOR_BLUE;
    521 		}
    522 		else if( servernodeptr->pingtime < 200 ) {
    523 			pingColor = S_COLOR_GREEN;
    524 		}
    525 		else if( servernodeptr->pingtime < 400 ) {
    526 			pingColor = S_COLOR_YELLOW;
    527 		}
    528 		else {
    529 			pingColor = S_COLOR_RED;
    530 		}
    531 
    532 		Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %3s %s%3d " S_COLOR_YELLOW "%s", 
    533 			servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
    534  			servernodeptr->maxclients, servernodeptr->gamename,
    535 			netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
    536 		j++;
    537 	}
    538 
    539 	g_arenaservers.list.numitems = j;
    540 	g_arenaservers.list.curvalue = 0;
    541 	g_arenaservers.list.top      = 0;
    542 
    543 	// update picture
    544 	ArenaServers_UpdatePicture();
    545 }
    546 
    547 
    548 /*
    549 =================
    550 ArenaServers_Remove
    551 =================
    552 */
    553 static void ArenaServers_Remove( void )
    554 {
    555 	int				i;
    556 	servernode_t*	servernodeptr;
    557 	table_t*		tableptr;
    558 
    559 	if (!g_arenaservers.list.numitems)
    560 		return;
    561 
    562 	// remove selected item from display list
    563 	// items are in scattered order due to sort and cull
    564 	// perform delete on list box contents, resync all lists
    565 
    566 	tableptr      = &g_arenaservers.table[g_arenaservers.list.curvalue];
    567 	servernodeptr = tableptr->servernode;
    568 
    569 	// find address in master list
    570 	for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
    571 		if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
    572 				break;
    573 
    574 	// delete address from master list
    575 	if (i <= g_arenaservers.numfavoriteaddresses-1)
    576 	{
    577 		if (i < g_arenaservers.numfavoriteaddresses-1)
    578 		{
    579 			// shift items up
    580 			memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)*sizeof(MAX_ADDRESSLENGTH));
    581 		}
    582 		g_arenaservers.numfavoriteaddresses--;
    583 	}	
    584 
    585 	// find address in server list
    586 	for (i=0; i<g_numfavoriteservers; i++)
    587 		if (&g_favoriteserverlist[i] == servernodeptr)
    588 				break;
    589 
    590 	// delete address from server list
    591 	if (i <= g_numfavoriteservers-1)
    592 	{
    593 		if (i < g_numfavoriteservers-1)
    594 		{
    595 			// shift items up
    596 			memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
    597 		}
    598 		g_numfavoriteservers--;
    599 	}	
    600 
    601 	g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
    602 	g_arenaservers.currentping       = g_arenaservers.numfavoriteaddresses;
    603 }
    604 
    605 
    606 /*
    607 =================
    608 ArenaServers_Insert
    609 =================
    610 */
    611 static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
    612 {
    613 	servernode_t*	servernodeptr;
    614 	char*			s;
    615 	int				i;
    616 
    617 
    618 	if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != AS_FAVORITES))
    619 	{
    620 		// slow global or local servers do not get entered
    621 		return;
    622 	}
    623 
    624 	if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
    625 		// list full;
    626 		servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
    627 	} else {
    628 		// next slot
    629 		servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
    630 		(*g_arenaservers.numservers)++;
    631 	}
    632 
    633 	Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
    634 
    635 	Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
    636 	Q_CleanStr( servernodeptr->hostname );
    637 	Q_strupr( servernodeptr->hostname );
    638 
    639 	Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
    640 	Q_CleanStr( servernodeptr->mapname );
    641 	Q_strupr( servernodeptr->mapname );
    642 
    643 	servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
    644 	servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
    645 	servernodeptr->pingtime   = pingtime;
    646 	servernodeptr->minPing    = atoi( Info_ValueForKey( info, "minPing") );
    647 	servernodeptr->maxPing    = atoi( Info_ValueForKey( info, "maxPing") );
    648 	servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
    649 
    650 	/*
    651 	s = Info_ValueForKey( info, "nettype" );
    652 	for (i=0; ;i++)
    653 	{
    654 		if (!netnames[i])
    655 		{
    656 			servernodeptr->nettype = 0;
    657 			break;
    658 		}
    659 		else if (!Q_stricmp( netnames[i], s ))
    660 		{
    661 			servernodeptr->nettype = i;
    662 			break;
    663 		}
    664 	}
    665 	*/
    666 	servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
    667 
    668 	s = Info_ValueForKey( info, "game");
    669 	i = atoi( Info_ValueForKey( info, "gametype") );
    670 	if( i < 0 ) {
    671 		i = 0;
    672 	}
    673 	else if( i > 11 ) {
    674 		i = 12;
    675 	}
    676 	if( *s ) {
    677 		servernodeptr->gametype = i;//-1;
    678 		Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
    679 	}
    680 	else {
    681 		servernodeptr->gametype = i;
    682 		Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
    683 	}
    684 }
    685 
    686 
    687 /*
    688 =================
    689 ArenaServers_InsertFavorites
    690 
    691 Insert nonresponsive address book entries into display lists.
    692 =================
    693 */
    694 void ArenaServers_InsertFavorites( void )
    695 {
    696 	int		i;
    697 	int		j;
    698 	char	info[MAX_INFO_STRING];
    699 
    700 	// resync existing results with new or deleted cvars
    701 	info[0] = '\0';
    702 	Info_SetValueForKey( info, "hostname", "No Response" );
    703 	for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
    704 	{
    705 		// find favorite address in refresh list
    706 		for (j=0; j<g_numfavoriteservers; j++)
    707 			if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
    708 				break;
    709 
    710 		if ( j >= g_numfavoriteservers)
    711 		{
    712 			// not in list, add it
    713 			ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
    714 		}
    715 	}
    716 }
    717 
    718 
    719 /*
    720 =================
    721 ArenaServers_LoadFavorites
    722 
    723 Load cvar address book entries into local lists.
    724 =================
    725 */
    726 void ArenaServers_LoadFavorites( void )
    727 {
    728 	int				i;
    729 	int				j;
    730 	int				numtempitems;
    731 	char			emptyinfo[MAX_INFO_STRING];
    732 	char			adrstr[MAX_ADDRESSLENGTH];
    733 	servernode_t	templist[MAX_FAVORITESERVERS];
    734 	qboolean		found;
    735 
    736 	found        = qfalse;
    737 	emptyinfo[0] = '\0';
    738 
    739 	// copy the old
    740 	memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
    741 	numtempitems = g_numfavoriteservers;
    742 
    743 	// clear the current for sync
    744 	memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
    745 	g_numfavoriteservers = 0;
    746 
    747 	// resync existing results with new or deleted cvars
    748 	for (i=0; i<MAX_FAVORITESERVERS; i++)
    749 	{
    750 		trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
    751 		if (!adrstr[0])
    752 			continue;
    753 
    754 		// quick sanity check to avoid slow domain name resolving
    755 		// first character must be numeric
    756 		if (adrstr[0] < '0' || adrstr[0] > '9')
    757 			continue;
    758 
    759 		// favorite server addresses must be maintained outside refresh list
    760 		// this mimics local and global netadr's stored in client
    761 		// these can be fetched to fill ping list
    762 		strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );
    763 
    764 		// find this server in the old list
    765 		for (j=0; j<numtempitems; j++)
    766 			if (!Q_stricmp( templist[j].adrstr, adrstr ))
    767 				break;
    768 
    769 		if (j < numtempitems)
    770 		{
    771 			// found server - add exisiting results
    772 			memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
    773 			found = qtrue;
    774 		}
    775 		else
    776 		{
    777 			// add new server
    778 			Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
    779 			g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
    780 		}
    781 
    782 		g_numfavoriteservers++;
    783 	}
    784 
    785 	g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
    786 
    787 	if (!found)
    788 	{
    789 		// no results were found, reset server list
    790 		// list will be automatically refreshed when selected
    791 		g_numfavoriteservers = 0;
    792 	}
    793 }
    794 
    795 
    796 /*
    797 =================
    798 ArenaServers_StopRefresh
    799 =================
    800 */
    801 static void ArenaServers_StopRefresh( void )
    802 {
    803 	if (!g_arenaservers.refreshservers)
    804 		// not currently refreshing
    805 		return;
    806 
    807 	g_arenaservers.refreshservers = qfalse;
    808 
    809 	if (g_servertype == AS_FAVORITES)
    810 	{
    811 		// nonresponsive favorites must be shown
    812 		ArenaServers_InsertFavorites();
    813 	}
    814 
    815 	// final tally
    816 	if (g_arenaservers.numqueriedservers >= 0)
    817 	{
    818 		g_arenaservers.currentping       = *g_arenaservers.numservers;
    819 		g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
    820 	}
    821 	
    822 	// sort
    823 	qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
    824 
    825 	ArenaServers_UpdateMenu();
    826 }
    827 
    828 
    829 /*
    830 =================
    831 ArenaServers_DoRefresh
    832 =================
    833 */
    834 static void ArenaServers_DoRefresh( void )
    835 {
    836 	int		i;
    837 	int		j;
    838 	int		time;
    839 	int		maxPing;
    840 	char	adrstr[MAX_ADDRESSLENGTH];
    841 	char	info[MAX_INFO_STRING];
    842 
    843 	if (uis.realtime < g_arenaservers.refreshtime)
    844 	{
    845 	  if (g_servertype != AS_FAVORITES) {
    846 			if (g_servertype == AS_LOCAL) {
    847 				if (!trap_LAN_GetServerCount(g_servertype)) {
    848 					return;
    849 				}
    850 			}
    851 			if (trap_LAN_GetServerCount(g_servertype) < 0) {
    852 			  // still waiting for response
    853 			  return;
    854 			}
    855 	  }
    856 	}
    857 
    858 	if (uis.realtime < g_arenaservers.nextpingtime)
    859 	{
    860 		// wait for time trigger
    861 		return;
    862 	}
    863 
    864 	// trigger at 10Hz intervals
    865 	g_arenaservers.nextpingtime = uis.realtime + 10;
    866 
    867 	// process ping results
    868 	maxPing = ArenaServers_MaxPing();
    869 	for (i=0; i<MAX_PINGREQUESTS; i++)
    870 	{
    871 		trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
    872 		if (!adrstr[0])
    873 		{
    874 			// ignore empty or pending pings
    875 			continue;
    876 		}
    877 
    878 		// find ping result in our local list
    879 		for (j=0; j<MAX_PINGREQUESTS; j++)
    880 			if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
    881 				break;
    882 
    883 		if (j < MAX_PINGREQUESTS)
    884 		{
    885 			// found it
    886 			if (!time)
    887 			{
    888 				time = uis.realtime - g_arenaservers.pinglist[j].start;
    889 				if (time < maxPing)
    890 				{
    891 					// still waiting
    892 					continue;
    893 				}
    894 			}
    895 
    896 			if (time > maxPing)
    897 			{
    898 				// stale it out
    899 				info[0] = '\0';
    900 				time    = maxPing;
    901 			}
    902 			else
    903 			{
    904 				trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
    905 			}
    906 
    907 			// insert ping results
    908 			ArenaServers_Insert( adrstr, info, time );
    909 
    910 			// clear this query from internal list
    911 			g_arenaservers.pinglist[j].adrstr[0] = '\0';
    912    		}
    913 
    914 		// clear this query from external list
    915 		trap_LAN_ClearPing( i );
    916 	}
    917 
    918 	// get results of servers query
    919 	// counts can increase as servers respond
    920 	if (g_servertype == AS_FAVORITES) {
    921 	  g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
    922 	} else {
    923 	  g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(g_servertype);
    924 	}
    925 
    926 //	if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
    927 //		g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
    928 
    929 	// send ping requests in reasonable bursts
    930 	// iterate ping through all found servers
    931 	for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
    932 	{
    933 		if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
    934 		{
    935 			// ping queue is full
    936 			break;
    937 		}
    938 
    939 		// find empty slot
    940 		for (j=0; j<MAX_PINGREQUESTS; j++)
    941 			if (!g_arenaservers.pinglist[j].adrstr[0])
    942 				break;
    943 
    944 		if (j >= MAX_PINGREQUESTS)
    945 			// no empty slots available yet - wait for timeout
    946 			break;
    947 
    948 		// get an address to ping
    949 
    950 		if (g_servertype == AS_FAVORITES) {
    951 		  strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] ); 		
    952 		} else {
    953 		  trap_LAN_GetServerAddressString(g_servertype, g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
    954 		}
    955 
    956 		strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
    957 		g_arenaservers.pinglist[j].start = uis.realtime;
    958 
    959 		trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr )  );
    960 		
    961 		// advance to next server
    962 		g_arenaservers.currentping++;
    963 	}
    964 
    965 	if (!trap_LAN_GetPingQueueCount())
    966 	{
    967 		// all pings completed
    968 		ArenaServers_StopRefresh();
    969 		return;
    970 	}
    971 
    972 	// update the user interface with ping status
    973 	ArenaServers_UpdateMenu();
    974 }
    975 
    976 
    977 /*
    978 =================
    979 ArenaServers_StartRefresh
    980 =================
    981 */
    982 static void ArenaServers_StartRefresh( void )
    983 {
    984 	int		i;
    985 	char	myargs[32], protocol[32];
    986 
    987 	memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
    988 
    989 	for (i=0; i<MAX_PINGREQUESTS; i++)
    990 	{
    991 		g_arenaservers.pinglist[i].adrstr[0] = '\0';
    992 		trap_LAN_ClearPing( i );
    993 	}
    994 
    995 	g_arenaservers.refreshservers    = qtrue;
    996 	g_arenaservers.currentping       = 0;
    997 	g_arenaservers.nextpingtime      = 0;
    998 	*g_arenaservers.numservers       = 0;
    999 	g_arenaservers.numqueriedservers = 0;
   1000 
   1001 	// allow max 5 seconds for responses
   1002 	g_arenaservers.refreshtime = uis.realtime + 5000;
   1003 
   1004 	// place menu in zeroed state
   1005 	ArenaServers_UpdateMenu();
   1006 
   1007 	if( g_servertype == AS_LOCAL ) {
   1008 		trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
   1009 		return;
   1010 	}
   1011 
   1012 	if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
   1013 		if( g_servertype == AS_GLOBAL ) {
   1014 			i = 0;
   1015 		}
   1016 		else {
   1017 			i = 1;
   1018 		}
   1019 
   1020 		switch( g_arenaservers.gametype.curvalue ) {
   1021 		default:
   1022 		case GAMES_ALL:
   1023 			myargs[0] = 0;
   1024 			break;
   1025 
   1026 		case GAMES_FFA:
   1027 			strcpy( myargs, " ffa" );
   1028 			break;
   1029 
   1030 		case GAMES_TEAMPLAY:
   1031 			strcpy( myargs, " team" );
   1032 			break;
   1033 
   1034 		case GAMES_TOURNEY:
   1035 			strcpy( myargs, " tourney" );
   1036 			break;
   1037 
   1038 		case GAMES_CTF:
   1039 			strcpy( myargs, " ctf" );
   1040 			break;
   1041 		}
   1042 
   1043 
   1044 		if (g_emptyservers) {
   1045 			strcat(myargs, " empty");
   1046 		}
   1047 
   1048 		if (g_fullservers) {
   1049 			strcat(myargs, " full");
   1050 		}
   1051 
   1052 		protocol[0] = '\0';
   1053 		trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) );
   1054 		if (strlen(protocol)) {
   1055 			trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", i, protocol, myargs ));
   1056 		}
   1057 		else {
   1058 			trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", i, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
   1059 		}
   1060 	}
   1061 }
   1062 
   1063 
   1064 /*
   1065 =================
   1066 ArenaServers_SaveChanges
   1067 =================
   1068 */
   1069 void ArenaServers_SaveChanges( void )
   1070 {
   1071 	int	i;
   1072 
   1073 	for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
   1074 		trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );
   1075 
   1076 	for (; i<MAX_FAVORITESERVERS; i++)
   1077 		trap_Cvar_Set( va("server%d",i+1), "" );
   1078 }
   1079 
   1080 
   1081 /*
   1082 =================
   1083 ArenaServers_Sort
   1084 =================
   1085 */
   1086 void ArenaServers_Sort( int type ) {
   1087 	if( g_sortkey == type ) {
   1088 		return;
   1089 	}
   1090 
   1091 	g_sortkey = type;
   1092 	qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
   1093 }
   1094 
   1095 
   1096 /*
   1097 =================
   1098 ArenaServers_SetType
   1099 =================
   1100 */
   1101 void ArenaServers_SetType( int type )
   1102 {
   1103 	if (g_servertype == type)
   1104 		return;
   1105 
   1106 	g_servertype = type;
   1107 
   1108 	switch( type ) {
   1109 	default:
   1110 	case AS_LOCAL:
   1111 		g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
   1112 		g_arenaservers.serverlist = g_localserverlist;
   1113 		g_arenaservers.numservers = &g_numlocalservers;
   1114 		g_arenaservers.maxservers = MAX_LOCALSERVERS;
   1115 		break;
   1116 
   1117 	case AS_GLOBAL:
   1118 		g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
   1119 		g_arenaservers.serverlist = g_globalserverlist;
   1120 		g_arenaservers.numservers = &g_numglobalservers;
   1121 		g_arenaservers.maxservers = MAX_GLOBALSERVERS;
   1122 		break;
   1123 
   1124 	case AS_FAVORITES:
   1125 		g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
   1126 		g_arenaservers.serverlist = g_favoriteserverlist;
   1127 		g_arenaservers.numservers = &g_numfavoriteservers;
   1128 		g_arenaservers.maxservers = MAX_FAVORITESERVERS;
   1129 		break;
   1130 
   1131 	case AS_MPLAYER:
   1132 		g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
   1133 		g_arenaservers.serverlist = g_mplayerserverlist;
   1134 		g_arenaservers.numservers = &g_nummplayerservers;
   1135 		g_arenaservers.maxservers = MAX_GLOBALSERVERS;
   1136 		break;
   1137 		
   1138 	}
   1139 
   1140 	if( !*g_arenaservers.numservers ) {
   1141 		ArenaServers_StartRefresh();
   1142 	}
   1143 	else {
   1144 		// avoid slow operation, use existing results
   1145 		g_arenaservers.currentping       = *g_arenaservers.numservers;
   1146 		g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
   1147 		ArenaServers_UpdateMenu();
   1148 	}
   1149 	strcpy(g_arenaservers.status.string,"hit refresh to update");
   1150 }
   1151 
   1152 /*
   1153 =================
   1154 PunkBuster_Confirm
   1155 =================
   1156 */
   1157 static void Punkbuster_ConfirmEnable( qboolean result ) {
   1158 	if (result)
   1159 	{		
   1160 		trap_SetPbClStatus(1);
   1161 	}
   1162 	g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
   1163 }
   1164 
   1165 static void Punkbuster_ConfirmDisable( qboolean result ) {
   1166 	if (result)
   1167 	{
   1168 		trap_SetPbClStatus(0);
   1169 		UI_Message( punkbuster_msg );
   1170 	}
   1171 	g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
   1172 }
   1173 
   1174 /*
   1175 =================
   1176 ArenaServers_Event
   1177 =================
   1178 */
   1179 static void ArenaServers_Event( void* ptr, int event ) {
   1180 	int		id;
   1181 	int value;
   1182 
   1183 	id = ((menucommon_s*)ptr)->id;
   1184 
   1185 	if( event != QM_ACTIVATED && id != ID_LIST ) {
   1186 		return;
   1187 	}
   1188 
   1189 	switch( id ) {
   1190 	case ID_MASTER:
   1191 		value = g_arenaservers.master.curvalue;
   1192 		if (value >= 1)
   1193 		{
   1194 			value++;
   1195 		}
   1196 		trap_Cvar_SetValue( "ui_browserMaster", value );
   1197 		ArenaServers_SetType( value );
   1198 		break;
   1199 
   1200 	case ID_GAMETYPE:
   1201 		trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
   1202 		g_gametype = g_arenaservers.gametype.curvalue;
   1203 		ArenaServers_UpdateMenu();
   1204 		break;
   1205 
   1206 	case ID_SORTKEY:
   1207 		trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
   1208 		ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
   1209 		ArenaServers_UpdateMenu();
   1210 		break;
   1211 
   1212 	case ID_SHOW_FULL:
   1213 		trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
   1214 		g_fullservers = g_arenaservers.showfull.curvalue;
   1215 		ArenaServers_UpdateMenu();
   1216 		break;
   1217 
   1218 	case ID_SHOW_EMPTY:
   1219 		trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
   1220 		g_emptyservers = g_arenaservers.showempty.curvalue;
   1221 		ArenaServers_UpdateMenu();
   1222 		break;
   1223 
   1224 	case ID_LIST:
   1225 		if( event == QM_GOTFOCUS ) {
   1226 			ArenaServers_UpdatePicture();
   1227 		}
   1228 		break;
   1229 
   1230 	case ID_SCROLL_UP:
   1231 		ScrollList_Key( &g_arenaservers.list, K_UPARROW );
   1232 		break;
   1233 
   1234 	case ID_SCROLL_DOWN:
   1235 		ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
   1236 		break;
   1237 
   1238 	case ID_BACK:
   1239 		ArenaServers_StopRefresh();
   1240 		ArenaServers_SaveChanges();
   1241 		UI_PopMenu();
   1242 		break;
   1243 
   1244 	case ID_REFRESH:
   1245 		ArenaServers_StartRefresh();
   1246 		break;
   1247 
   1248 	case ID_SPECIFY:
   1249 		UI_SpecifyServerMenu();
   1250 		break;
   1251 
   1252 	case ID_CREATE:
   1253 		UI_StartServerMenu( qtrue );
   1254 		break;
   1255 
   1256 	case ID_CONNECT:
   1257 		ArenaServers_Go();
   1258 		break;
   1259 
   1260 	case ID_REMOVE:
   1261 		ArenaServers_Remove();
   1262 		ArenaServers_UpdateMenu();
   1263 		break;
   1264 	
   1265 	case ID_PUNKBUSTER:
   1266 		if (g_arenaservers.punkbuster.curvalue)			
   1267 		{
   1268 			UI_ConfirmMenu_Style( "Enable Punkbuster?",  UI_CENTER|UI_INVERSE|UI_SMALLFONT, (voidfunc_f)NULL, Punkbuster_ConfirmEnable );
   1269 		}
   1270 		else
   1271 		{
   1272 			UI_ConfirmMenu_Style( "Disable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, (voidfunc_f)NULL, Punkbuster_ConfirmDisable );
   1273 		}
   1274 		break;
   1275 	}
   1276 }
   1277 
   1278 
   1279 /*
   1280 =================
   1281 ArenaServers_MenuDraw
   1282 =================
   1283 */
   1284 static void ArenaServers_MenuDraw( void )
   1285 {
   1286 	if (g_arenaservers.refreshservers)
   1287 		ArenaServers_DoRefresh();
   1288 
   1289 	Menu_Draw( &g_arenaservers.menu );
   1290 }
   1291 
   1292 
   1293 /*
   1294 =================
   1295 ArenaServers_MenuKey
   1296 =================
   1297 */
   1298 static sfxHandle_t ArenaServers_MenuKey( int key ) {
   1299 	if( key == K_SPACE  && g_arenaservers.refreshservers ) {
   1300 		ArenaServers_StopRefresh();	
   1301 		return menu_move_sound;
   1302 	}
   1303 
   1304 	if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == AS_FAVORITES ) &&
   1305 		( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
   1306 		ArenaServers_Remove();
   1307 		ArenaServers_UpdateMenu();
   1308 		return menu_move_sound;
   1309 	}
   1310 
   1311 	if( key == K_MOUSE2 || key == K_ESCAPE ) {
   1312 		ArenaServers_StopRefresh();
   1313 		ArenaServers_SaveChanges();
   1314 	}
   1315 
   1316 
   1317 	return Menu_DefaultKey( &g_arenaservers.menu, key );
   1318 }
   1319 
   1320 
   1321 /*
   1322 =================
   1323 ArenaServers_MenuInit
   1324 =================
   1325 */
   1326 static void ArenaServers_MenuInit( void ) {
   1327 	int			i;
   1328 	int			type;
   1329 	int			y;
   1330 	int			value;
   1331 	static char	statusbuffer[MAX_STATUSLENGTH];
   1332 
   1333 	// zero set all our globals
   1334 	memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
   1335 
   1336 	ArenaServers_Cache();
   1337 
   1338 	g_arenaservers.menu.fullscreen = qtrue;
   1339 	g_arenaservers.menu.wrapAround = qtrue;
   1340 	g_arenaservers.menu.draw       = ArenaServers_MenuDraw;
   1341 	g_arenaservers.menu.key        = ArenaServers_MenuKey;
   1342 
   1343 	g_arenaservers.banner.generic.type  = MTYPE_BTEXT;
   1344 	g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
   1345 	g_arenaservers.banner.generic.x	    = 320;
   1346 	g_arenaservers.banner.generic.y	    = 16;
   1347 	g_arenaservers.banner.string  		= "ARENA SERVERS";
   1348 	g_arenaservers.banner.style  	    = UI_CENTER;
   1349 	g_arenaservers.banner.color  	    = color_white;
   1350 
   1351 	y = 80;
   1352 	g_arenaservers.master.generic.type			= MTYPE_SPINCONTROL;
   1353 	g_arenaservers.master.generic.name			= "Servers:";
   1354 	g_arenaservers.master.generic.flags			= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1355 	g_arenaservers.master.generic.callback		= ArenaServers_Event;
   1356 	g_arenaservers.master.generic.id			= ID_MASTER;
   1357 	g_arenaservers.master.generic.x				= 320;
   1358 	g_arenaservers.master.generic.y				= y;
   1359 	g_arenaservers.master.itemnames				= master_items;
   1360 
   1361 	y += SMALLCHAR_HEIGHT;
   1362 	g_arenaservers.gametype.generic.type		= MTYPE_SPINCONTROL;
   1363 	g_arenaservers.gametype.generic.name		= "Game Type:";
   1364 	g_arenaservers.gametype.generic.flags		= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1365 	g_arenaservers.gametype.generic.callback	= ArenaServers_Event;
   1366 	g_arenaservers.gametype.generic.id			= ID_GAMETYPE;
   1367 	g_arenaservers.gametype.generic.x			= 320;
   1368 	g_arenaservers.gametype.generic.y			= y;
   1369 	g_arenaservers.gametype.itemnames			= servertype_items;
   1370 
   1371 	y += SMALLCHAR_HEIGHT;
   1372 	g_arenaservers.sortkey.generic.type			= MTYPE_SPINCONTROL;
   1373 	g_arenaservers.sortkey.generic.name			= "Sort By:";
   1374 	g_arenaservers.sortkey.generic.flags		= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1375 	g_arenaservers.sortkey.generic.callback		= ArenaServers_Event;
   1376 	g_arenaservers.sortkey.generic.id			= ID_SORTKEY;
   1377 	g_arenaservers.sortkey.generic.x			= 320;
   1378 	g_arenaservers.sortkey.generic.y			= y;
   1379 	g_arenaservers.sortkey.itemnames			= sortkey_items;
   1380 
   1381 	y += SMALLCHAR_HEIGHT;
   1382 	g_arenaservers.showfull.generic.type		= MTYPE_RADIOBUTTON;
   1383 	g_arenaservers.showfull.generic.name		= "Show Full:";
   1384 	g_arenaservers.showfull.generic.flags		= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1385 	g_arenaservers.showfull.generic.callback	= ArenaServers_Event;
   1386 	g_arenaservers.showfull.generic.id			= ID_SHOW_FULL;
   1387 	g_arenaservers.showfull.generic.x			= 320;
   1388 	g_arenaservers.showfull.generic.y			= y;
   1389 
   1390 	y += SMALLCHAR_HEIGHT;
   1391 	g_arenaservers.showempty.generic.type		= MTYPE_RADIOBUTTON;
   1392 	g_arenaservers.showempty.generic.name		= "Show Empty:";
   1393 	g_arenaservers.showempty.generic.flags		= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1394 	g_arenaservers.showempty.generic.callback	= ArenaServers_Event;
   1395 	g_arenaservers.showempty.generic.id			= ID_SHOW_EMPTY;
   1396 	g_arenaservers.showempty.generic.x			= 320;
   1397 	g_arenaservers.showempty.generic.y			= y;
   1398 
   1399 	y += 3 * SMALLCHAR_HEIGHT;
   1400 	g_arenaservers.list.generic.type			= MTYPE_SCROLLLIST;
   1401 	g_arenaservers.list.generic.flags			= QMF_HIGHLIGHT_IF_FOCUS;
   1402 	g_arenaservers.list.generic.id				= ID_LIST;
   1403 	g_arenaservers.list.generic.callback		= ArenaServers_Event;
   1404 	g_arenaservers.list.generic.x				= 72;
   1405 	g_arenaservers.list.generic.y				= y;
   1406 	g_arenaservers.list.width					= MAX_LISTBOXWIDTH;
   1407 	g_arenaservers.list.height					= 11;
   1408 	g_arenaservers.list.itemnames				= (const char **)g_arenaservers.items;
   1409 	for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
   1410 		g_arenaservers.items[i] = g_arenaservers.table[i].buff;
   1411 	}
   1412 
   1413 	g_arenaservers.mappic.generic.type			= MTYPE_BITMAP;
   1414 	g_arenaservers.mappic.generic.flags			= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
   1415 	g_arenaservers.mappic.generic.x				= 72;
   1416 	g_arenaservers.mappic.generic.y				= 80;
   1417 	g_arenaservers.mappic.width					= 128;
   1418 	g_arenaservers.mappic.height				= 96;
   1419 	g_arenaservers.mappic.errorpic				= ART_UNKNOWNMAP;
   1420 
   1421 	g_arenaservers.arrows.generic.type			= MTYPE_BITMAP;
   1422 	g_arenaservers.arrows.generic.name			= ART_ARROWS0;
   1423 	g_arenaservers.arrows.generic.flags			= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
   1424 	g_arenaservers.arrows.generic.callback		= ArenaServers_Event;
   1425 	g_arenaservers.arrows.generic.x				= 512+48;
   1426 	g_arenaservers.arrows.generic.y				= 240-64+16;
   1427 	g_arenaservers.arrows.width					= 64;
   1428 	g_arenaservers.arrows.height				= 128;
   1429 
   1430 	g_arenaservers.up.generic.type				= MTYPE_BITMAP;
   1431 	g_arenaservers.up.generic.flags				= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
   1432 	g_arenaservers.up.generic.callback			= ArenaServers_Event;
   1433 	g_arenaservers.up.generic.id				= ID_SCROLL_UP;
   1434 	g_arenaservers.up.generic.x					= 512+48;
   1435 	g_arenaservers.up.generic.y					= 240-64+16;
   1436 	g_arenaservers.up.width						= 64;
   1437 	g_arenaservers.up.height					= 64;
   1438 	g_arenaservers.up.focuspic					= ART_ARROWS_UP;
   1439 
   1440 	g_arenaservers.down.generic.type			= MTYPE_BITMAP;
   1441 	g_arenaservers.down.generic.flags			= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
   1442 	g_arenaservers.down.generic.callback		= ArenaServers_Event;
   1443 	g_arenaservers.down.generic.id				= ID_SCROLL_DOWN;
   1444 	g_arenaservers.down.generic.x				= 512+48;
   1445 	g_arenaservers.down.generic.y				= 240+16;
   1446 	g_arenaservers.down.width					= 64;
   1447 	g_arenaservers.down.height					= 64;
   1448 	g_arenaservers.down.focuspic				= ART_ARROWS_DOWN;
   1449 
   1450 	y = 376;
   1451 	g_arenaservers.status.generic.type		= MTYPE_TEXT;
   1452 	g_arenaservers.status.generic.x			= 320;
   1453 	g_arenaservers.status.generic.y			= y;
   1454 	g_arenaservers.status.string			= statusbuffer;
   1455 	g_arenaservers.status.style				= UI_CENTER|UI_SMALLFONT;
   1456 	g_arenaservers.status.color				= menu_text_color;
   1457 
   1458 	y += SMALLCHAR_HEIGHT;
   1459 	g_arenaservers.statusbar.generic.type   = MTYPE_TEXT;
   1460 	g_arenaservers.statusbar.generic.x	    = 320;
   1461 	g_arenaservers.statusbar.generic.y	    = y;
   1462 	g_arenaservers.statusbar.string	        = "";
   1463 	g_arenaservers.statusbar.style	        = UI_CENTER|UI_SMALLFONT;
   1464 	g_arenaservers.statusbar.color	        = text_color_normal;
   1465 
   1466 	g_arenaservers.remove.generic.type		= MTYPE_BITMAP;
   1467 	g_arenaservers.remove.generic.name		= ART_REMOVE0;
   1468 	g_arenaservers.remove.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1469 	g_arenaservers.remove.generic.callback	= ArenaServers_Event;
   1470 	g_arenaservers.remove.generic.id		= ID_REMOVE;
   1471 	g_arenaservers.remove.generic.x			= 450;
   1472 	g_arenaservers.remove.generic.y			= 86;
   1473 	g_arenaservers.remove.width				= 96;
   1474 	g_arenaservers.remove.height			= 48;
   1475 	g_arenaservers.remove.focuspic			= ART_REMOVE1;
   1476 
   1477 	g_arenaservers.back.generic.type		= MTYPE_BITMAP;
   1478 	g_arenaservers.back.generic.name		= ART_BACK0;
   1479 	g_arenaservers.back.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1480 	g_arenaservers.back.generic.callback	= ArenaServers_Event;
   1481 	g_arenaservers.back.generic.id			= ID_BACK;
   1482 	g_arenaservers.back.generic.x			= 0;
   1483 	g_arenaservers.back.generic.y			= 480-64;
   1484 	g_arenaservers.back.width				= 128;
   1485 	g_arenaservers.back.height				= 64;
   1486 	g_arenaservers.back.focuspic			= ART_BACK1;
   1487 
   1488 	g_arenaservers.specify.generic.type	    = MTYPE_BITMAP;
   1489 	g_arenaservers.specify.generic.name		= ART_SPECIFY0;
   1490 	g_arenaservers.specify.generic.flags    = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1491 	g_arenaservers.specify.generic.callback = ArenaServers_Event;
   1492 	g_arenaservers.specify.generic.id	    = ID_SPECIFY;
   1493 	g_arenaservers.specify.generic.x		= 128;
   1494 	g_arenaservers.specify.generic.y		= 480-64;
   1495 	g_arenaservers.specify.width  		    = 128;
   1496 	g_arenaservers.specify.height  		    = 64;
   1497 	g_arenaservers.specify.focuspic         = ART_SPECIFY1;
   1498 
   1499 	g_arenaservers.refresh.generic.type		= MTYPE_BITMAP;
   1500 	g_arenaservers.refresh.generic.name		= ART_REFRESH0;
   1501 	g_arenaservers.refresh.generic.flags	= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1502 	g_arenaservers.refresh.generic.callback	= ArenaServers_Event;
   1503 	g_arenaservers.refresh.generic.id		= ID_REFRESH;
   1504 	g_arenaservers.refresh.generic.x		= 256;
   1505 	g_arenaservers.refresh.generic.y		= 480-64;
   1506 	g_arenaservers.refresh.width			= 128;
   1507 	g_arenaservers.refresh.height			= 64;
   1508 	g_arenaservers.refresh.focuspic			= ART_REFRESH1;
   1509 
   1510 	g_arenaservers.create.generic.type		= MTYPE_BITMAP;
   1511 	g_arenaservers.create.generic.name		= ART_CREATE0;
   1512 	g_arenaservers.create.generic.flags		= QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
   1513 	g_arenaservers.create.generic.callback	= ArenaServers_Event;
   1514 	g_arenaservers.create.generic.id		= ID_CREATE;
   1515 	g_arenaservers.create.generic.x			= 384;
   1516 	g_arenaservers.create.generic.y			= 480-64;
   1517 	g_arenaservers.create.width				= 128;
   1518 	g_arenaservers.create.height			= 64;
   1519 	g_arenaservers.create.focuspic			= ART_CREATE1;
   1520 
   1521 	g_arenaservers.go.generic.type			= MTYPE_BITMAP;
   1522 	g_arenaservers.go.generic.name			= ART_CONNECT0;
   1523 	g_arenaservers.go.generic.flags			= QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
   1524 	g_arenaservers.go.generic.callback		= ArenaServers_Event;
   1525 	g_arenaservers.go.generic.id			= ID_CONNECT;
   1526 	g_arenaservers.go.generic.x				= 640;
   1527 	g_arenaservers.go.generic.y				= 480-64;
   1528 	g_arenaservers.go.width					= 128;
   1529 	g_arenaservers.go.height				= 64;
   1530 	g_arenaservers.go.focuspic				= ART_CONNECT1;
   1531 
   1532 	g_arenaservers.punkbuster.generic.type			= MTYPE_SPINCONTROL;
   1533 	g_arenaservers.punkbuster.generic.name			= "Punkbuster:";
   1534 	g_arenaservers.punkbuster.generic.flags			= QMF_PULSEIFFOCUS|QMF_SMALLFONT;
   1535 	g_arenaservers.punkbuster.generic.callback		= ArenaServers_Event;
   1536 	g_arenaservers.punkbuster.generic.id			= ID_PUNKBUSTER;
   1537 	g_arenaservers.punkbuster.generic.x				= 480+32;
   1538 	g_arenaservers.punkbuster.generic.y				= 144;
   1539 	g_arenaservers.punkbuster.itemnames				= punkbuster_items;
   1540 	
   1541 	g_arenaservers.pblogo.generic.type			= MTYPE_BITMAP;
   1542 	g_arenaservers.pblogo.generic.name			= ART_PUNKBUSTER;
   1543 	g_arenaservers.pblogo.generic.flags			= QMF_LEFT_JUSTIFY|QMF_INACTIVE;
   1544 	g_arenaservers.pblogo.generic.x				= 526;
   1545 	g_arenaservers.pblogo.generic.y				= 176;
   1546 	g_arenaservers.pblogo.width					= 32;
   1547 	g_arenaservers.pblogo.height				= 16;
   1548 	g_arenaservers.pblogo.errorpic				= ART_UNKNOWNMAP;
   1549 	
   1550 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );
   1551 
   1552 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
   1553 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
   1554 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
   1555 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
   1556 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
   1557 
   1558 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
   1559 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list );
   1560 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status );
   1561 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar );
   1562 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows );
   1563 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up );
   1564 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down );
   1565 
   1566 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
   1567 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
   1568 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
   1569 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
   1570 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
   1571 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
   1572 
   1573 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
   1574 	Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
   1575 	
   1576 	ArenaServers_LoadFavorites();
   1577 
   1578 	g_servertype = Com_Clamp( 0, 3, ui_browserMaster.integer );
   1579 	// hack to get rid of MPlayer stuff
   1580 	value = g_servertype;
   1581 	if (value >= 1)
   1582 		value--;
   1583 	g_arenaservers.master.curvalue = value;
   1584 
   1585 	g_gametype = Com_Clamp( 0, 4, ui_browserGameType.integer );
   1586 	g_arenaservers.gametype.curvalue = g_gametype;
   1587 
   1588 	g_sortkey = Com_Clamp( 0, 4, ui_browserSortKey.integer );
   1589 	g_arenaservers.sortkey.curvalue = g_sortkey;
   1590 
   1591 	g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
   1592 	g_arenaservers.showfull.curvalue = g_fullservers;
   1593 
   1594 	g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
   1595 	g_arenaservers.showempty.curvalue = g_emptyservers;
   1596 	
   1597 	g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
   1598 
   1599 	// force to initial state and refresh
   1600 	type = g_servertype;
   1601 	g_servertype = -1;
   1602 	ArenaServers_SetType( type );
   1603 
   1604 	trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
   1605 }
   1606 
   1607 
   1608 /*
   1609 =================
   1610 ArenaServers_Cache
   1611 =================
   1612 */
   1613 void ArenaServers_Cache( void ) {
   1614 	trap_R_RegisterShaderNoMip( ART_BACK0 );
   1615 	trap_R_RegisterShaderNoMip( ART_BACK1 );
   1616 	trap_R_RegisterShaderNoMip( ART_CREATE0 );
   1617 	trap_R_RegisterShaderNoMip( ART_CREATE1 );
   1618 	trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
   1619 	trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
   1620 	trap_R_RegisterShaderNoMip( ART_REFRESH0 );
   1621 	trap_R_RegisterShaderNoMip( ART_REFRESH1 );
   1622 	trap_R_RegisterShaderNoMip( ART_CONNECT0 );
   1623 	trap_R_RegisterShaderNoMip( ART_CONNECT1 );
   1624 	trap_R_RegisterShaderNoMip( ART_ARROWS0  );
   1625 	trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
   1626 	trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
   1627 	trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
   1628 	trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
   1629 }
   1630 
   1631 
   1632 /*
   1633 =================
   1634 UI_ArenaServersMenu
   1635 =================
   1636 */
   1637 void UI_ArenaServersMenu( void ) {
   1638 	ArenaServers_MenuInit();
   1639 	UI_PushMenu( &g_arenaservers.menu );
   1640 }