ui_servers2.c 44 KB

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