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 }