Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ui_servers2.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 /*
00024 =======================================================================
00025 
00026 MULTIPLAYER MENU (SERVER BROWSER)
00027 
00028 =======================================================================
00029 */
00030 
00031 
00032 #include "ui_local.h"
00033 
00034 
00035 #define MAX_GLOBALSERVERS       128
00036 #define MAX_PINGREQUESTS        32
00037 #define MAX_ADDRESSLENGTH       64
00038 #define MAX_HOSTNAMELENGTH      22
00039 #define MAX_MAPNAMELENGTH       16
00040 #define MAX_LISTBOXITEMS        128
00041 #define MAX_LOCALSERVERS        128
00042 #define MAX_STATUSLENGTH        64
00043 #define MAX_LEAGUELENGTH        28
00044 #define MAX_LISTBOXWIDTH        68
00045 
00046 #define ART_BACK0               "menu/art/back_0"
00047 #define ART_BACK1               "menu/art/back_1"
00048 #define ART_CREATE0             "menu/art/create_0"
00049 #define ART_CREATE1             "menu/art/create_1"
00050 #define ART_SPECIFY0            "menu/art/specify_0"
00051 #define ART_SPECIFY1            "menu/art/specify_1"
00052 #define ART_REFRESH0            "menu/art/refresh_0"
00053 #define ART_REFRESH1            "menu/art/refresh_1"
00054 #define ART_CONNECT0            "menu/art/fight_0"
00055 #define ART_CONNECT1            "menu/art/fight_1"
00056 #define ART_ARROWS0             "menu/art/arrows_vert_0"
00057 #define ART_ARROWS_UP           "menu/art/arrows_vert_top"
00058 #define ART_ARROWS_DOWN         "menu/art/arrows_vert_bot"
00059 #define ART_UNKNOWNMAP          "menu/art/unknownmap"
00060 #define ART_REMOVE0             "menu/art/delete_0"
00061 #define ART_REMOVE1             "menu/art/delete_1"
00062 #define ART_PUNKBUSTER      "menu/art/pblogo"
00063 
00064 #define ID_MASTER           10
00065 #define ID_GAMETYPE         11
00066 #define ID_SORTKEY          12
00067 #define ID_SHOW_FULL        13
00068 #define ID_SHOW_EMPTY       14
00069 #define ID_LIST             15
00070 #define ID_SCROLL_UP        16
00071 #define ID_SCROLL_DOWN      17
00072 #define ID_BACK             18
00073 #define ID_REFRESH          19
00074 #define ID_SPECIFY          20
00075 #define ID_CREATE           21
00076 #define ID_CONNECT          22
00077 #define ID_REMOVE           23
00078 #define ID_PUNKBUSTER 24
00079 
00080 #define GR_LOGO             30
00081 #define GR_LETTERS          31
00082 
00083 #define AS_LOCAL            0
00084 #define AS_MPLAYER          1
00085 #define AS_GLOBAL           2
00086 #define AS_FAVORITES        3
00087 
00088 #define SORT_HOST           0
00089 #define SORT_MAP            1
00090 #define SORT_CLIENTS        2
00091 #define SORT_GAME           3
00092 #define SORT_PING           4
00093 
00094 #define GAMES_ALL           0
00095 #define GAMES_FFA           1
00096 #define GAMES_TEAMPLAY      2
00097 #define GAMES_TOURNEY       3
00098 #define GAMES_CTF           4
00099 
00100 static const char *master_items[] = {
00101     "Local",
00102     "Internet",
00103     "Favorites",
00104     0
00105 };
00106 
00107 static const char *servertype_items[] = {
00108     "All",
00109     "Free For All",
00110     "Team Deathmatch",
00111     "Tournament",
00112     "Capture the Flag",
00113     0
00114 };
00115 
00116 static const char *sortkey_items[] = {
00117     "Server Name",
00118     "Map Name",
00119     "Open Player Spots",
00120     "Game Type",
00121     "Ping Time",
00122     0
00123 };
00124 
00125 static char* gamenames[] = {
00126     "DM ",  // deathmatch
00127     "1v1",  // tournament
00128     "SP ",  // single player
00129     "Team DM",  // team deathmatch
00130     "CTF",  // capture the flag
00131     "One Flag CTF",     // one flag ctf
00132     "OverLoad",             // Overload
00133     "Harvester",            // Harvester
00134     "Rocket Arena 3",   // Rocket Arena 3
00135     "Q3F",                      // Q3F
00136     "Urban Terror",     // Urban Terror
00137     "OSP",                      // Orange Smoothie Productions
00138     "???",          // unknown
00139     0
00140 };
00141 
00142 static char* netnames[] = {
00143     "???",
00144     "UDP",
00145     "IPX",
00146     NULL
00147 };
00148 
00149 static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
00150 
00151 const char* punkbuster_items[] = {
00152     "Disabled",
00153     "Enabled",
00154     NULL
00155 };
00156 
00157 const char* punkbuster_msg[] = {
00158     "PunkBuster will be",
00159     "disabled the next time",
00160     "Quake III Arena",
00161     "is started.",
00162     NULL
00163 };
00164 
00165 typedef struct {
00166     char    adrstr[MAX_ADDRESSLENGTH];
00167     int     start;
00168 } pinglist_t;
00169 
00170 typedef struct servernode_s {
00171     char    adrstr[MAX_ADDRESSLENGTH];
00172     char    hostname[MAX_HOSTNAMELENGTH+3];
00173     char    mapname[MAX_MAPNAMELENGTH];
00174     int     numclients;
00175     int     maxclients;
00176     int     pingtime;
00177     int     gametype;
00178     char    gamename[12];
00179     int     nettype;
00180     int     minPing;
00181     int     maxPing;
00182     qboolean bPB;
00183 
00184 } servernode_t; 
00185 
00186 typedef struct {
00187     char            buff[MAX_LISTBOXWIDTH];
00188     servernode_t*   servernode;
00189 } table_t;
00190 
00191 typedef struct {
00192     menuframework_s     menu;
00193 
00194     menutext_s          banner;
00195 
00196     menulist_s          master;
00197     menulist_s          gametype;
00198     menulist_s          sortkey;
00199     menuradiobutton_s   showfull;
00200     menuradiobutton_s   showempty;
00201 
00202     menulist_s          list;
00203     menubitmap_s        mappic;
00204     menubitmap_s        arrows;
00205     menubitmap_s        up;
00206     menubitmap_s        down;
00207     menutext_s          status;
00208     menutext_s          statusbar;
00209 
00210     menubitmap_s        remove;
00211     menubitmap_s        back;
00212     menubitmap_s        refresh;
00213     menubitmap_s        specify;
00214     menubitmap_s        create;
00215     menubitmap_s        go;
00216 
00217     pinglist_t          pinglist[MAX_PINGREQUESTS];
00218     table_t             table[MAX_LISTBOXITEMS];
00219     char*               items[MAX_LISTBOXITEMS];
00220     int                 numqueriedservers;
00221     int                 *numservers;
00222     servernode_t        *serverlist;    
00223     int                 currentping;
00224     qboolean            refreshservers;
00225     int                 nextpingtime;
00226     int                 maxservers;
00227     int                 refreshtime;
00228     char                favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
00229     int                 numfavoriteaddresses;
00230 
00231     menulist_s      punkbuster;
00232     menubitmap_s    pblogo;
00233 } arenaservers_t;
00234 
00235 static arenaservers_t   g_arenaservers;
00236 
00237 
00238 static servernode_t     g_globalserverlist[MAX_GLOBALSERVERS];
00239 static int              g_numglobalservers;
00240 static servernode_t     g_localserverlist[MAX_LOCALSERVERS];
00241 static int              g_numlocalservers;
00242 static servernode_t     g_favoriteserverlist[MAX_FAVORITESERVERS];
00243 static int              g_numfavoriteservers;
00244 static servernode_t     g_mplayerserverlist[MAX_GLOBALSERVERS];
00245 static int              g_nummplayerservers;
00246 static int              g_servertype;
00247 static int              g_gametype;
00248 static int              g_sortkey;
00249 static int              g_emptyservers;
00250 static int              g_fullservers;
00251 
00252 
00253 /*
00254 =================
00255 ArenaServers_MaxPing
00256 =================
00257 */
00258 static int ArenaServers_MaxPing( void ) {
00259     int     maxPing;
00260 
00261     maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
00262     if( maxPing < 100 ) {
00263         maxPing = 100;
00264     }
00265     return maxPing;
00266 }
00267 
00268 
00269 /*
00270 =================
00271 ArenaServers_Compare
00272 =================
00273 */
00274 static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
00275     float           f1;
00276     float           f2;
00277     servernode_t*   t1;
00278     servernode_t*   t2;
00279 
00280     t1 = (servernode_t *)arg1;
00281     t2 = (servernode_t *)arg2;
00282 
00283     switch( g_sortkey ) {
00284     case SORT_HOST:
00285         return Q_stricmp( t1->hostname, t2->hostname );
00286 
00287     case SORT_MAP:
00288         return Q_stricmp( t1->mapname, t2->mapname );
00289 
00290     case SORT_CLIENTS:
00291         f1 = t1->maxclients - t1->numclients;
00292         if( f1 < 0 ) {
00293             f1 = 0;
00294         }
00295 
00296         f2 = t2->maxclients - t2->numclients;
00297         if( f2 < 0 ) {
00298             f2 = 0;
00299         }
00300 
00301         if( f1 < f2 ) {
00302             return 1;
00303         }
00304         if( f1 == f2 ) {
00305             return 0;
00306         }
00307         return -1;
00308 
00309     case SORT_GAME:
00310         if( t1->gametype < t2->gametype ) {
00311             return -1;
00312         }
00313         if( t1->gametype == t2->gametype ) {
00314             return 0;
00315         }
00316         return 1;
00317 
00318     case SORT_PING:
00319         if( t1->pingtime < t2->pingtime ) {
00320             return -1;
00321         }
00322         if( t1->pingtime > t2->pingtime ) {
00323             return 1;
00324         }
00325         return Q_stricmp( t1->hostname, t2->hostname );
00326     }
00327 
00328     return 0;
00329 }
00330 
00331 
00332 /*
00333 =================
00334 ArenaServers_Go
00335 =================
00336 */
00337 static void ArenaServers_Go( void ) {
00338     servernode_t*   servernode;
00339 
00340     servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
00341     if( servernode ) {
00342         trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
00343     }
00344 }
00345 
00346 
00347 /*
00348 =================
00349 ArenaServers_UpdatePicture
00350 =================
00351 */
00352 static void ArenaServers_UpdatePicture( void ) {
00353     static char     picname[64];
00354     servernode_t*   servernodeptr;
00355 
00356     if( !g_arenaservers.list.numitems ) {
00357         g_arenaservers.mappic.generic.name = NULL;
00358     }
00359     else {
00360         servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
00361         Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
00362         g_arenaservers.mappic.generic.name = picname;
00363     
00364     }
00365 
00366     // force shader update during draw
00367     g_arenaservers.mappic.shader = 0;
00368 }
00369 
00370 
00371 /*
00372 =================
00373 ArenaServers_UpdateMenu
00374 =================
00375 */
00376 static void ArenaServers_UpdateMenu( void ) {
00377     int             i;
00378     int             j;
00379     int             count;
00380     char*           buff;
00381     servernode_t*   servernodeptr;
00382     table_t*        tableptr;
00383     char            *pingColor;
00384 
00385     if( g_arenaservers.numqueriedservers > 0 ) {
00386         // servers found
00387         if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
00388             // show progress
00389             Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
00390             g_arenaservers.statusbar.string  = "Press SPACE to stop";
00391             qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
00392         }
00393         else {
00394             // all servers pinged - enable controls
00395             g_arenaservers.master.generic.flags     &= ~QMF_GRAYED;
00396             g_arenaservers.gametype.generic.flags   &= ~QMF_GRAYED;
00397             g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
00398             g_arenaservers.showempty.generic.flags  &= ~QMF_GRAYED;
00399             g_arenaservers.showfull.generic.flags   &= ~QMF_GRAYED;
00400             g_arenaservers.list.generic.flags       &= ~QMF_GRAYED;
00401             g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
00402             g_arenaservers.go.generic.flags         &= ~QMF_GRAYED;
00403             g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
00404 
00405             // update status bar
00406             if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
00407                 g_arenaservers.statusbar.string = quake3worldMessage;
00408             }
00409             else {
00410                 g_arenaservers.statusbar.string = "";
00411             }
00412 
00413         }
00414     }
00415     else {
00416         // no servers found
00417         if( g_arenaservers.refreshservers ) {
00418             strcpy( g_arenaservers.status.string,"Scanning For Servers." );
00419             g_arenaservers.statusbar.string = "Press SPACE to stop";
00420 
00421             // disable controls during refresh
00422             g_arenaservers.master.generic.flags     |= QMF_GRAYED;
00423             g_arenaservers.gametype.generic.flags   |= QMF_GRAYED;
00424             g_arenaservers.sortkey.generic.flags    |= QMF_GRAYED;
00425             g_arenaservers.showempty.generic.flags  |= QMF_GRAYED;
00426             g_arenaservers.showfull.generic.flags   |= QMF_GRAYED;
00427             g_arenaservers.list.generic.flags       |= QMF_GRAYED;
00428             g_arenaservers.refresh.generic.flags    |= QMF_GRAYED;
00429             g_arenaservers.go.generic.flags         |= QMF_GRAYED;
00430             g_arenaservers.punkbuster.generic.flags |= QMF_GRAYED;
00431         }
00432         else {
00433             if( g_arenaservers.numqueriedservers < 0 ) {
00434                 strcpy(g_arenaservers.status.string,"No Response From Master Server." );
00435             }
00436             else {
00437                 strcpy(g_arenaservers.status.string,"No Servers Found." );
00438             }
00439 
00440             // update status bar
00441             if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
00442                 g_arenaservers.statusbar.string = quake3worldMessage;
00443             }
00444             else {
00445                 g_arenaservers.statusbar.string = "";
00446             }
00447 
00448             // end of refresh - set control state
00449             g_arenaservers.master.generic.flags     &= ~QMF_GRAYED;
00450             g_arenaservers.gametype.generic.flags   &= ~QMF_GRAYED;
00451             g_arenaservers.sortkey.generic.flags    &= ~QMF_GRAYED;
00452             g_arenaservers.showempty.generic.flags  &= ~QMF_GRAYED;
00453             g_arenaservers.showfull.generic.flags   &= ~QMF_GRAYED;
00454             g_arenaservers.list.generic.flags       |= QMF_GRAYED;
00455             g_arenaservers.refresh.generic.flags    &= ~QMF_GRAYED;
00456             g_arenaservers.go.generic.flags         |= QMF_GRAYED;
00457             g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
00458         }
00459 
00460         // zero out list box
00461         g_arenaservers.list.numitems = 0;
00462         g_arenaservers.list.curvalue = 0;
00463         g_arenaservers.list.top      = 0;
00464 
00465         // update picture
00466         ArenaServers_UpdatePicture();
00467         return;
00468     }
00469 
00470     // build list box strings - apply culling filters
00471     servernodeptr = g_arenaservers.serverlist;
00472     count         = *g_arenaservers.numservers;
00473     for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
00474         tableptr = &g_arenaservers.table[j];
00475         tableptr->servernode = servernodeptr;
00476         buff = tableptr->buff;
00477 
00478         // can only cull valid results
00479         if( !g_emptyservers && !servernodeptr->numclients ) {
00480             continue;
00481         }
00482 
00483         if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
00484             continue;
00485         }
00486 
00487         switch( g_gametype ) {
00488         case GAMES_ALL:
00489             break;
00490 
00491         case GAMES_FFA:
00492             if( servernodeptr->gametype != GT_FFA ) {
00493                 continue;
00494             }
00495             break;
00496 
00497         case GAMES_TEAMPLAY:
00498             if( servernodeptr->gametype != GT_TEAM ) {
00499                 continue;
00500             }
00501             break;
00502 
00503         case GAMES_TOURNEY:
00504             if( servernodeptr->gametype != GT_TOURNAMENT ) {
00505                 continue;
00506             }
00507             break;
00508 
00509         case GAMES_CTF:
00510             if( servernodeptr->gametype != GT_CTF ) {
00511                 continue;
00512             }
00513             break;
00514         }
00515 
00516         if( servernodeptr->pingtime < servernodeptr->minPing ) {
00517             pingColor = S_COLOR_BLUE;
00518         }
00519         else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
00520             pingColor = S_COLOR_BLUE;
00521         }
00522         else if( servernodeptr->pingtime < 200 ) {
00523             pingColor = S_COLOR_GREEN;
00524         }
00525         else if( servernodeptr->pingtime < 400 ) {
00526             pingColor = S_COLOR_YELLOW;
00527         }
00528         else {
00529             pingColor = S_COLOR_RED;
00530         }
00531 
00532         Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %3s %s%3d " S_COLOR_YELLOW "%s", 
00533             servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
00534             servernodeptr->maxclients, servernodeptr->gamename,
00535             netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
00536         j++;
00537     }
00538 
00539     g_arenaservers.list.numitems = j;
00540     g_arenaservers.list.curvalue = 0;
00541     g_arenaservers.list.top      = 0;
00542 
00543     // update picture
00544     ArenaServers_UpdatePicture();
00545 }
00546 
00547 
00548 /*
00549 =================
00550 ArenaServers_Remove
00551 =================
00552 */
00553 static void ArenaServers_Remove( void )
00554 {
00555     int             i;
00556     servernode_t*   servernodeptr;
00557     table_t*        tableptr;
00558 
00559     if (!g_arenaservers.list.numitems)
00560         return;
00561 
00562     // remove selected item from display list
00563     // items are in scattered order due to sort and cull
00564     // perform delete on list box contents, resync all lists
00565 
00566     tableptr      = &g_arenaservers.table[g_arenaservers.list.curvalue];
00567     servernodeptr = tableptr->servernode;
00568 
00569     // find address in master list
00570     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
00571         if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
00572                 break;
00573 
00574     // delete address from master list
00575     if (i <= g_arenaservers.numfavoriteaddresses-1)
00576     {
00577         if (i < g_arenaservers.numfavoriteaddresses-1)
00578         {
00579             // shift items up
00580             memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)*sizeof(MAX_ADDRESSLENGTH));
00581         }
00582         g_arenaservers.numfavoriteaddresses--;
00583     }   
00584 
00585     // find address in server list
00586     for (i=0; i<g_numfavoriteservers; i++)
00587         if (&g_favoriteserverlist[i] == servernodeptr)
00588                 break;
00589 
00590     // delete address from server list
00591     if (i <= g_numfavoriteservers-1)
00592     {
00593         if (i < g_numfavoriteservers-1)
00594         {
00595             // shift items up
00596             memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
00597         }
00598         g_numfavoriteservers--;
00599     }   
00600 
00601     g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
00602     g_arenaservers.currentping       = g_arenaservers.numfavoriteaddresses;
00603 }
00604 
00605 
00606 /*
00607 =================
00608 ArenaServers_Insert
00609 =================
00610 */
00611 static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
00612 {
00613     servernode_t*   servernodeptr;
00614     char*           s;
00615     int             i;
00616 
00617 
00618     if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != AS_FAVORITES))
00619     {
00620         // slow global or local servers do not get entered
00621         return;
00622     }
00623 
00624     if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
00625         // list full;
00626         servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
00627     } else {
00628         // next slot
00629         servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
00630         (*g_arenaservers.numservers)++;
00631     }
00632 
00633     Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
00634 
00635     Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
00636     Q_CleanStr( servernodeptr->hostname );
00637     Q_strupr( servernodeptr->hostname );
00638 
00639     Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
00640     Q_CleanStr( servernodeptr->mapname );
00641     Q_strupr( servernodeptr->mapname );
00642 
00643     servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
00644     servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
00645     servernodeptr->pingtime   = pingtime;
00646     servernodeptr->minPing    = atoi( Info_ValueForKey( info, "minPing") );
00647     servernodeptr->maxPing    = atoi( Info_ValueForKey( info, "maxPing") );
00648     servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
00649 
00650     /*
00651     s = Info_ValueForKey( info, "nettype" );
00652     for (i=0; ;i++)
00653     {
00654         if (!netnames[i])
00655         {
00656             servernodeptr->nettype = 0;
00657             break;
00658         }
00659         else if (!Q_stricmp( netnames[i], s ))
00660         {
00661             servernodeptr->nettype = i;
00662             break;
00663         }
00664     }
00665     */
00666     servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
00667 
00668     s = Info_ValueForKey( info, "game");
00669     i = atoi( Info_ValueForKey( info, "gametype") );
00670     if( i < 0 ) {
00671         i = 0;
00672     }
00673     else if( i > 11 ) {
00674         i = 12;
00675     }
00676     if( *s ) {
00677         servernodeptr->gametype = i;//-1;
00678         Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
00679     }
00680     else {
00681         servernodeptr->gametype = i;
00682         Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
00683     }
00684 }
00685 
00686 
00687 /*
00688 =================
00689 ArenaServers_InsertFavorites
00690 
00691 Insert nonresponsive address book entries into display lists.
00692 =================
00693 */
00694 void ArenaServers_InsertFavorites( void )
00695 {
00696     int     i;
00697     int     j;
00698     char    info[MAX_INFO_STRING];
00699 
00700     // resync existing results with new or deleted cvars
00701     info[0] = '\0';
00702     Info_SetValueForKey( info, "hostname", "No Response" );
00703     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
00704     {
00705         // find favorite address in refresh list
00706         for (j=0; j<g_numfavoriteservers; j++)
00707             if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
00708                 break;
00709 
00710         if ( j >= g_numfavoriteservers)
00711         {
00712             // not in list, add it
00713             ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
00714         }
00715     }
00716 }
00717 
00718 
00719 /*
00720 =================
00721 ArenaServers_LoadFavorites
00722 
00723 Load cvar address book entries into local lists.
00724 =================
00725 */
00726 void ArenaServers_LoadFavorites( void )
00727 {
00728     int             i;
00729     int             j;
00730     int             numtempitems;
00731     char            emptyinfo[MAX_INFO_STRING];
00732     char            adrstr[MAX_ADDRESSLENGTH];
00733     servernode_t    templist[MAX_FAVORITESERVERS];
00734     qboolean        found;
00735 
00736     found        = qfalse;
00737     emptyinfo[0] = '\0';
00738 
00739     // copy the old
00740     memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
00741     numtempitems = g_numfavoriteservers;
00742 
00743     // clear the current for sync
00744     memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
00745     g_numfavoriteservers = 0;
00746 
00747     // resync existing results with new or deleted cvars
00748     for (i=0; i<MAX_FAVORITESERVERS; i++)
00749     {
00750         trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
00751         if (!adrstr[0])
00752             continue;
00753 
00754         // quick sanity check to avoid slow domain name resolving
00755         // first character must be numeric
00756         if (adrstr[0] < '0' || adrstr[0] > '9')
00757             continue;
00758 
00759         // favorite server addresses must be maintained outside refresh list
00760         // this mimics local and global netadr's stored in client
00761         // these can be fetched to fill ping list
00762         strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );
00763 
00764         // find this server in the old list
00765         for (j=0; j<numtempitems; j++)
00766             if (!Q_stricmp( templist[j].adrstr, adrstr ))
00767                 break;
00768 
00769         if (j < numtempitems)
00770         {
00771             // found server - add exisiting results
00772             memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
00773             found = qtrue;
00774         }
00775         else
00776         {
00777             // add new server
00778             Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
00779             g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
00780         }
00781 
00782         g_numfavoriteservers++;
00783     }
00784 
00785     g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
00786 
00787     if (!found)
00788     {
00789         // no results were found, reset server list
00790         // list will be automatically refreshed when selected
00791         g_numfavoriteservers = 0;
00792     }
00793 }
00794 
00795 
00796 /*
00797 =================
00798 ArenaServers_StopRefresh
00799 =================
00800 */
00801 static void ArenaServers_StopRefresh( void )
00802 {
00803     if (!g_arenaservers.refreshservers)
00804         // not currently refreshing
00805         return;
00806 
00807     g_arenaservers.refreshservers = qfalse;
00808 
00809     if (g_servertype == AS_FAVORITES)
00810     {
00811         // nonresponsive favorites must be shown
00812         ArenaServers_InsertFavorites();
00813     }
00814 
00815     // final tally
00816     if (g_arenaservers.numqueriedservers >= 0)
00817     {
00818         g_arenaservers.currentping       = *g_arenaservers.numservers;
00819         g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
00820     }
00821     
00822     // sort
00823     qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
00824 
00825     ArenaServers_UpdateMenu();
00826 }
00827 
00828 
00829 /*
00830 =================
00831 ArenaServers_DoRefresh
00832 =================
00833 */
00834 static void ArenaServers_DoRefresh( void )
00835 {
00836     int     i;
00837     int     j;
00838     int     time;
00839     int     maxPing;
00840     char    adrstr[MAX_ADDRESSLENGTH];
00841     char    info[MAX_INFO_STRING];
00842 
00843     if (uis.realtime < g_arenaservers.refreshtime)
00844     {
00845       if (g_servertype != AS_FAVORITES) {
00846             if (g_servertype == AS_LOCAL) {
00847                 if (!trap_LAN_GetServerCount(g_servertype)) {
00848                     return;
00849                 }
00850             }
00851             if (trap_LAN_GetServerCount(g_servertype) < 0) {
00852               // still waiting for response
00853               return;
00854             }
00855       }
00856     }
00857 
00858     if (uis.realtime < g_arenaservers.nextpingtime)
00859     {
00860         // wait for time trigger
00861         return;
00862     }
00863 
00864     // trigger at 10Hz intervals
00865     g_arenaservers.nextpingtime = uis.realtime + 10;
00866 
00867     // process ping results
00868     maxPing = ArenaServers_MaxPing();
00869     for (i=0; i<MAX_PINGREQUESTS; i++)
00870     {
00871         trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
00872         if (!adrstr[0])
00873         {
00874             // ignore empty or pending pings
00875             continue;
00876         }
00877 
00878         // find ping result in our local list
00879         for (j=0; j<MAX_PINGREQUESTS; j++)
00880             if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
00881                 break;
00882 
00883         if (j < MAX_PINGREQUESTS)
00884         {
00885             // found it
00886             if (!time)
00887             {
00888                 time = uis.realtime - g_arenaservers.pinglist[j].start;
00889                 if (time < maxPing)
00890                 {
00891                     // still waiting
00892                     continue;
00893                 }
00894             }
00895 
00896             if (time > maxPing)
00897             {
00898                 // stale it out
00899                 info[0] = '\0';
00900                 time    = maxPing;
00901             }
00902             else
00903             {
00904                 trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
00905             }
00906 
00907             // insert ping results
00908             ArenaServers_Insert( adrstr, info, time );
00909 
00910             // clear this query from internal list
00911             g_arenaservers.pinglist[j].adrstr[0] = '\0';
00912         }
00913 
00914         // clear this query from external list
00915         trap_LAN_ClearPing( i );
00916     }
00917 
00918     // get results of servers query
00919     // counts can increase as servers respond
00920     if (g_servertype == AS_FAVORITES) {
00921       g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
00922     } else {
00923       g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(g_servertype);
00924     }
00925 
00926 //  if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
00927 //      g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
00928 
00929     // send ping requests in reasonable bursts
00930     // iterate ping through all found servers
00931     for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
00932     {
00933         if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
00934         {
00935             // ping queue is full
00936             break;
00937         }
00938 
00939         // find empty slot
00940         for (j=0; j<MAX_PINGREQUESTS; j++)
00941             if (!g_arenaservers.pinglist[j].adrstr[0])
00942                 break;
00943 
00944         if (j >= MAX_PINGREQUESTS)
00945             // no empty slots available yet - wait for timeout
00946             break;
00947 
00948         // get an address to ping
00949 
00950         if (g_servertype == AS_FAVORITES) {
00951           strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );       
00952         } else {
00953           trap_LAN_GetServerAddressString(g_servertype, g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
00954         }
00955 
00956         strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
00957         g_arenaservers.pinglist[j].start = uis.realtime;
00958 
00959         trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr )  );
00960         
00961         // advance to next server
00962         g_arenaservers.currentping++;
00963     }
00964 
00965     if (!trap_LAN_GetPingQueueCount())
00966     {
00967         // all pings completed
00968         ArenaServers_StopRefresh();
00969         return;
00970     }
00971 
00972     // update the user interface with ping status
00973     ArenaServers_UpdateMenu();
00974 }
00975 
00976 
00977 /*
00978 =================
00979 ArenaServers_StartRefresh
00980 =================
00981 */
00982 static void ArenaServers_StartRefresh( void )
00983 {
00984     int     i;
00985     char    myargs[32], protocol[32];
00986 
00987     memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
00988 
00989     for (i=0; i<MAX_PINGREQUESTS; i++)
00990     {
00991         g_arenaservers.pinglist[i].adrstr[0] = '\0';
00992         trap_LAN_ClearPing( i );
00993     }
00994 
00995     g_arenaservers.refreshservers    = qtrue;
00996     g_arenaservers.currentping       = 0;
00997     g_arenaservers.nextpingtime      = 0;
00998     *g_arenaservers.numservers       = 0;
00999     g_arenaservers.numqueriedservers = 0;
01000 
01001     // allow max 5 seconds for responses
01002     g_arenaservers.refreshtime = uis.realtime + 5000;
01003 
01004     // place menu in zeroed state
01005     ArenaServers_UpdateMenu();
01006 
01007     if( g_servertype == AS_LOCAL ) {
01008         trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
01009         return;
01010     }
01011 
01012     if( g_servertype == AS_GLOBAL || g_servertype == AS_MPLAYER ) {
01013         if( g_servertype == AS_GLOBAL ) {
01014             i = 0;
01015         }
01016         else {
01017             i = 1;
01018         }
01019 
01020         switch( g_arenaservers.gametype.curvalue ) {
01021         default:
01022         case GAMES_ALL:
01023             myargs[0] = 0;
01024             break;
01025 
01026         case GAMES_FFA:
01027             strcpy( myargs, " ffa" );
01028             break;
01029 
01030         case GAMES_TEAMPLAY:
01031             strcpy( myargs, " team" );
01032             break;
01033 
01034         case GAMES_TOURNEY:
01035             strcpy( myargs, " tourney" );
01036             break;
01037 
01038         case GAMES_CTF:
01039             strcpy( myargs, " ctf" );
01040             break;
01041         }
01042 
01043 
01044         if (g_emptyservers) {
01045             strcat(myargs, " empty");
01046         }
01047 
01048         if (g_fullservers) {
01049             strcat(myargs, " full");
01050         }
01051 
01052         protocol[0] = '\0';
01053         trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) );
01054         if (strlen(protocol)) {
01055             trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", i, protocol, myargs ));
01056         }
01057         else {
01058             trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", i, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
01059         }
01060     }
01061 }
01062 
01063 
01064 /*
01065 =================
01066 ArenaServers_SaveChanges
01067 =================
01068 */
01069 void ArenaServers_SaveChanges( void )
01070 {
01071     int i;
01072 
01073     for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
01074         trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );
01075 
01076     for (; i<MAX_FAVORITESERVERS; i++)
01077         trap_Cvar_Set( va("server%d",i+1), "" );
01078 }
01079 
01080 
01081 /*
01082 =================
01083 ArenaServers_Sort
01084 =================
01085 */
01086 void ArenaServers_Sort( int type ) {
01087     if( g_sortkey == type ) {
01088         return;
01089     }
01090 
01091     g_sortkey = type;
01092     qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
01093 }
01094 
01095 
01096 /*
01097 =================
01098 ArenaServers_SetType
01099 =================
01100 */
01101 void ArenaServers_SetType( int type )
01102 {
01103     if (g_servertype == type)
01104         return;
01105 
01106     g_servertype = type;
01107 
01108     switch( type ) {
01109     default:
01110     case AS_LOCAL:
01111         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
01112         g_arenaservers.serverlist = g_localserverlist;
01113         g_arenaservers.numservers = &g_numlocalservers;
01114         g_arenaservers.maxservers = MAX_LOCALSERVERS;
01115         break;
01116 
01117     case AS_GLOBAL:
01118         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
01119         g_arenaservers.serverlist = g_globalserverlist;
01120         g_arenaservers.numservers = &g_numglobalservers;
01121         g_arenaservers.maxservers = MAX_GLOBALSERVERS;
01122         break;
01123 
01124     case AS_FAVORITES:
01125         g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
01126         g_arenaservers.serverlist = g_favoriteserverlist;
01127         g_arenaservers.numservers = &g_numfavoriteservers;
01128         g_arenaservers.maxservers = MAX_FAVORITESERVERS;
01129         break;
01130 
01131     case AS_MPLAYER:
01132         g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
01133         g_arenaservers.serverlist = g_mplayerserverlist;
01134         g_arenaservers.numservers = &g_nummplayerservers;
01135         g_arenaservers.maxservers = MAX_GLOBALSERVERS;
01136         break;
01137         
01138     }
01139 
01140     if( !*g_arenaservers.numservers ) {
01141         ArenaServers_StartRefresh();
01142     }
01143     else {
01144         // avoid slow operation, use existing results
01145         g_arenaservers.currentping       = *g_arenaservers.numservers;
01146         g_arenaservers.numqueriedservers = *g_arenaservers.numservers; 
01147         ArenaServers_UpdateMenu();
01148     }
01149     strcpy(g_arenaservers.status.string,"hit refresh to update");
01150 }
01151 
01152 /*
01153 =================
01154 PunkBuster_Confirm
01155 =================
01156 */
01157 static void Punkbuster_ConfirmEnable( qboolean result ) {
01158     if (result)
01159     {       
01160         trap_SetPbClStatus(1);
01161     }
01162     g_arenaservers.punkbuster.