lilium-voyager/code/q3_ui/ui_servers2.c
Zack Middleton 9f239d647b Make 'globalservers 0' fetch all masters
This commit fixes the vanilla Q3 UI VMs not displaying a server list
when id Software's master server is down.

Originally master 0 for the globalservers command was Internet and
master 1 was MPlayer (defunct). In 2008 ioquake3 changed it so that
master 0 to 4 were five separate master servers with no affect on
original Quake3/Team Arena UI VMs; they continued to get the server
list from master.quake3arena.com.

id Software's master server (master.quake3arena.com) goes down
occasionally. Using ioq3's UI VM additional master servers can be
accessed but players using the original UI VMs are unable to get a
server list.

In order to fix the original UI VMs in Quake3/Team Arena's pk3s this
commit makes 'globalservers 0' fetch all master servers. So players
get a combined list of id Software's and ioquake3's master list. Or
just ioquake3's list if id Software's master is down.

Getting lists from individual master servers using globalservers has
changed from 0 through 4 to 1 through 5 to accommodate using 0 for
other purposes. This commit modifies ioq3's UI code to support the new
values for globalservers command.

A side affect of these changes is that UI VMs based on ioq3 since 2008
will have Internet1 fetch all master servers and Internet2 request
sv_master1 instead of sv_master2 and so on. It may be worth noting that
getting server list from masters 3-5 could not be done using ioq3's UI
before 2011.
2017-07-20 15:39:51 -05:00

1636 lines
44 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
/*
=======================================================================
MULTIPLAYER MENU (SERVER BROWSER)
=======================================================================
*/
#include "ui_local.h"
#define MAX_GLOBALSERVERS 128
#define MAX_PINGREQUESTS 32
#define MAX_ADDRESSLENGTH 64
#define MAX_HOSTNAMELENGTH 22
#define MAX_MAPNAMELENGTH 16
#define MAX_LISTBOXITEMS 128
#define MAX_LOCALSERVERS 128
#define MAX_STATUSLENGTH 64
#define MAX_LEAGUELENGTH 28
#define MAX_LISTBOXWIDTH 68
#define ART_BACK0 "menu/art/back_0"
#define ART_BACK1 "menu/art/back_1"
#define ART_CREATE0 "menu/art/create_0"
#define ART_CREATE1 "menu/art/create_1"
#define ART_SPECIFY0 "menu/art/specify_0"
#define ART_SPECIFY1 "menu/art/specify_1"
#define ART_REFRESH0 "menu/art/refresh_0"
#define ART_REFRESH1 "menu/art/refresh_1"
#define ART_CONNECT0 "menu/art/fight_0"
#define ART_CONNECT1 "menu/art/fight_1"
#define ART_ARROWS0 "menu/art/arrows_vert_0"
#define ART_ARROWS_UP "menu/art/arrows_vert_top"
#define ART_ARROWS_DOWN "menu/art/arrows_vert_bot"
#define ART_UNKNOWNMAP "menu/art/unknownmap"
#define ART_REMOVE0 "menu/art/delete_0"
#define ART_REMOVE1 "menu/art/delete_1"
#define ART_PUNKBUSTER "menu/art/pblogo"
#define ID_MASTER 10
#define ID_GAMETYPE 11
#define ID_SORTKEY 12
#define ID_SHOW_FULL 13
#define ID_SHOW_EMPTY 14
#define ID_LIST 15
#define ID_SCROLL_UP 16
#define ID_SCROLL_DOWN 17
#define ID_BACK 18
#define ID_REFRESH 19
#define ID_SPECIFY 20
#define ID_CREATE 21
#define ID_CONNECT 22
#define ID_REMOVE 23
#define ID_PUNKBUSTER 24
#define GR_LOGO 30
#define GR_LETTERS 31
#define UIAS_LOCAL 0
#define UIAS_GLOBAL0 1
#define UIAS_GLOBAL1 2
#define UIAS_GLOBAL2 3
#define UIAS_GLOBAL3 4
#define UIAS_GLOBAL4 5
#define UIAS_GLOBAL5 6
#define UIAS_FAVORITES 7
#define UI_MAX_MASTER_SERVERS 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",
"Internet",
"Master1",
"Master2",
"Master3",
"Master4",
"Master5",
"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[UI_MAX_MASTER_SERVERS][MAX_GLOBALSERVERS];
static int g_numglobalservers[UI_MAX_MASTER_SERVERS];
static servernode_t g_localserverlist[MAX_LOCALSERVERS];
static int g_numlocalservers;
static servernode_t g_favoriteserverlist[MAX_FAVORITESERVERS];
static int g_numfavoriteservers;
static int g_servertype;
static int g_gametype;
static int g_sortkey;
static int g_emptyservers;
static int g_fullservers;
/*
=================
ArenaServers_MaxPing
=================
*/
static int ArenaServers_MaxPing( void ) {
int maxPing;
maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
if( maxPing < 100 ) {
maxPing = 100;
}
return maxPing;
}
/*
=================
ArenaServers_Compare
=================
*/
static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
float f1;
float f2;
servernode_t* t1;
servernode_t* t2;
t1 = (servernode_t *)arg1;
t2 = (servernode_t *)arg2;
switch( g_sortkey ) {
case SORT_HOST:
return Q_stricmp( t1->hostname, t2->hostname );
case SORT_MAP:
return Q_stricmp( t1->mapname, t2->mapname );
case SORT_CLIENTS:
f1 = t1->maxclients - t1->numclients;
if( f1 < 0 ) {
f1 = 0;
}
f2 = t2->maxclients - t2->numclients;
if( f2 < 0 ) {
f2 = 0;
}
if( f1 < f2 ) {
return 1;
}
if( f1 == f2 ) {
return 0;
}
return -1;
case SORT_GAME:
if( t1->gametype < t2->gametype ) {
return -1;
}
if( t1->gametype == t2->gametype ) {
return 0;
}
return 1;
case SORT_PING:
if( t1->pingtime < t2->pingtime ) {
return -1;
}
if( t1->pingtime > t2->pingtime ) {
return 1;
}
return Q_stricmp( t1->hostname, t2->hostname );
}
return 0;
}
/*
=================
ArenaServers_SourceForLAN
Convert ui's g_servertype to AS_* used by trap calls.
=================
*/
int ArenaServers_SourceForLAN(void) {
switch( g_servertype ) {
default:
case UIAS_LOCAL:
return AS_LOCAL;
case UIAS_GLOBAL0:
case UIAS_GLOBAL1:
case UIAS_GLOBAL2:
case UIAS_GLOBAL3:
case UIAS_GLOBAL4:
case UIAS_GLOBAL5:
return AS_GLOBAL;
case UIAS_FAVORITES:
return AS_FAVORITES;
}
}
/*
=================
ArenaServers_Go
=================
*/
static void ArenaServers_Go( void ) {
servernode_t* servernode;
servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
if( servernode ) {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
}
}
/*
=================
ArenaServers_UpdatePicture
=================
*/
static void ArenaServers_UpdatePicture( void ) {
static char picname[64];
servernode_t* servernodeptr;
if( !g_arenaservers.list.numitems ) {
g_arenaservers.mappic.generic.name = NULL;
}
else {
servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
g_arenaservers.mappic.generic.name = picname;
}
// force shader update during draw
g_arenaservers.mappic.shader = 0;
}
/*
=================
ArenaServers_UpdateMenu
=================
*/
static void ArenaServers_UpdateMenu( void ) {
int i;
int j;
int count;
char* buff;
servernode_t* servernodeptr;
table_t* tableptr;
char *pingColor;
if( g_arenaservers.numqueriedservers > 0 ) {
// servers found
if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
// show progress
Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
g_arenaservers.statusbar.string = "Press SPACE to stop";
qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
}
else {
// all servers pinged - enable controls
g_arenaservers.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_GLOBAL0 && 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.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_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) {
g_arenaservers.statusbar.string = quake3worldMessage;
}
else {
g_arenaservers.statusbar.string = "";
}
// end of refresh - set control state
g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
g_arenaservers.list.generic.flags |= QMF_GRAYED;
g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
g_arenaservers.go.generic.flags |= QMF_GRAYED;
g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
}
// zero out list box
g_arenaservers.list.numitems = 0;
g_arenaservers.list.curvalue = 0;
g_arenaservers.list.top = 0;
// update picture
ArenaServers_UpdatePicture();
return;
}
// build list box strings - apply culling filters
servernodeptr = g_arenaservers.serverlist;
count = *g_arenaservers.numservers;
for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
tableptr = &g_arenaservers.table[j];
tableptr->servernode = servernodeptr;
buff = tableptr->buff;
// can only cull valid results
if( !g_emptyservers && !servernodeptr->numclients ) {
continue;
}
if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
continue;
}
switch( g_gametype ) {
case GAMES_ALL:
break;
case GAMES_FFA:
if( servernodeptr->gametype != GT_FFA ) {
continue;
}
break;
case GAMES_TEAMPLAY:
if( servernodeptr->gametype != GT_TEAM ) {
continue;
}
break;
case GAMES_TOURNEY:
if( servernodeptr->gametype != GT_TOURNAMENT ) {
continue;
}
break;
case GAMES_CTF:
if( servernodeptr->gametype != GT_CTF ) {
continue;
}
break;
}
if( servernodeptr->pingtime < servernodeptr->minPing ) {
pingColor = S_COLOR_BLUE;
}
else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
pingColor = S_COLOR_BLUE;
}
else if( servernodeptr->pingtime < 200 ) {
pingColor = S_COLOR_GREEN;
}
else if( servernodeptr->pingtime < 400 ) {
pingColor = S_COLOR_YELLOW;
}
else {
pingColor = S_COLOR_RED;
}
Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %4s%s%3d " S_COLOR_YELLOW "%s",
servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
servernodeptr->maxclients, servernodeptr->gamename,
netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
j++;
}
g_arenaservers.list.numitems = j;
g_arenaservers.list.curvalue = 0;
g_arenaservers.list.top = 0;
// update picture
ArenaServers_UpdatePicture();
}
/*
=================
ArenaServers_Remove
=================
*/
static void ArenaServers_Remove( void )
{
int i;
servernode_t* servernodeptr;
table_t* tableptr;
if (!g_arenaservers.list.numitems)
return;
// remove selected item from display list
// items are in scattered order due to sort and cull
// perform delete on list box contents, resync all lists
tableptr = &g_arenaservers.table[g_arenaservers.list.curvalue];
servernodeptr = tableptr->servernode;
// find address in master list
for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
{
if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
{
// delete address from master list
if (i < g_arenaservers.numfavoriteaddresses-1)
{
// shift items up
memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)* MAX_ADDRESSLENGTH );
}
g_arenaservers.numfavoriteaddresses--;
memset( &g_arenaservers.favoriteaddresses[g_arenaservers.numfavoriteaddresses], 0, MAX_ADDRESSLENGTH );
break;
}
}
// find address in server list
for (i=0; i<g_numfavoriteservers; i++)
{
if (&g_favoriteserverlist[i] == servernodeptr)
{
// delete address from server list
if (i < g_numfavoriteservers-1)
{
// shift items up
memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
}
g_numfavoriteservers--;
memset( &g_favoriteserverlist[ g_numfavoriteservers ], 0, sizeof(servernode_t));
break;
}
}
g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
g_arenaservers.currentping = g_arenaservers.numfavoriteaddresses;
}
/*
=================
ArenaServers_Insert
=================
*/
static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
{
servernode_t* servernodeptr;
char* s;
int i;
if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != UIAS_FAVORITES))
{
// slow global or local servers do not get entered
return;
}
if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
// list full;
servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
} else {
// next slot
servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
(*g_arenaservers.numservers)++;
}
Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
Q_CleanStr( servernodeptr->hostname );
Q_strupr( servernodeptr->hostname );
Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
Q_CleanStr( servernodeptr->mapname );
Q_strupr( servernodeptr->mapname );
servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
servernodeptr->pingtime = pingtime;
servernodeptr->minPing = atoi( Info_ValueForKey( info, "minPing") );
servernodeptr->maxPing = atoi( Info_ValueForKey( info, "maxPing") );
servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
/*
s = Info_ValueForKey( info, "nettype" );
for (i=0; ;i++)
{
if (!netnames[i])
{
servernodeptr->nettype = 0;
break;
}
else if (!Q_stricmp( netnames[i], s ))
{
servernodeptr->nettype = i;
break;
}
}
*/
servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
if (servernodeptr->nettype < 0 || servernodeptr->nettype >= ARRAY_LEN(netnames) - 1) {
servernodeptr->nettype = 0;
}
s = Info_ValueForKey( info, "game");
i = atoi( Info_ValueForKey( info, "gametype") );
if( i < 0 ) {
i = 0;
}
else if( i > 11 ) {
i = 12;
}
if( *s ) {
servernodeptr->gametype = i;//-1;
Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
}
else {
servernodeptr->gametype = i;
Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
}
}
/*
=================
ArenaServers_LoadFavorites
Load cvar address book entries into local lists.
=================
*/
void ArenaServers_LoadFavorites( void )
{
int i;
int j;
int numtempitems;
char adrstr[MAX_ADDRESSLENGTH];
servernode_t templist[MAX_FAVORITESERVERS];
qboolean found;
found = qfalse;
// copy the old
memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
numtempitems = g_numfavoriteservers;
// clear the current for sync
memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
g_numfavoriteservers = 0;
// resync existing results with new or deleted cvars
for (i=0; i<MAX_FAVORITESERVERS; i++)
{
trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
if (!adrstr[0])
continue;
// 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;
// final tally
if (g_arenaservers.numqueriedservers >= 0)
{
g_arenaservers.currentping = *g_arenaservers.numservers;
g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
}
// sort
qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
ArenaServers_UpdateMenu();
}
/*
=================
ArenaServers_DoRefresh
=================
*/
static void ArenaServers_DoRefresh( void )
{
int i;
int j;
int time;
int maxPing;
char adrstr[MAX_ADDRESSLENGTH];
char info[MAX_INFO_STRING];
if (uis.realtime < g_arenaservers.refreshtime)
{
if (g_servertype != UIAS_FAVORITES) {
if (g_servertype == UIAS_LOCAL) {
if (!trap_LAN_GetServerCount(AS_LOCAL)) {
return;
}
}
if (trap_LAN_GetServerCount(ArenaServers_SourceForLAN()) < 0) {
// still waiting for response
return;
}
}
} else if (g_servertype == UIAS_LOCAL) {
if (!trap_LAN_GetServerCount(AS_LOCAL)) {
// no local servers found, check again
trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
g_arenaservers.refreshtime = uis.realtime + 5000;
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;
// set hostname for nonresponsive favorite server
if (g_servertype == UIAS_FAVORITES) {
Info_SetValueForKey( info, "hostname", adrstr );
Info_SetValueForKey( info, "game", "???" );
}
}
else
{
trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
}
// insert ping results
ArenaServers_Insert( adrstr, info, time );
// clear this query from internal list
g_arenaservers.pinglist[j].adrstr[0] = '\0';
}
// clear this query from external list
trap_LAN_ClearPing( i );
}
// get results of servers query
// counts can increase as servers respond
if (g_servertype == UIAS_FAVORITES) {
g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
} else {
g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(ArenaServers_SourceForLAN());
}
// if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
// g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
// send ping requests in reasonable bursts
// iterate ping through all found servers
for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
{
if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
{
// ping queue is full
break;
}
// find empty slot
for (j=0; j<MAX_PINGREQUESTS; j++)
if (!g_arenaservers.pinglist[j].adrstr[0])
break;
if (j >= MAX_PINGREQUESTS)
// no empty slots available yet - wait for timeout
break;
// get an address to ping
if (g_servertype == UIAS_FAVORITES) {
strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );
} else {
trap_LAN_GetServerAddressString(ArenaServers_SourceForLAN(), g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
}
strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
g_arenaservers.pinglist[j].start = uis.realtime;
trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr ) );
// advance to next server
g_arenaservers.currentping++;
}
if (!trap_LAN_GetPingQueueCount())
{
// all pings completed
ArenaServers_StopRefresh();
return;
}
// update the user interface with ping status
ArenaServers_UpdateMenu();
}
/*
=================
ArenaServers_StartRefresh
=================
*/
static void ArenaServers_StartRefresh( void )
{
int i;
char myargs[32], protocol[32];
memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
for (i=0; i<MAX_PINGREQUESTS; i++)
{
g_arenaservers.pinglist[i].adrstr[0] = '\0';
trap_LAN_ClearPing( i );
}
g_arenaservers.refreshservers = qtrue;
g_arenaservers.currentping = 0;
g_arenaservers.nextpingtime = 0;
*g_arenaservers.numservers = 0;
g_arenaservers.numqueriedservers = 0;
// allow max 5 seconds for responses
g_arenaservers.refreshtime = uis.realtime + 5000;
// place menu in zeroed state
ArenaServers_UpdateMenu();
if( g_servertype == UIAS_LOCAL ) {
trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
return;
}
if( g_servertype >= UIAS_GLOBAL0 && 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 - UIAS_GLOBAL0, protocol, myargs ));
}
else {
trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - UIAS_GLOBAL0, (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 )
{
ArenaServers_StopRefresh();
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 - UIAS_GLOBAL0);
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_GLOBAL0:
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[type-UIAS_GLOBAL0];
g_arenaservers.numservers = &g_numglobalservers[type-UIAS_GLOBAL0];
g_arenaservers.maxservers = MAX_GLOBALSERVERS;
break;
case UIAS_FAVORITES:
g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
g_arenaservers.serverlist = g_favoriteserverlist;
g_arenaservers.numservers = &g_numfavoriteservers;
g_arenaservers.maxservers = MAX_FAVORITESERVERS;
break;
}
if( !*g_arenaservers.numservers ) {
ArenaServers_StartRefresh();
}
else {
// avoid slow operation, use existing results
g_arenaservers.currentping = *g_arenaservers.numservers;
g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
ArenaServers_UpdateMenu();
strcpy(g_arenaservers.status.string,"hit refresh to update");
}
return type;
}
/*
=================
PunkBuster_Confirm
=================
*/
static void Punkbuster_ConfirmEnable( qboolean result ) {
if (result)
{
trap_SetPbClStatus(1);
}
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
}
static void Punkbuster_ConfirmDisable( qboolean result ) {
if (result)
{
trap_SetPbClStatus(0);
UI_Message( punkbuster_msg );
}
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
}
/*
=================
ArenaServers_Event
=================
*/
static void ArenaServers_Event( void* ptr, int event ) {
int id;
id = ((menucommon_s*)ptr)->id;
if( event != QM_ACTIVATED && id != ID_LIST ) {
return;
}
switch( id ) {
case ID_MASTER:
g_arenaservers.master.curvalue = ArenaServers_SetType(g_arenaservers.master.curvalue);
trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue);
break;
case ID_GAMETYPE:
trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
g_gametype = g_arenaservers.gametype.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_SORTKEY:
trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
ArenaServers_UpdateMenu();
break;
case ID_SHOW_FULL:
trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
g_fullservers = g_arenaservers.showfull.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_SHOW_EMPTY:
trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
g_emptyservers = g_arenaservers.showempty.curvalue;
ArenaServers_UpdateMenu();
break;
case ID_LIST:
if( event == QM_GOTFOCUS ) {
ArenaServers_UpdatePicture();
}
break;
case ID_SCROLL_UP:
ScrollList_Key( &g_arenaservers.list, K_UPARROW );
break;
case ID_SCROLL_DOWN:
ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
break;
case ID_BACK:
ArenaServers_StopRefresh();
ArenaServers_SaveChanges();
UI_PopMenu();
break;
case ID_REFRESH:
ArenaServers_StartRefresh();
break;
case ID_SPECIFY:
UI_SpecifyServerMenu();
break;
case ID_CREATE:
UI_StartServerMenu( qtrue );
break;
case ID_CONNECT:
ArenaServers_Go();
break;
case ID_REMOVE:
ArenaServers_Remove();
ArenaServers_UpdateMenu();
break;
case ID_PUNKBUSTER:
if (g_arenaservers.punkbuster.curvalue)
{
UI_ConfirmMenu_Style( "Enable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmEnable );
}
else
{
UI_ConfirmMenu_Style( "Disable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmDisable );
}
break;
}
}
/*
=================
ArenaServers_MenuDraw
=================
*/
static void ArenaServers_MenuDraw( void )
{
if (g_arenaservers.refreshservers)
ArenaServers_DoRefresh();
Menu_Draw( &g_arenaservers.menu );
}
/*
=================
ArenaServers_MenuKey
=================
*/
static sfxHandle_t ArenaServers_MenuKey( int key ) {
if( key == K_SPACE && g_arenaservers.refreshservers ) {
ArenaServers_StopRefresh();
return menu_move_sound;
}
if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == UIAS_FAVORITES ) &&
( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
ArenaServers_Remove();
ArenaServers_UpdateMenu();
return menu_move_sound;
}
if( key == K_MOUSE2 || key == K_ESCAPE ) {
ArenaServers_StopRefresh();
ArenaServers_SaveChanges();
}
return Menu_DefaultKey( &g_arenaservers.menu, key );
}
/*
=================
ArenaServers_MenuInit
=================
*/
static void ArenaServers_MenuInit( void ) {
int i;
int y;
static char statusbuffer[MAX_STATUSLENGTH];
// zero set all our globals
memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
ArenaServers_Cache();
g_arenaservers.menu.fullscreen = qtrue;
g_arenaservers.menu.wrapAround = qtrue;
g_arenaservers.menu.draw = ArenaServers_MenuDraw;
g_arenaservers.menu.key = ArenaServers_MenuKey;
g_arenaservers.banner.generic.type = MTYPE_BTEXT;
g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
g_arenaservers.banner.generic.x = 320;
g_arenaservers.banner.generic.y = 16;
g_arenaservers.banner.string = "ARENA SERVERS";
g_arenaservers.banner.style = UI_CENTER;
g_arenaservers.banner.color = color_white;
y = 80;
g_arenaservers.master.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.master.generic.name = "Servers:";
g_arenaservers.master.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.master.generic.callback = ArenaServers_Event;
g_arenaservers.master.generic.id = ID_MASTER;
g_arenaservers.master.generic.x = 320;
g_arenaservers.master.generic.y = y;
g_arenaservers.master.itemnames = master_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.gametype.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.gametype.generic.name = "Game Type:";
g_arenaservers.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.gametype.generic.callback = ArenaServers_Event;
g_arenaservers.gametype.generic.id = ID_GAMETYPE;
g_arenaservers.gametype.generic.x = 320;
g_arenaservers.gametype.generic.y = y;
g_arenaservers.gametype.itemnames = servertype_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.sortkey.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.sortkey.generic.name = "Sort By:";
g_arenaservers.sortkey.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.sortkey.generic.callback = ArenaServers_Event;
g_arenaservers.sortkey.generic.id = ID_SORTKEY;
g_arenaservers.sortkey.generic.x = 320;
g_arenaservers.sortkey.generic.y = y;
g_arenaservers.sortkey.itemnames = sortkey_items;
y += SMALLCHAR_HEIGHT;
g_arenaservers.showfull.generic.type = MTYPE_RADIOBUTTON;
g_arenaservers.showfull.generic.name = "Show Full:";
g_arenaservers.showfull.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.showfull.generic.callback = ArenaServers_Event;
g_arenaservers.showfull.generic.id = ID_SHOW_FULL;
g_arenaservers.showfull.generic.x = 320;
g_arenaservers.showfull.generic.y = y;
y += SMALLCHAR_HEIGHT;
g_arenaservers.showempty.generic.type = MTYPE_RADIOBUTTON;
g_arenaservers.showempty.generic.name = "Show Empty:";
g_arenaservers.showempty.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.showempty.generic.callback = ArenaServers_Event;
g_arenaservers.showempty.generic.id = ID_SHOW_EMPTY;
g_arenaservers.showempty.generic.x = 320;
g_arenaservers.showempty.generic.y = y;
y += 3 * SMALLCHAR_HEIGHT;
g_arenaservers.list.generic.type = MTYPE_SCROLLLIST;
g_arenaservers.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;
g_arenaservers.list.generic.id = ID_LIST;
g_arenaservers.list.generic.callback = ArenaServers_Event;
g_arenaservers.list.generic.x = 72;
g_arenaservers.list.generic.y = y;
g_arenaservers.list.width = MAX_LISTBOXWIDTH;
g_arenaservers.list.height = 11;
g_arenaservers.list.itemnames = (const char **)g_arenaservers.items;
for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
g_arenaservers.items[i] = g_arenaservers.table[i].buff;
}
g_arenaservers.mappic.generic.type = MTYPE_BITMAP;
g_arenaservers.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.mappic.generic.x = 72;
g_arenaservers.mappic.generic.y = 80;
g_arenaservers.mappic.width = 128;
g_arenaservers.mappic.height = 96;
g_arenaservers.mappic.errorpic = ART_UNKNOWNMAP;
g_arenaservers.arrows.generic.type = MTYPE_BITMAP;
g_arenaservers.arrows.generic.name = ART_ARROWS0;
g_arenaservers.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.arrows.generic.callback = ArenaServers_Event;
g_arenaservers.arrows.generic.x = 512+48;
g_arenaservers.arrows.generic.y = 240-64+16;
g_arenaservers.arrows.width = 64;
g_arenaservers.arrows.height = 128;
g_arenaservers.up.generic.type = MTYPE_BITMAP;
g_arenaservers.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
g_arenaservers.up.generic.callback = ArenaServers_Event;
g_arenaservers.up.generic.id = ID_SCROLL_UP;
g_arenaservers.up.generic.x = 512+48;
g_arenaservers.up.generic.y = 240-64+16;
g_arenaservers.up.width = 64;
g_arenaservers.up.height = 64;
g_arenaservers.up.focuspic = ART_ARROWS_UP;
g_arenaservers.down.generic.type = MTYPE_BITMAP;
g_arenaservers.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
g_arenaservers.down.generic.callback = ArenaServers_Event;
g_arenaservers.down.generic.id = ID_SCROLL_DOWN;
g_arenaservers.down.generic.x = 512+48;
g_arenaservers.down.generic.y = 240+16;
g_arenaservers.down.width = 64;
g_arenaservers.down.height = 64;
g_arenaservers.down.focuspic = ART_ARROWS_DOWN;
y = 376;
g_arenaservers.status.generic.type = MTYPE_TEXT;
g_arenaservers.status.generic.x = 320;
g_arenaservers.status.generic.y = y;
g_arenaservers.status.string = statusbuffer;
g_arenaservers.status.style = UI_CENTER|UI_SMALLFONT;
g_arenaservers.status.color = menu_text_color;
y += SMALLCHAR_HEIGHT;
g_arenaservers.statusbar.generic.type = MTYPE_TEXT;
g_arenaservers.statusbar.generic.x = 320;
g_arenaservers.statusbar.generic.y = y;
g_arenaservers.statusbar.string = "";
g_arenaservers.statusbar.style = UI_CENTER|UI_SMALLFONT;
g_arenaservers.statusbar.color = text_color_normal;
g_arenaservers.remove.generic.type = MTYPE_BITMAP;
g_arenaservers.remove.generic.name = ART_REMOVE0;
g_arenaservers.remove.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.remove.generic.callback = ArenaServers_Event;
g_arenaservers.remove.generic.id = ID_REMOVE;
g_arenaservers.remove.generic.x = 450;
g_arenaservers.remove.generic.y = 86;
g_arenaservers.remove.width = 96;
g_arenaservers.remove.height = 48;
g_arenaservers.remove.focuspic = ART_REMOVE1;
g_arenaservers.back.generic.type = MTYPE_BITMAP;
g_arenaservers.back.generic.name = ART_BACK0;
g_arenaservers.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.back.generic.callback = ArenaServers_Event;
g_arenaservers.back.generic.id = ID_BACK;
g_arenaservers.back.generic.x = 0;
g_arenaservers.back.generic.y = 480-64;
g_arenaservers.back.width = 128;
g_arenaservers.back.height = 64;
g_arenaservers.back.focuspic = ART_BACK1;
g_arenaservers.specify.generic.type = MTYPE_BITMAP;
g_arenaservers.specify.generic.name = ART_SPECIFY0;
g_arenaservers.specify.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.specify.generic.callback = ArenaServers_Event;
g_arenaservers.specify.generic.id = ID_SPECIFY;
g_arenaservers.specify.generic.x = 128;
g_arenaservers.specify.generic.y = 480-64;
g_arenaservers.specify.width = 128;
g_arenaservers.specify.height = 64;
g_arenaservers.specify.focuspic = ART_SPECIFY1;
g_arenaservers.refresh.generic.type = MTYPE_BITMAP;
g_arenaservers.refresh.generic.name = ART_REFRESH0;
g_arenaservers.refresh.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.refresh.generic.callback = ArenaServers_Event;
g_arenaservers.refresh.generic.id = ID_REFRESH;
g_arenaservers.refresh.generic.x = 256;
g_arenaservers.refresh.generic.y = 480-64;
g_arenaservers.refresh.width = 128;
g_arenaservers.refresh.height = 64;
g_arenaservers.refresh.focuspic = ART_REFRESH1;
g_arenaservers.create.generic.type = MTYPE_BITMAP;
g_arenaservers.create.generic.name = ART_CREATE0;
g_arenaservers.create.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.create.generic.callback = ArenaServers_Event;
g_arenaservers.create.generic.id = ID_CREATE;
g_arenaservers.create.generic.x = 384;
g_arenaservers.create.generic.y = 480-64;
g_arenaservers.create.width = 128;
g_arenaservers.create.height = 64;
g_arenaservers.create.focuspic = ART_CREATE1;
g_arenaservers.go.generic.type = MTYPE_BITMAP;
g_arenaservers.go.generic.name = ART_CONNECT0;
g_arenaservers.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
g_arenaservers.go.generic.callback = ArenaServers_Event;
g_arenaservers.go.generic.id = ID_CONNECT;
g_arenaservers.go.generic.x = 640;
g_arenaservers.go.generic.y = 480-64;
g_arenaservers.go.width = 128;
g_arenaservers.go.height = 64;
g_arenaservers.go.focuspic = ART_CONNECT1;
g_arenaservers.punkbuster.generic.type = MTYPE_SPINCONTROL;
g_arenaservers.punkbuster.generic.name = "Punkbuster:";
g_arenaservers.punkbuster.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
g_arenaservers.punkbuster.generic.callback = ArenaServers_Event;
g_arenaservers.punkbuster.generic.id = ID_PUNKBUSTER;
g_arenaservers.punkbuster.generic.x = 480+32;
g_arenaservers.punkbuster.generic.y = 144;
g_arenaservers.punkbuster.itemnames = punkbuster_items;
g_arenaservers.pblogo.generic.type = MTYPE_BITMAP;
g_arenaservers.pblogo.generic.name = ART_PUNKBUSTER;
g_arenaservers.pblogo.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
g_arenaservers.pblogo.generic.x = 526;
g_arenaservers.pblogo.generic.y = 176;
g_arenaservers.pblogo.width = 32;
g_arenaservers.pblogo.height = 16;
g_arenaservers.pblogo.errorpic = ART_UNKNOWNMAP;
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.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.list );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
ArenaServers_LoadFavorites();
g_arenaservers.master.curvalue = g_servertype = Com_Clamp( 0, 6, ui_browserMaster.integer );
g_gametype = Com_Clamp( 0, 4, ui_browserGameType.integer );
g_arenaservers.gametype.curvalue = g_gametype;
g_sortkey = Com_Clamp( 0, 4, ui_browserSortKey.integer );
g_arenaservers.sortkey.curvalue = g_sortkey;
g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
g_arenaservers.showfull.curvalue = g_fullservers;
g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
g_arenaservers.showempty.curvalue = g_emptyservers;
g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
// force to initial state and refresh
g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype);
trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
}
/*
=================
ArenaServers_Cache
=================
*/
void ArenaServers_Cache( void ) {
trap_R_RegisterShaderNoMip( ART_BACK0 );
trap_R_RegisterShaderNoMip( ART_BACK1 );
trap_R_RegisterShaderNoMip( ART_CREATE0 );
trap_R_RegisterShaderNoMip( ART_CREATE1 );
trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
trap_R_RegisterShaderNoMip( ART_REFRESH0 );
trap_R_RegisterShaderNoMip( ART_REFRESH1 );
trap_R_RegisterShaderNoMip( ART_CONNECT0 );
trap_R_RegisterShaderNoMip( ART_CONNECT1 );
trap_R_RegisterShaderNoMip( ART_ARROWS0 );
trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
}
/*
=================
UI_ArenaServersMenu
=================
*/
void UI_ArenaServersMenu( void ) {
ArenaServers_MenuInit();
UI_PushMenu( &g_arenaservers.menu );
}