1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10
11
12 // Menu System functions
13 // Created 30/6/02
14 // Jason Boettcher
15
16
17 #include <assert.h>
18
19 #include <set>
20
21 #include "Debug.h"
22 #include "LieroX.h"
23 #include "console.h"
24 #include "EndianSwap.h"
25 #include "AuxLib.h"
26 #include "Error.h"
27 #include "ConfigHandler.h"
28 #include "CClient.h"
29 #include "IpToCountryDB.h"
30 #include "DeprecatedGUI/Graphics.h"
31 #include "DeprecatedGUI/Menu.h"
32 #include "GfxPrimitives.h"
33 #include "FindFile.h"
34 #include "StringUtils.h"
35 #include "CWorm.h"
36 #include "Cursor.h"
37 #include "DeprecatedGUI/CButton.h"
38 #include "DedicatedControl.h"
39 #include "OLXG15.h"
40 #include "Timer.h"
41 #include "IRC.h"
42 #include "FileUtils.h"
43 #include "Command.h"
44 #include "HTTP.h"
45 #include "Version.h"
46 #include "CrashHandler.h"
47 #include "Touchscreen.h"
48
49
50 // TODO: move this out here
51 // declare them only locally here as nobody really should use them explicitly
52 std::string Utf8String(const std::string &OldLxString);
53
54
55
56
57 namespace DeprecatedGUI {
58
59 menu_t *tMenu = NULL;
60
61
62 bool *bGame = NULL;
63 int iSkipStart = false;
64 CWidgetList LayoutWidgets[LAYOUT_COUNT];
65
66 static int Menu_MouseWarpedX = 0;
67 static int Menu_MouseWarpedY = 0;
68
69 ///////////////////
70 // Initialize the menu system
Menu_Initialize(bool * game)71 bool Menu_Initialize(bool *game)
72 {
73 bGame = game;
74 *bGame = false;
75 bJoin_Update = true;
76 bHost_Update = true;
77
78 // Allocate the menu structure
79 tMenu = new menu_t;
80 if(tMenu == NULL) {
81 SystemError("Error: Out of memory in for menu");
82 return false;
83 }
84
85 if(bDedicated) return true;
86
87 // Load the frontend info
88 Menu_LoadFrontendInfo();
89
90 tMenu->iReturnTo = net_internet;
91 tMenu->bForbidConsole = false;
92 tMenu->iListItemHeight = tLX->cFont.GetHeight();
93 tMenu->bFingerDrag = false;
94 #ifdef __ANDROID__
95 if (!getenv("ANDROID_TV"))
96 #else
97 if (getenv("TEST_TOUCH_UI"))
98 #endif
99 {
100 tMenu->bFingerDrag = true;
101 tMenu->iListItemHeight = tLX->cFont.GetHeight() * 2;
102 }
103
104 // Load the images
105 //LOAD_IMAGE(tMenu->bmpMainBack,"data/frontend/background.png");
106 //LOAD_IMAGE(tMenu->bmpMainBack_lg,"data/frontend/background_lg.png");
107 LOAD_IMAGE(tMenu->bmpMainBack_wob,"data/frontend/background_wob.png");
108
109 // bmpMainBack_common, for backward compatibility: if it doesn't exist, we use bmpMainBack_wob
110 tMenu->bmpMainBack_common = LoadGameImage("data/frontend/background_common.png");
111 if (!tMenu->bmpMainBack_common.get())
112 tMenu->bmpMainBack_common = tMenu->bmpMainBack_wob;
113
114
115 tMenu->bmpBuffer = gfxCreateSurface(640,480);
116 if(tMenu->bmpBuffer.get() == NULL) {
117 SystemError("Error: Out of memory back buffer");
118 return false;
119 }
120
121
122 tMenu->bmpMsgBuffer = gfxCreateSurface(640,480);
123 if(tMenu->bmpMsgBuffer.get() == NULL) {
124 SystemError("Error: Out of memory in MsgBuffer");
125 return false;
126 }
127
128 tMenu->bmpMiniMapBuffer = gfxCreateSurface(128,96);
129 if(tMenu->bmpMiniMapBuffer.get() == NULL) {
130 SystemError("Error: Out of memory in MiniMapBuffer");
131 return false;
132 }
133
134 SmartPointer<SDL_Surface> lobby_state = NULL;
135 LOAD_IMAGE_WITHALPHA(tMenu->bmpMainTitles,"data/frontend/maintitles.png");
136 LOAD_IMAGE_WITHALPHA(tMenu->bmpLieroXtreme,"data/frontend/lierox.png");
137 LOAD_IMAGE_WITHALPHA(tMenu->bmpSubTitles,"data/frontend/subtitles.png");
138 LOAD_IMAGE_WITHALPHA(tMenu->bmpButtons,"data/frontend/buttons.png");
139 LOAD_IMAGE_WITHALPHA(tMenu->bmpMapEdTool,"data/frontend/map_toolbar.png");
140 LOAD_IMAGE_WITHALPHA(tMenu->bmpCheckbox,"data/frontend/checkbox.png");
141 LOAD_IMAGE_WITHALPHA(tMenu->bmpInputbox,"data/frontend/inputbox.png");
142 //LOAD_IMAGE_WITHALPHA(tMenu->bmpAI,"data/frontend/cpu.png");
143 LOAD_IMAGE_WITHALPHA(lobby_state, "data/frontend/lobbyready.png");;
144 LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[0], "data/frontend/con_good.png");
145 LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[1], "data/frontend/con_average.png");
146 LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[2], "data/frontend/con_bad.png");
147 LOAD_IMAGE_WITHALPHA(tMenu->bmpConnectionSpeeds[3], "data/frontend/con_none.png");
148 LOAD_IMAGE_WITHALPHA2(tMenu->bmpConnectionSpeeds[4], "data/frontend/con_nat.png", "data/frontend/con_bad.png");
149 LOAD_IMAGE_WITHALPHA(tMenu->bmpTriangleUp, "data/frontend/triangle_up.png");
150 LOAD_IMAGE_WITHALPHA(tMenu->bmpTriangleDown, "data/frontend/triangle_down.png");
151 tMenu->bmpDownload = LoadGameImage("data/frontend/download.png", true); // Doesn't have to exist
152 tMenu->bmpChatBackground = LoadGameImage("data/frontend/background_chat.png", true);
153 tMenu->bmpChatBackgroundMain = LoadGameImage("data/frontend/background_chat_main.png", true);
154
155 // Split up the lobby ready image
156 tMenu->bmpLobbyReady = gfxCreateSurfaceAlpha(lobby_state.get()->w, 12);
157 if (!tMenu->bmpLobbyReady.get()) {
158 errors << "Out of memory while creating tMenu->bmpLobbyReady" << endl;
159 return false;
160 }
161 CopySurface(tMenu->bmpLobbyReady.get(), lobby_state, 0, 0, 0, 0, lobby_state.get()->w, 12);
162
163 tMenu->bmpLobbyNotReady = gfxCreateSurfaceAlpha(lobby_state.get()->w, 12);
164 if (!tMenu->bmpLobbyNotReady.get()) {
165 errors << "Out of memory while creating tMenu->bmpLobbyNotReady" << endl;
166 return false;
167 }
168 CopySurface(tMenu->bmpLobbyNotReady.get(), lobby_state, 0, 12, 0, 0, lobby_state.get()->w, 12);
169
170
171 for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
172 tMenu->tSocket[i] = new NetworkSocket();
173
174 // HACK: open an unreliable foo socket
175 // Some routers simply ignore first open socket and don't let any data through, this is a workaround
176 tMenu->tSocket[SCK_FOO]->OpenUnreliable(0);
177 // Open a socket for broadcasting over a LAN (UDP)
178 tMenu->tSocket[SCK_LAN]->OpenBroadcast(0);
179 // Open a socket for communicating over the net (UDP)
180 tMenu->tSocket[SCK_NET]->OpenUnreliable(0);
181
182 if(!tMenu->tSocket[SCK_LAN]->isOpen() || !tMenu->tSocket[SCK_NET]->isOpen()) {
183 SystemError("Error: Failed to open a socket for networking");
184 return false;
185 }
186
187 // Send some random data to some random IP
188 if (tMenu->tSocket[SCK_FOO]->isOpen()) {
189 NetworkAddr a; StringToNetAddr("1.2.3.4:5678", a);
190 // For example, if no network is connected, you likely only have 127.* in your routing table.
191 if(IsNetAddrAvailable(a)) {
192 tMenu->tSocket[SCK_FOO]->setRemoteAddress(a);
193 tMenu->tSocket[SCK_FOO]->Write("foo");
194 }
195 }
196
197 // Add default widget IDs to the widget list
198 //Menu_AddDefaultWidgets();
199
200 return true;
201 }
202
203 /////////////////////////
204 // Load the infor about frontend
Menu_LoadFrontendInfo()205 void Menu_LoadFrontendInfo()
206 {
207 ReadInteger("data/frontend/frontend.cfg","MainTitles","X",&tMenu->tFrontendInfo.iMainTitlesLeft,50);
208 ReadInteger("data/frontend/frontend.cfg","MainTitles","Y",&tMenu->tFrontendInfo.iMainTitlesTop,160);
209 ReadInteger("data/frontend/frontend.cfg","Credits","X",&tMenu->tFrontendInfo.iCreditsLeft,370);
210 ReadInteger("data/frontend/frontend.cfg","Credits","Y",&tMenu->tFrontendInfo.iCreditsTop,379);
211 ReadInteger("data/frontend/frontend.cfg","Credits","Spacing",&tMenu->tFrontendInfo.iCreditsSpacing,0);
212 ReadString ("data/frontend/frontend.cfg","Credits","FrontendCredits", tMenu->tFrontendInfo.sFrontendCredits, " ");
213 ReadInteger("data/frontend/frontend.cfg","MainTitles","Spacing",&tMenu->tFrontendInfo.iMainTitlesSpacing,15);
214 ReadKeyword("data/frontend/frontend.cfg","PageBoxes","Visible",&tMenu->tFrontendInfo.bPageBoxes,true);
215 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","AnimX",&tMenu->tFrontendInfo.iLoadingAnimLeft,5);
216 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","AnimY",&tMenu->tFrontendInfo.iLoadingAnimTop,5);
217 ReadFloat("data/frontend/frontend.cfg","IpToCountryLoading","AnimFrameTime",&tMenu->tFrontendInfo.fLoadingAnimFrameTime,0.2f);
218 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","BarX",&tMenu->tFrontendInfo.iLoadingBarLeft,5);
219 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","BarY",&tMenu->tFrontendInfo.iLoadingBarTop,80);
220 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","LabelX",&tMenu->tFrontendInfo.iLoadingLabelLeft,5);
221 ReadInteger("data/frontend/frontend.cfg","IpToCountryLoading","LabelY",&tMenu->tFrontendInfo.iLoadingLabelTop,60);
222 }
223
224 ///////////////////
225 // Shutdown the menu
Menu_Shutdown()226 void Menu_Shutdown()
227 {
228 Menu_Current_Shutdown();
229
230 if(tMenu) {
231 // The rest get free'd in the cache
232 delete tMenu;
233 tMenu = NULL;
234 }
235
236 // Shutdown the layouts
237 //for (int i=0; i<LAYOUT_COUNT; i++)
238 // LayoutWidgets[i].Shutdown();
239
240 Menu_SvrList_Shutdown();
241 }
242
243
244 ///////////////////
245 // Start the menu
Menu_Start()246 void Menu_Start()
247 {
248 tMenu->bMenuRunning = true;
249
250 if(!bDedicated) {
251 if(!iSkipStart) {
252 notes << "Loading main menu" << endl;
253 tMenu->iMenuType = MNU_MAIN;
254 Menu_MainInitialize();
255 } else
256 Menu_RedrawMouse(true);
257 }
258
259 iSkipStart = false;
260 Menu_Loop();
261 }
262
263
264 ///////////////////
265 // Set the skip start bit
Menu_SetSkipStart(int s)266 void Menu_SetSkipStart(int s)
267 {
268 iSkipStart = s;
269 }
270
Menu_Frame()271 void Menu_Frame() {
272 HandlePendingCommands();
273
274 if(bDedicated) {
275 DedicatedControl::Get()->Menu_Frame();
276 return;
277 }
278
279 if(!tMenu->bMenuRunning) return; // could be already quitted
280
281 // Check if user pressed screenshot key
282 if (tLX->cTakeScreenshot.isDownOnce()) {
283 PushScreenshot("scrshots", "");
284 }
285
286 Menu_RedrawMouse(true);
287
288 #ifdef WITH_G15
289 if (OLXG15)
290 OLXG15->menuFrame();
291 #endif //WITH_G15
292
293 switch(tMenu->iMenuType) {
294
295 // Main
296 case MNU_MAIN:
297 Menu_MainFrame();
298 break;
299
300 // Local
301 case MNU_LOCAL:
302 Menu_LocalFrame();
303 break;
304
305 // News
306 case MNU_NETWORK:
307 Menu_NetFrame();
308 break;
309
310 // Player
311 case MNU_PLAYER:
312 Menu_PlayerFrame();
313 break;
314
315 // Map editor
316 case MNU_MAPED:
317 Menu_MapEdFrame(VideoPostProcessor::videoSurface(),true);
318 break;
319
320 // Options
321 case MNU_OPTIONS:
322 Menu_OptionsFrame();
323 break;
324 }
325
326 // DEBUG: show FPS
327 #ifdef DEBUG
328 if(tLX->fDeltaTime != TimeDiff()) {
329 Menu_redrawBufferRect(0, 0, 100, 20);
330 tLX->cFont.Draw(VideoPostProcessor::videoSurface(), 0, 0, tLX->clWhite, "FPS: " + itoa((int)(1.0f/tLX->fDeltaTime.seconds())));
331 }
332 #endif
333
334 if (!tMenu->bForbidConsole) {
335 Con_Process(tLX->fDeltaTime);
336 Con_Draw(VideoPostProcessor::videoSurface());
337 }
338 tMenu->bForbidConsole = false; // Reset it here, it might get recovered next frame
339
340 // we need to clone the screen buffer because of the current way we are drawing the menu
341 struct CopyScreenBuffer : Action {
342 int handle() { VideoPostProcessor::cloneBuffer(); return 0; }
343 };
344 doVppOperation(new CopyScreenBuffer());
345
346 // now do the actual flip&draw
347 doVideoFrameInMainThread();
348
349 SetTouchscreenControlsShown(false);
350 }
351
352
353 ///////////////////
354 // Main menu loop
Menu_Loop()355 void Menu_Loop()
356 {
357 AbsTime menuStartTime = tLX->currentTime = GetTime();
358
359 bool last_frame_was_because_of_an_event = false;
360 last_frame_was_because_of_an_event = ProcessEvents();
361
362 while(tMenu->bMenuRunning) {
363 AbsTime oldtime = tLX->currentTime;
364
365 Menu_Frame();
366 if(!tMenu->bMenuRunning) break;
367 CapFPS();
368 SetCrashHandlerReturnPoint("Menu_Loop");
369
370 if(last_frame_was_because_of_an_event || bDedicated) {
371 // Use ProcessEvents() here to handle other processes in queue.
372 // There aren't probably any but it has also the effect that
373 // we run the loop another time after an event which is sometimes
374 // because of the current code needed. Sometimes after an event,
375 // some new menu elements got initialised but not drawn.
376 last_frame_was_because_of_an_event = ProcessEvents();
377 } else {
378 last_frame_was_because_of_an_event = WaitForNextEvent();
379 }
380
381 ProcessIRC();
382
383 tLX->currentTime = GetTime();
384 tLX->fDeltaTime = tLX->currentTime - oldtime;
385 tLX->fRealDeltaTime = tLX->fDeltaTime;
386
387 // If we have run fine for >=5 seconds, it is probably safe & make sense
388 // to restart the game in case of a crash.
389 if(tLX->currentTime - menuStartTime >= TimeDiff(5.0f))
390 CrashHandler::restartAfterCrash = true;
391 }
392
393 // If we go out of the menu, it means the user has selected something.
394 // This indicates that everything is fine, so we should restart in case of a crash.
395 // Note that we will set this again to false later on in case the user quitted.
396 CrashHandler::restartAfterCrash = true;
397 }
398
399
400 ///////////////////
401 // Redraw the rectangle under the mouse (total means a total buffer redraw)
402 // TODO: rename this function (one would expect that it redraws the mouse)
Menu_RedrawMouse(bool total)403 void Menu_RedrawMouse(bool total)
404 {
405 if(total) {
406 SDL_BlitSurface(tMenu->bmpBuffer.get(),NULL,VideoPostProcessor::videoSurface(),NULL);
407 return;
408 }
409
410 int hw = GetMaxCursorWidth() / 2 - 1;
411 int hh = GetMaxCursorHeight() / 2 - 1;
412
413 mouse_t *m = GetMouse();
414 DrawImageAdv(VideoPostProcessor::videoSurface(),tMenu->bmpBuffer,
415 m->X - hw - m->deltaX,
416 m->Y - hh - m->deltaY,
417
418 m->X - hw - m->deltaX,
419 m->Y - hh - m->deltaY,
420 GetMaxCursorWidth() * 2, GetMaxCursorHeight() * 2);
421 }
422
423
424 ///////////////////
425 // Draw a sub title
Menu_DrawSubTitle(SDL_Surface * bmpDest,int id)426 void Menu_DrawSubTitle(SDL_Surface * bmpDest, int id)
427 {
428 int x = VideoPostProcessor::videoSurface()->w/2;
429 x -= tMenu->bmpSubTitles.get()->w/2;
430
431 DrawImageAdv(bmpDest,tMenu->bmpSubTitles, 0, id*70, x,30, tMenu->bmpSubTitles.get()->w, 65);
432 }
433
434
435 ///////////////////
436 // Draw a sub title advanced
Menu_DrawSubTitleAdv(SDL_Surface * bmpDest,int id,int y)437 void Menu_DrawSubTitleAdv(SDL_Surface * bmpDest, int id, int y)
438 {
439 int x = VideoPostProcessor::videoSurface()->w/2;
440 x -= tMenu->bmpSubTitles.get()->w/2;
441
442 DrawImageAdv(bmpDest,tMenu->bmpSubTitles, 0, id*70, x,y, tMenu->bmpSubTitles.get()->w, 65);
443 }
444
445
446 ////////////////
447 // Draws advanced box
Menu_DrawBoxAdv(SDL_Surface * bmpDest,int x,int y,int x2,int y2,int border,Color LightColour,Color DarkColour,Color BgColour,uchar type)448 void Menu_DrawBoxAdv(SDL_Surface * bmpDest, int x, int y, int x2, int y2, int border, Color LightColour, Color DarkColour, Color BgColour, uchar type)
449 {
450 // First draw the background
451 if (BgColour != tLX->clPink)
452 DrawRectFill(bmpDest,x+border,y+border,x2-border+1,y2-border+1,BgColour);
453
454 if (!border)
455 return;
456
457 int i;
458
459 // Switch the light and dark colour when inset
460 if (type == BX_INSET) {
461 Color tmp = LightColour;
462 LightColour = DarkColour;
463 DarkColour = tmp;
464 }
465
466 // Create gradient when needed
467 int r_step,g_step,b_step;
468 const Uint8 r1 = DarkColour.r, g1 = DarkColour.g, b1 = DarkColour.b;
469 const Uint8 r2 = LightColour.r, g2 = LightColour.b, b2 = LightColour.b;
470
471 if (type != BX_SOLID) {
472 r_step = (r2-r1)/border;
473 g_step = (g2-g1)/border;
474 b_step = (b2-b1)/border;
475 }
476 else {
477 r_step = g_step = b_step = 0;
478 }
479
480
481 // Draw the box
482 for (i=0;i<border;i++)
483 DrawRect(bmpDest,x+i,y+i,x2-i,y2-i,Color(r1+r_step*i,g1+g_step*i,b1+b_step*i));
484 }
485
486
487 ///////////////////
488 // Draw a box
Menu_DrawBox(SDL_Surface * bmpDest,int x,int y,int x2,int y2)489 void Menu_DrawBox(SDL_Surface * bmpDest, int x, int y, int x2, int y2)
490 {
491 if(bmpDest == NULL) {
492 errors << "Menu_DrawBox: bmpDest == NULL" << endl;
493 return;
494 }
495
496 DrawRect( bmpDest,x+1, y+1, x2-1,y2-1, tLX->clBoxLight);
497 //DrawRect( bmpDest,x+2, y+2, x2-2,y2-2, tLX->clBoxDark);
498 DrawHLine(bmpDest,x+2, x2-1,y, tLX->clBoxDark);
499 DrawHLine(bmpDest,x+2, x2-1,y2, tLX->clBoxDark);
500
501 DrawVLine(bmpDest,y+2, y2-1,x, tLX->clBoxDark);
502 DrawVLine(bmpDest,y+2, y2-1,x2, tLX->clBoxDark);
503
504 Uint32 dark = tLX->clBoxDark.get(bmpDest->format);
505
506 LOCK_OR_QUIT(bmpDest);
507 PutPixel( bmpDest,x+1, y+1, dark);
508 PutPixel( bmpDest,x2-1,y+1, dark);
509 PutPixel( bmpDest,x+1, y2-1, dark);
510 PutPixel( bmpDest,x2-1,y2-1, dark);
511 UnlockSurface(bmpDest);
512 }
513
514
515 ///////////////////
516 // Draw an inset box
Menu_DrawBoxInset(SDL_Surface * bmpDest,int x,int y,int x2,int y2)517 void Menu_DrawBoxInset(SDL_Surface * bmpDest, int x, int y, int x2, int y2)
518 {
519 if(bmpDest == NULL) {
520 errors << "Menu_DrawBoxInset: bmpDest is NULL" << endl;
521 return;
522 }
523
524 // Clipping
525 if (x < 0) { x2 += x; x = 0; }
526 if (x2 >= bmpDest->w) { x2 = bmpDest->w - 1; }
527 if (y < 0) { y2 += y; y = 0; }
528 if (y2 >= bmpDest->h) { y2 = bmpDest->h - 1; }
529
530 DrawRect( bmpDest,x+1, y+1, x2-1,y2-1, tLX->clBoxDark);
531 DrawHLine(bmpDest,x+2, x2-1,y, tLX->clBoxLight);
532 DrawHLine(bmpDest,x+2, x2-1,y2, tLX->clBoxLight);
533
534 DrawVLine(bmpDest,y+2, y2-1,x, tLX->clBoxLight);
535 DrawVLine(bmpDest,y+2, y2-1,x2, tLX->clBoxLight);
536
537 Uint32 light = tLX->clBoxLight.get(bmpDest->format);
538
539 LOCK_OR_QUIT(bmpDest);
540 if(PointInRect(x+1,y+1,bmpDest->clip_rect)) PutPixel( bmpDest,x+1, y+1, light);
541 if(PointInRect(x2-1,y+1,bmpDest->clip_rect)) PutPixel( bmpDest,x2-1,y+1, light);
542 if(PointInRect(x+1,y2-1,bmpDest->clip_rect)) PutPixel( bmpDest,x+1, y2-1, light);
543 if(PointInRect(x2-1,y2-1,bmpDest->clip_rect)) PutPixel( bmpDest,x2-1,y2-1, light);
544 UnlockSurface(bmpDest);
545 }
546
547
548 ///////////////////
549 // Draw a windows style button
Menu_DrawWinButton(SDL_Surface * bmpDest,int x,int y,int w,int h,bool down)550 void Menu_DrawWinButton(SDL_Surface * bmpDest, int x, int y, int w, int h, bool down)
551 {
552 DrawRectFill(bmpDest, x,y, x+w, y+h, tLX->clWinBtnBody);
553 const Color dark = tLX->clWinBtnDark;
554 const Color light = tLX->clWinBtnLight;
555 if(down) {
556 DrawHLine(bmpDest, x, x+w, y, dark);
557 DrawHLine(bmpDest, x, x+w, y+h, light);
558 DrawVLine(bmpDest, y, y+h, x, dark);
559 DrawVLine(bmpDest, y, y+h, x+w, light);
560 } else {
561 DrawHLine(bmpDest, x, x+w, y, light);
562 DrawHLine(bmpDest, x, x+w, y+h, dark);
563 DrawVLine(bmpDest, y, y+h, x, light);
564 DrawVLine(bmpDest, y, y+h, x+w, dark);
565 }
566 }
567
568
569 ///////////////////
570 // Show a message box
Menu_MessageBox(const std::string & sTitle,const std::string & sText,MessageBoxType type)571 MessageBoxReturnType Menu_MessageBox(const std::string& sTitle, const std::string& sText, MessageBoxType type)
572 {
573 SetTouchscreenControlsShown(false);
574 if(bDedicated) {
575 hints << "Menu_MessageBox: " << sTitle << ": " << sText << endl;
576 switch(type) {
577 case LMB_OK: return MBR_OK;
578 case LMB_YESNO:
579 hints << "Dedicated server is positive and says YES." << endl;
580 return MBR_YES;
581 }
582 return MBR_OK;
583 }
584
585 MessageBoxReturnType ret = MBR_INVALID;
586 gui_event_t *ev = NULL;
587
588 SetGameCursor(CURSOR_ARROW);
589
590 int minw = 350;
591 int maxw = 500;
592 int x = 160;
593 int y = 170;
594 int w = minw; // the whole box
595 int h = 140; // including caption and button, the whole box
596
597 // Adjust the width
598 int longest_line = w;
599 std::vector<std::string> lines = explode(sText, "\n");
600 std::vector<std::string>::const_iterator it;
601 for (it=lines.begin(); it!=lines.end(); it++) {
602 int tw = tLX->cFont.GetWidth(*it);
603 if (tw > longest_line)
604 longest_line = tw;
605 }
606 w = CLAMP(longest_line + 40, minw, maxw);
607 x = (VideoPostProcessor::get()->screenWidth() - w) / 2;
608
609 // Handle multiline messages
610 lines = splitstring(sText, (size_t)-1, w - 2, tLX->cFont);
611
612 const int line_hspace = 2;
613 const int button_h = 24;
614 const int caption_h = 25;
615
616 if( (tLX->cFont.GetHeight() + line_hspace) * (int)lines.size() + button_h + caption_h + 2 > h ) {
617 // TODO: hardcoded screen height (480)
618 h = (int)MIN( (tLX->cFont.GetHeight() + line_hspace) * lines.size() + 90, (size_t)478);
619 y = 240-h/2;
620 }
621
622 int cx = x+w/2;
623 int cy = y + caption_h;
624 if(lines.size() > 0) {
625 cy += (h - button_h - caption_h) / 2;
626 cy -= ((int)(lines.size() - 1) * (tLX->cFont.GetHeight() + line_hspace)) / 2;
627 cy -= tLX->cFont.GetHeight() / 2;
628 }
629
630 //
631 // Setup the gui
632 //
633 CGuiLayout msgbox;
634
635 msgbox.Initialize();
636
637 if(type == LMB_OK)
638 msgbox.Add( new CButton(BUT_OK,tMenu->bmpButtons), 0, cx-20,y+h-button_h, 40,15);
639 else if(type == LMB_YESNO) {
640 msgbox.Add( new CButton(BUT_YES,tMenu->bmpButtons), 1, x+15,y+h-button_h, 35,15);
641 msgbox.Add( new CButton(BUT_NO,tMenu->bmpButtons), 2, x+w-35,y+h-button_h, 30,15);
642 }
643
644 // Store the old buffer into a temp buffer to keep it
645 SDL_BlitSurface(tMenu->bmpBuffer.get(), NULL, tMenu->bmpMsgBuffer.get(), NULL);
646
647
648 // Draw to the buffer
649 //DrawImage(tMenu->bmpBuffer, shadow, 177,167);
650 Menu_DrawBox(tMenu->bmpBuffer.get(), x, y, x+w, y+h);
651 DrawRectFill(tMenu->bmpBuffer.get(), x+2,y+2, x+w-1,y+h-1,tLX->clDialogBackground);
652 DrawRectFill(tMenu->bmpBuffer.get(), x+2,y+2, x+w-1,y+caption_h,tLX->clDialogCaption);
653
654 tLX->cFont.DrawCentre(tMenu->bmpBuffer.get(), cx, y+5, tLX->clNormalLabel,sTitle);
655 for (it=lines.begin(); it!=lines.end(); it++) {
656 cx = x+w/2;//-(tLX->cFont.GetWidth(lines[i])+30)/2;
657 tLX->cFont.DrawCentre(tMenu->bmpBuffer.get(), cx, cy, tLX->clNormalLabel, *it);
658 cy += tLX->cFont.GetHeight()+line_hspace;
659 }
660
661 Menu_RedrawMouse(true);
662
663
664 ProcessEvents();
665
666
667 // TODO: make this event-based (don't check GetKeyboard() directly)
668 while(true) {
669 Menu_RedrawMouse(true);
670
671 SetGameCursor(CURSOR_ARROW);
672
673 DrawImageAdv(VideoPostProcessor::videoSurface(),tMenu->bmpBuffer, x,y, x,y, w, h);
674
675 // Process the gui
676 ev = msgbox.Process();
677 msgbox.Draw(VideoPostProcessor::videoSurface());
678
679 if(ev) {
680
681 if(ev->cWidget->getType() == wid_Button)
682 SetGameCursor(CURSOR_HAND);
683 if(ev->cWidget->getType() == wid_Textbox)
684 SetGameCursor(CURSOR_TEXT);
685
686 if(ev->iEventMsg == BTN_CLICKED) {
687 switch(ev->iControlID) {
688
689 // OK
690 case 0:
691 ret = MBR_OK;
692 break;
693
694 // Yes
695 case 1:
696 ret = MBR_YES;
697 break;
698
699 // No
700 case 2:
701 ret = MBR_NO;
702 break;
703 }
704 }
705 }
706
707 // Handle the Enter key
708 if (WasKeyboardEventHappening(SDLK_RETURN) || WasKeyboardEventHappening(SDLK_KP_ENTER))
709 {
710 if (type == LMB_YESNO) {
711 ret = MBR_YES;
712 break;
713 }
714 else {
715 ret = MBR_OK;
716 break;
717 }
718 }
719
720 if(!WasKeyboardEventHappening(SDLK_ESCAPE) && !tLX->bQuitGame && ret == MBR_INVALID) {
721 DrawCursor(VideoPostProcessor::videoSurface());
722 doVideoFrameInMainThread();
723 CapFPS();
724 tLX->currentTime = GetTime(); // we need this for CapFPS()
725 WaitForNextEvent();
726 } else
727 break;
728 }
729
730 SetGameCursor(CURSOR_ARROW);
731 msgbox.Shutdown();
732
733 // Restore the old buffer
734 SDL_BlitSurface(tMenu->bmpMsgBuffer.get(), NULL, tMenu->bmpBuffer.get(), NULL);
735 //Menu_RedrawMouse(true);
736 //doVideoFrameInMainThread();
737
738 return ret;
739 }
740
741 ///////////////////
742 // Add all the default widgets
Menu_AddDefaultWidgets()743 void Menu_AddDefaultWidgets()
744 {
745 // 34 layouts total
746
747 // L_MAINMENU: 6 widgets
748 LayoutWidgets[L_MAINMENU].Add("LocalPlay");
749 LayoutWidgets[L_MAINMENU].Add("NetPlay");
750 LayoutWidgets[L_MAINMENU].Add("PlayerProfiles");
751 LayoutWidgets[L_MAINMENU].Add("LevelEditor");
752 LayoutWidgets[L_MAINMENU].Add("Options");
753 LayoutWidgets[L_MAINMENU].Add("Quit");
754
755 // L_LOCALPLAY: 9 widgets
756 LayoutWidgets[L_LOCALPLAY].Add("Back");
757 LayoutWidgets[L_LOCALPLAY].Add("Start");
758 LayoutWidgets[L_LOCALPLAY].Add("Playing");
759 LayoutWidgets[L_LOCALPLAY].Add("PlayerList");
760 LayoutWidgets[L_LOCALPLAY].Add("LevelList");
761 LayoutWidgets[L_LOCALPLAY].Add("Gametype");
762 LayoutWidgets[L_LOCALPLAY].Add("ModName");
763 LayoutWidgets[L_LOCALPLAY].Add("GameSettings");
764 LayoutWidgets[L_LOCALPLAY].Add("WeaponOptions");
765
766 // L_GAMESETTINGS: 9 widgets
767 LayoutWidgets[L_GAMESETTINGS].Add("gs_Ok");
768 LayoutWidgets[L_GAMESETTINGS].Add("gs_Default");
769 LayoutWidgets[L_GAMESETTINGS].Add("Lives");
770 LayoutWidgets[L_GAMESETTINGS].Add("MaxKills");
771 LayoutWidgets[L_GAMESETTINGS].Add("LoadingTime");
772 LayoutWidgets[L_GAMESETTINGS].Add("LoadingTimeLabel");
773 LayoutWidgets[L_GAMESETTINGS].Add("Bonuses");
774 LayoutWidgets[L_GAMESETTINGS].Add("ShowBonusNames");
775 LayoutWidgets[L_GAMESETTINGS].Add("MaxTime");
776
777 // L_WEAPONOPTIONS: 8 widgets
778 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Ok");
779 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Scroll");
780 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Reset");
781 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_ListBox");
782 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Cancel");
783 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Random");
784 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Load");
785 LayoutWidgets[L_WEAPONOPTIONS].Add("wr_Save");
786
787 // L_LOADWEAPONS: 4 widgets
788 LayoutWidgets[L_LOADWEAPONS].Add("wp_Cancel");
789 LayoutWidgets[L_LOADWEAPONS].Add("wp_Ok");
790 LayoutWidgets[L_LOADWEAPONS].Add("wp_PresetList");
791 LayoutWidgets[L_LOADWEAPONS].Add("wp_PresetName");
792
793 // L_SAVEWEAPONS: 4 widgets
794 LayoutWidgets[L_SAVEWEAPONS].Add("wp_Cancel");
795 LayoutWidgets[L_SAVEWEAPONS].Add("wp_Ok");
796 LayoutWidgets[L_SAVEWEAPONS].Add("wp_PresetList");
797 LayoutWidgets[L_SAVEWEAPONS].Add("wp_PresetName");
798
799 // L_NET: 4 widgets
800 LayoutWidgets[L_NET].Add("InternetTab");
801 LayoutWidgets[L_NET].Add("LANTab");
802 LayoutWidgets[L_NET].Add("HostTab");
803 LayoutWidgets[L_NET].Add("FavouritesTab");
804
805 // L_NETINTERNET: 8 widgets
806 LayoutWidgets[L_NETINTERNET].Add("Join");
807 LayoutWidgets[L_NETINTERNET].Add("ServerList");
808 LayoutWidgets[L_NETINTERNET].Add("Refresh");
809 LayoutWidgets[L_NETINTERNET].Add("UpdateList");
810 LayoutWidgets[L_NETINTERNET].Add("AddServer");
811 LayoutWidgets[L_NETINTERNET].Add("Back");
812 LayoutWidgets[L_NETINTERNET].Add("PopupMenu");
813 LayoutWidgets[L_NETINTERNET].Add("PlayerSelection");
814
815 // L_INTERNETDETAILS: 1 widgets
816 LayoutWidgets[L_INTERNETDETAILS].Add("id_Ok");
817
818 // L_ADDSERVER: 3 widgets
819 LayoutWidgets[L_ADDSERVER].Add("na_Cancel");
820 LayoutWidgets[L_ADDSERVER].Add("na_Add");
821 LayoutWidgets[L_ADDSERVER].Add("na_Address");
822
823 // L_NETLAN: 6 widgets
824 LayoutWidgets[L_NETLAN].Add("Join");
825 LayoutWidgets[L_NETLAN].Add("ServerList");
826 LayoutWidgets[L_NETLAN].Add("Refresh");
827 LayoutWidgets[L_NETLAN].Add("Back");
828 LayoutWidgets[L_NETLAN].Add("PopupMenu");
829 LayoutWidgets[L_NETLAN].Add("PlayerSelection");
830
831 // L_LANDETAILS: 1 widgets
832 LayoutWidgets[L_LANDETAILS].Add("ld_Ok");
833
834 // L_NETHOST: 10 widgets
835 LayoutWidgets[L_NETHOST].Add("Back");
836 LayoutWidgets[L_NETHOST].Add("Ok");
837 LayoutWidgets[L_NETHOST].Add("PlayerList");
838 LayoutWidgets[L_NETHOST].Add("Playing");
839 LayoutWidgets[L_NETHOST].Add("Servername");
840 LayoutWidgets[L_NETHOST].Add("MaxPlayers");
841 LayoutWidgets[L_NETHOST].Add("Register");
842 LayoutWidgets[L_NETHOST].Add("Password");
843 LayoutWidgets[L_NETHOST].Add("WelcomeMessage");
844 LayoutWidgets[L_NETHOST].Add("AllowWantsJoin");
845
846 // L_NETFAVOURITES: 7 widgets
847 LayoutWidgets[L_NETFAVOURITES].Add("Join");
848 LayoutWidgets[L_NETFAVOURITES].Add("ServerList");
849 LayoutWidgets[L_NETFAVOURITES].Add("Refresh");
850 LayoutWidgets[L_NETFAVOURITES].Add("Add");
851 LayoutWidgets[L_NETFAVOURITES].Add("Back");
852 LayoutWidgets[L_NETFAVOURITES].Add("PopupMenu");
853 LayoutWidgets[L_NETFAVOURITES].Add("PlayerSelection");
854
855 // L_FAVOURITESDETAILS: 1 widgets
856 LayoutWidgets[L_FAVOURITESDETAILS].Add("fd_Ok");
857
858 // L_RENAMESERVER: 3 widgets
859 LayoutWidgets[L_RENAMESERVER].Add("rs_Cancel");
860 LayoutWidgets[L_RENAMESERVER].Add("rs_Ok");
861 LayoutWidgets[L_RENAMESERVER].Add("rs_NewName");
862
863 // L_ADDFAVOURITE: 4 widgets
864 LayoutWidgets[L_ADDFAVOURITE].Add("fa_Cancel");
865 LayoutWidgets[L_ADDFAVOURITE].Add("fa_Add");
866 LayoutWidgets[L_ADDFAVOURITE].Add("fa_Address");
867 LayoutWidgets[L_ADDFAVOURITE].Add("fa_Name");
868
869 // L_CONNECTING: 1 widgets
870 LayoutWidgets[L_CONNECTING].Add("Cancel");
871
872 // L_NETJOINLOBBY: 4 widgets
873 LayoutWidgets[L_NETJOINLOBBY].Add("Back2");
874 LayoutWidgets[L_NETJOINLOBBY].Add("Ready");
875 LayoutWidgets[L_NETJOINLOBBY].Add("ChatText");
876 LayoutWidgets[L_NETJOINLOBBY].Add("ChatList");
877
878 // L_NETHOSTLOBBY: 14 widgets
879 LayoutWidgets[L_NETHOSTLOBBY].Add("Back2");
880 LayoutWidgets[L_NETHOSTLOBBY].Add("Start");
881 LayoutWidgets[L_NETHOSTLOBBY].Add("ChatText");
882 LayoutWidgets[L_NETHOSTLOBBY].Add("ChatList");
883 LayoutWidgets[L_NETHOSTLOBBY].Add("LevelList");
884 LayoutWidgets[L_NETHOSTLOBBY].Add("Lives");
885 LayoutWidgets[L_NETHOSTLOBBY].Add("MaxKills");
886 LayoutWidgets[L_NETHOSTLOBBY].Add("ModName");
887 LayoutWidgets[L_NETHOSTLOBBY].Add("Gametype");
888 LayoutWidgets[L_NETHOSTLOBBY].Add("GameSettings");
889 LayoutWidgets[L_NETHOSTLOBBY].Add("WeaponOptions");
890 LayoutWidgets[L_NETHOSTLOBBY].Add("PopupMenu");
891 LayoutWidgets[L_NETHOSTLOBBY].Add("Banned");
892 LayoutWidgets[L_NETHOSTLOBBY].Add("ServerSettings");
893
894 // L_SERVERSETTINGS: 7 widgets
895 LayoutWidgets[L_SERVERSETTINGS].Add("ss_Ok");
896 LayoutWidgets[L_SERVERSETTINGS].Add("ss_Cancel");
897 LayoutWidgets[L_SERVERSETTINGS].Add("ss_AllowOnlyList");
898 LayoutWidgets[L_SERVERSETTINGS].Add("ss_WelcomeMessage");
899 LayoutWidgets[L_SERVERSETTINGS].Add("ss_ServerName");
900 LayoutWidgets[L_SERVERSETTINGS].Add("ss_AllowWantsJoin");
901 LayoutWidgets[L_SERVERSETTINGS].Add("ss_MaxPlayers");
902
903 // L_BANLIST: 4 widgets
904 LayoutWidgets[L_BANLIST].Add("bl_Close");
905 LayoutWidgets[L_BANLIST].Add("bl_Clear");
906 LayoutWidgets[L_BANLIST].Add("bl_Unban");
907 LayoutWidgets[L_BANLIST].Add("bl_ListBox");
908
909 // L_PLAYERPROFILES: 2 widgets
910 LayoutWidgets[L_PLAYERPROFILES].Add("NewPlayerTab");
911 LayoutWidgets[L_PLAYERPROFILES].Add("ViewPlayersTab");
912
913 // L_CREATEPLAYER: 12 widgets
914 LayoutWidgets[L_CREATEPLAYER].Add("np_Back");
915 LayoutWidgets[L_CREATEPLAYER].Add("np_Create");
916 LayoutWidgets[L_CREATEPLAYER].Add("np_Name");
917 LayoutWidgets[L_CREATEPLAYER].Add("np_Red");
918 LayoutWidgets[L_CREATEPLAYER].Add("np_Blue");
919 LayoutWidgets[L_CREATEPLAYER].Add("np_Green");
920 LayoutWidgets[L_CREATEPLAYER].Add("np_Type");
921 LayoutWidgets[L_CREATEPLAYER].Add("np_AIDiffLbl");
922 LayoutWidgets[L_CREATEPLAYER].Add("np_AIDiff");
923 LayoutWidgets[L_CREATEPLAYER].Add("np_PlySkin");
924 LayoutWidgets[L_CREATEPLAYER].Add("np_Username");
925 LayoutWidgets[L_CREATEPLAYER].Add("np_Password");
926
927 // L_VIEWPLAYERS: 12 widgets
928 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Back");
929 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Name");
930 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Red");
931 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Blue");
932 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Green");
933 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Players");
934 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Delete");
935 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Apply");
936 LayoutWidgets[L_VIEWPLAYERS].Add("vp_Type");
937 LayoutWidgets[L_VIEWPLAYERS].Add("vp_AIDiffLbl");
938 LayoutWidgets[L_VIEWPLAYERS].Add("vp_AIDiff");
939 LayoutWidgets[L_VIEWPLAYERS].Add("vp_PlySkin");
940
941 // L_LEVELEDITOR: 5 widgets
942 LayoutWidgets[L_LEVELEDITOR].Add("map_new");
943 LayoutWidgets[L_LEVELEDITOR].Add("map_random");
944 LayoutWidgets[L_LEVELEDITOR].Add("map_load");
945 LayoutWidgets[L_LEVELEDITOR].Add("map_save");
946 LayoutWidgets[L_LEVELEDITOR].Add("map_quit");
947
948 // L_NEWDIALOG: 5 widgets
949 LayoutWidgets[L_NEWDIALOG].Add("mn_Cancel");
950 LayoutWidgets[L_NEWDIALOG].Add("mn_Ok");
951 LayoutWidgets[L_NEWDIALOG].Add("mn_Width");
952 LayoutWidgets[L_NEWDIALOG].Add("mn_Height");
953 LayoutWidgets[L_NEWDIALOG].Add("mn_Scheme");
954
955 // L_SAVELOADLEVEL: 4 widgets
956 LayoutWidgets[L_SAVELOADLEVEL].Add("sl_Cancel");
957 LayoutWidgets[L_SAVELOADLEVEL].Add("sl_Ok");
958 LayoutWidgets[L_SAVELOADLEVEL].Add("sl_FileList");
959 LayoutWidgets[L_SAVELOADLEVEL].Add("sl_FileName");
960
961 // L_OPTIONS: 3 widgets
962 LayoutWidgets[L_OPTIONS].Add("ControlsTab");
963 LayoutWidgets[L_OPTIONS].Add("GameTab");
964 LayoutWidgets[L_OPTIONS].Add("SystemTab");
965
966 // L_OPTIONSCONTROLS: 23 widgets
967 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Up");
968 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Down");
969 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Left");
970 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Right");
971 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Shoot");
972 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Jump");
973 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Selweapon");
974 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply1_Rope");
975 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Up");
976 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Down");
977 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Left");
978 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Right");
979 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Shoot");
980 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Jump");
981 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Selweapon");
982 LayoutWidgets[L_OPTIONSCONTROLS].Add("Ply2_Rope");
983 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Chat");
984 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Score");
985 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_Health");
986 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_CurSettings");
987 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_TakeScreenshot");
988 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_ViewportManager");
989 LayoutWidgets[L_OPTIONSCONTROLS].Add("Gen_SwitchMode");
990
991 // L_OPTIONSGAME: 9 widgets
992 LayoutWidgets[L_OPTIONSGAME].Add("BloodAmount");
993 LayoutWidgets[L_OPTIONSGAME].Add("Shadows");
994 LayoutWidgets[L_OPTIONSGAME].Add("Particles");
995 LayoutWidgets[L_OPTIONSGAME].Add("OldSkoolRope");
996 LayoutWidgets[L_OPTIONSGAME].Add("AIDifficulty");
997 LayoutWidgets[L_OPTIONSGAME].Add("ShowWormHealth");
998 LayoutWidgets[L_OPTIONSGAME].Add("ColorizeNicks");
999 LayoutWidgets[L_OPTIONSGAME].Add("AutoTyping");
1000 LayoutWidgets[L_OPTIONSGAME].Add("ScreenshotFormat");
1001
1002 // L_OPTIONSSYSTEM: 12 widgets
1003 LayoutWidgets[L_OPTIONSSYSTEM].Add("Back");
1004 LayoutWidgets[L_OPTIONSSYSTEM].Add("Fullscreen");
1005 LayoutWidgets[L_OPTIONSSYSTEM].Add("OpenGL");
1006 LayoutWidgets[L_OPTIONSSYSTEM].Add("SoundOn");
1007 LayoutWidgets[L_OPTIONSSYSTEM].Add("SoundVolume");
1008 LayoutWidgets[L_OPTIONSSYSTEM].Add("NetworkPort");
1009 LayoutWidgets[L_OPTIONSSYSTEM].Add("NetworkSpeed");
1010 LayoutWidgets[L_OPTIONSSYSTEM].Add("ShowFPS");
1011 LayoutWidgets[L_OPTIONSSYSTEM].Add("ShowPing");
1012 LayoutWidgets[L_OPTIONSSYSTEM].Add("Filtered");
1013 LayoutWidgets[L_OPTIONSSYSTEM].Add("LogConvos");
1014 LayoutWidgets[L_OPTIONSSYSTEM].Add("Apply");
1015
1016 // L_MESSAGEBOXOK: 1 widgets
1017 LayoutWidgets[L_MESSAGEBOXOK].Add("mb_Ok");
1018
1019 // L_MESSAGEBOXYESNO: 2 widgets
1020 LayoutWidgets[L_MESSAGEBOXYESNO].Add("mb_Yes");
1021 LayoutWidgets[L_MESSAGEBOXYESNO].Add("mb_No");
1022 }
1023
1024
1025 // Load the level list
1026 struct LevelComboFiller {
1027 CCombobox* cmb;
LevelComboFillerDeprecatedGUI::LevelComboFiller1028 LevelComboFiller(CCombobox* c) : cmb(c) {}
operator ()DeprecatedGUI::LevelComboFiller1029 bool operator() (const std::string& filename) {
1030 std::string mapName = CMap::GetLevelName(filename, true);
1031 if(mapName.size() != 0)
1032 cmb->addItem(GetBaseFilename(filename), mapName);
1033
1034 return true;
1035 }
1036 };
1037
1038
1039 ///////////////////
1040 // Fill a listbox with the levels
Menu_FillLevelList(CCombobox * cmb,int random)1041 void Menu_FillLevelList(CCombobox *cmb, int random)
1042 {
1043 cmb->clear();
1044 cmb->setSorted(SORT_ASC);
1045 cmb->setUnique(true);
1046
1047 LevelComboFiller filler(cmb);
1048 FindFiles(filler, "levels", false, FM_REG);
1049
1050 // Disable sorting and add the random level at the beginning
1051 cmb->setSorted(SORT_NONE);
1052 //if(random) // If random is true, we add the 'random' level to the list
1053 // cmb->addItem(0, "_random_", "- Random level -");
1054
1055 cmb->setCurSIndexItem(tLXOptions->tGameInfo.sMapFile);
1056 }
1057
1058
1059 ///////////////////
1060 // Redraw a section from the buffer to the screen
Menu_redrawBufferRect(int x,int y,int w,int h)1061 void Menu_redrawBufferRect(int x, int y, int w, int h)
1062 {
1063 DrawImageAdv(VideoPostProcessor::videoSurface(), tMenu->bmpBuffer, x,y, x,y, w,h);
1064 }
1065
1066
1067
Menu_DisableNetEvents()1068 void Menu_DisableNetEvents()
1069 {
1070 for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
1071 if(i != SCK_FOO)
1072 tMenu->tSocket[i]->setWithEvents(false);
1073 }
1074
Menu_EnableNetEvents()1075 void Menu_EnableNetEvents()
1076 {
1077 for (size_t i = 0; i < sizeof(tMenu->tSocket)/sizeof(tMenu->tSocket[0]); ++i)
1078 if(i != SCK_FOO)
1079 tMenu->tSocket[i]->setWithEvents(true);
1080 }
1081
Menu_IsKeyboardNavigationUsed()1082 bool Menu_IsKeyboardNavigationUsed()
1083 {
1084 return CGuiLayout::isKeyboardNavigationUsed();
1085 }
1086
Menu_WarpMouse(int x,int y)1087 void Menu_WarpMouse(int x, int y)
1088 {
1089 struct RepositionMouse: public Action
1090 {
1091 int x, y;
1092 RepositionMouse(int _x, int _y): x(_x), y(_y)
1093 {
1094 }
1095 int handle()
1096 {
1097 SDL_WarpMouse(x, y);
1098 return true;
1099 }
1100 };
1101 Menu_MouseWarpedX = x;
1102 Menu_MouseWarpedY = y;
1103 doActionInMainThread( new RepositionMouse(x, y) );
1104 }
1105
Menu_ProcessMouseMotion(int x,int y)1106 void Menu_ProcessMouseMotion(int x, int y)
1107 {
1108 if (x != Menu_MouseWarpedX || y != Menu_MouseWarpedY) {
1109 CGuiLayout::setKeyboardNavigationUsed(false);
1110 }
1111 }
1112
1113 /*
1114 ============================
1115
1116 Server list functions
1117
1118 ============================
1119 */
1120
1121
1122 std::list<server_t> psServerList;
1123
1124 // Maximum number of pings/queries before we ignore the server
1125 static const int MaxPings = 4;
1126 static const int MaxQueries = MAX_QUERIES;
1127
1128
1129
1130 ///////////////////
1131 // Clear the server list
Menu_SvrList_Clear()1132 void Menu_SvrList_Clear()
1133 {
1134 Menu_SvrList_Shutdown();
1135 }
1136
1137
1138 ///////////////////
1139 // Clear any servers automatically added
Menu_SvrList_ClearAuto()1140 void Menu_SvrList_ClearAuto()
1141 {
1142 for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1143 {
1144 if(!it->bManual)
1145 {
1146 psServerList.erase(it);
1147 if (psServerList.empty())
1148 return;
1149 it = psServerList.begin();
1150 }
1151 }
1152 }
1153
1154
1155 ///////////////////
1156 // Shutdown the server list
Menu_SvrList_Shutdown()1157 void Menu_SvrList_Shutdown()
1158 {
1159 psServerList.clear();
1160 }
1161
1162
1163
SendBroadcastPing(int port)1164 static void SendBroadcastPing(int port) {
1165 // Broadcast a ping on the LAN
1166 CBytestream bs;
1167 bs.writeInt(-1,4);
1168 bs.writeString("lx::ping");
1169
1170 NetworkAddr a;
1171 StringToNetAddr("255.255.255.255", a);
1172 SetNetAddrPort(a, port);
1173 tMenu->tSocket[SCK_LAN]->setRemoteAddress(a);
1174
1175 // Send the ping
1176 bs.Send(tMenu->tSocket[SCK_LAN]);
1177 }
1178
1179 ///////////////////
1180 // Send a ping out to the LAN (LAN menu)
Menu_SvrList_PingLAN()1181 void Menu_SvrList_PingLAN()
1182 {
1183 SendBroadcastPing(LX_PORT);
1184 if(tLXOptions->iNetworkPort != LX_PORT)
1185 SendBroadcastPing(tLXOptions->iNetworkPort); // try also our own port
1186 }
1187
1188
1189 ///////////////////
1190 // Ping a server
Menu_SvrList_PingServer(server_t * svr)1191 void Menu_SvrList_PingServer(server_t *svr)
1192 {
1193 // If not available, probably the network is not connected right now.
1194 if(!IsNetAddrAvailable(svr->getBestAddress())) return;
1195
1196 for( int af = 0; af < 2; af++ )
1197 {
1198 NetworkAddr addr = af ? svr->sAddress6 : svr->sAddress;
1199 if( !IsNetAddrValid(addr) )
1200 continue;
1201 if( af == 0 )
1202 {
1203 if( svr->ports.size() == 0 )
1204 {
1205 errors << "svr->ports.size() == 0 at " << FILELINE << endl;
1206 continue;
1207 }
1208
1209 svr->lastPingedPort++;
1210 if( svr->lastPingedPort >= (int)svr->ports.size() || svr->lastPingedPort < 0 )
1211 svr->lastPingedPort = 0;
1212 SetNetAddrPort(addr, svr->ports[svr->lastPingedPort].first);
1213 }
1214
1215 //hints << "Pinging server " << tmp << " real addr " << svr->szAddress << " name " << svr->szName << endl;
1216 tMenu->tSocket[SCK_NET]->setRemoteAddress(addr);
1217
1218 CBytestream bs;
1219 bs.writeInt(-1,4);
1220 bs.writeString("lx::ping");
1221 bs.Send(tMenu->tSocket[SCK_NET]);
1222 }
1223
1224 svr->bProcessing = true;
1225 svr->nPings++;
1226 svr->fLastPing = tLX->currentTime;
1227 }
1228
1229 ///////////////////
1230 // Send Wants To Join message
Menu_SvrList_WantsJoin(const std::string & Nick,server_t * svr)1231 void Menu_SvrList_WantsJoin(const std::string& Nick, server_t *svr)
1232 {
1233 tMenu->tSocket[SCK_NET]->setRemoteAddress(svr->getBestAddress());
1234
1235 CBytestream bs;
1236 bs.writeInt(-1,4);
1237
1238 if( svr->bBehindNat )
1239 {
1240 NetworkAddr masterserverAddr;
1241 SetNetAddrValid(masterserverAddr, false);
1242 NetworkAddr ignored;
1243
1244 if( ! GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored ) )
1245 {
1246 GetNetAddrFromNameAsync( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress) );
1247 }
1248
1249 for( int count = 0; !IsNetAddrValid(masterserverAddr) && count < 5; count++ )
1250 {
1251 SDL_Delay(20);
1252 GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored );
1253 }
1254
1255 if( !IsNetAddrValid(masterserverAddr) )
1256 return;
1257
1258 tMenu->tSocket[SCK_NET]->setRemoteAddress(masterserverAddr);
1259 bs.writeString("lx::traverse");
1260 bs.writeString(svr->szAddress);
1261 }
1262
1263 bs.writeString("lx::wantsjoin");
1264 bs.writeString(RemoveSpecialChars(Nick));
1265 bs.Send(tMenu->tSocket[SCK_NET]);
1266 }
1267
1268 ///////////////////
1269 // Get server info
Menu_SvrList_GetServerInfo(server_t * svr)1270 void Menu_SvrList_GetServerInfo(server_t *svr)
1271 {
1272 // Send a getinfo request
1273 tMenu->tSocket[SCK_NET]->setRemoteAddress(svr->getBestAddress());
1274
1275 CBytestream bs;
1276 bs.writeInt(-1,4);
1277
1278 if( svr->bBehindNat )
1279 {
1280 NetworkAddr masterserverAddr;
1281 SetNetAddrValid(masterserverAddr, false);
1282 NetworkAddr ignored;
1283
1284 if( ! GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored ) )
1285 {
1286 GetNetAddrFromNameAsync( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress) );
1287 }
1288
1289 for( int count = 0; !IsNetAddrValid(masterserverAddr) && count < 5; count++ )
1290 {
1291 SDL_Delay(20);
1292 GetFromDnsCache( Menu_SvrList_GetUdpMasterserverForServer(svr->szAddress), masterserverAddr, ignored );
1293 }
1294
1295 if( !IsNetAddrValid(masterserverAddr) )
1296 return;
1297
1298 tMenu->tSocket[SCK_NET]->setRemoteAddress(masterserverAddr);
1299 bs.writeString("lx::traverse");
1300 bs.writeString(svr->szAddress);
1301 }
1302
1303 bs.writeString("lx::getinfo");
1304 bs.Send(tMenu->tSocket[SCK_NET]);
1305 }
1306
1307 ///////////////////
1308 // Query a server
Menu_SvrList_QueryServer(server_t * svr)1309 void Menu_SvrList_QueryServer(server_t *svr)
1310 {
1311 for( int af = 0; af < 2; af++ )
1312 {
1313 NetworkAddr addr = af ? svr->sAddress6 : svr->sAddress;
1314 if( !IsNetAddrValid(addr) )
1315 continue;
1316
1317 tMenu->tSocket[SCK_NET]->setRemoteAddress(addr);
1318
1319 CBytestream bs;
1320 bs.writeInt(-1,4);
1321 bs.writeString("lx::query");
1322 bs.writeByte(svr->nQueries);
1323 bs.Send(tMenu->tSocket[SCK_NET]);
1324 }
1325 svr->fQueryTimes[svr->nQueries] = tLX->currentTime;
1326
1327 svr->bProcessing = true;
1328 svr->nQueries++;
1329 svr->fLastQuery = tLX->currentTime;
1330 }
1331
1332
1333 ///////////////////
1334 // Refresh the server list (Internet menu)
Menu_SvrList_RefreshList()1335 void Menu_SvrList_RefreshList()
1336 {
1337 // Set all the servers to be pinged
1338 for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1339 {
1340 if( ! it->bBehindNat )
1341 Menu_SvrList_RefreshServer(&(*it), false);
1342 }
1343
1344 // Update the GUI
1345 Timer("Menu_SvrList_RefreshList ping waiter", null, NULL, PingWait, true).startHeadless();
1346
1347 //Menu_SvrList_UpdateUDPList(); // It adds duplicate server entries
1348 }
1349
1350
1351 ///////////////////
1352 // Refresh a single server
Menu_SvrList_RefreshServer(server_t * s,bool updategui)1353 void Menu_SvrList_RefreshServer(server_t *s, bool updategui)
1354 {
1355 if (!tLX)
1356 return;
1357
1358 s->bProcessing = true;
1359 s->bgotPong = false;
1360 s->bgotQuery = false;
1361 s->bgotQuery6 = false;
1362 s->bIgnore = false;
1363 s->fLastPing = AbsTime();
1364 s->fLastQuery = AbsTime();
1365 s->nPings = 0;
1366 s->fInitTime = tLX->currentTime;
1367 s->nQueries = 0;
1368 s->nPing = 0;
1369 s->nPing4 = s->nPing6 = 9999;
1370 s->bAddrReady = IsNetAddrValid(s->sAddress) || IsNetAddrValid(s->sAddress6);
1371 s->lastPingedPort = 0;
1372
1373 if( s->ports.size() == 0 )
1374 {
1375 int port = IsNetAddrValid(s->sAddress) ? GetNetAddrPort(s->sAddress) : 0;
1376 if (port == 0)
1377 port = IsNetAddrValid(s->sAddress6) ?GetNetAddrPort(s->sAddress6) : 0;
1378 if (port == 0)
1379 port = LX_PORT;
1380 s->ports.push_back(std::make_pair(port, -1));
1381 }
1382
1383 if (updategui)
1384 Timer("Menu_SvrList_RefreshServer ping waiter", null, NULL, PingWait, true).startHeadless();
1385 }
1386
1387
1388 ///////////////////
1389 // Add a server onto the list (for list and manually)
Menu_SvrList_AddServer(const std::string & address,bool bManual,const std::string & name,int udpMasterserverIndex,const std::string & v4address)1390 server_t *Menu_SvrList_AddServer(const std::string& address, bool bManual, const std::string & name, int udpMasterserverIndex, const std::string& v4address)
1391 {
1392 // Check if the server is already in the list
1393 // If it is, don't bother adding it
1394 NetworkAddr ad;
1395 std::string tmp_address = address;
1396 TrimSpaces(tmp_address);
1397 int port = -1;
1398 if(StringToNetAddr(tmp_address, ad))
1399 {
1400 port = GetNetAddrPort(ad);
1401 if( port == 0 )
1402 port = LX_PORT;
1403 }
1404
1405 server_t * found = Menu_SvrList_FindServerStr(tmp_address, name);
1406 if( !found && v4address != "" )
1407 found = Menu_SvrList_FindServerStr(v4address, name);
1408
1409 if( found && port != -1 && port != 0 ) {
1410 if( found->szName == "Untitled" ) {
1411 found->szName = name;
1412 TrimSpaces(found->szName);
1413 }
1414 //hints << "Menu_SvrList_AddServer(): merging duplicate " << found->szName << " " << found->szAddress << endl;
1415 if( StringToNetAddr(tmp_address, ad) ) {
1416 SetNetAddrPort(ad, port);
1417 if( IsNetAddrV6(tmp_address) ) {
1418 if( !AreNetAddrEqual(ad, found->sAddress6) ) {
1419 // Query it again, to measure v4 ping vs v6 ping
1420 found->fLastQuery = AbsTime();
1421 }
1422 found->sAddress6 = ad;
1423 } else {
1424 if( !IsNetAddrValid(found->sAddress) && IsNetAddrValid(found->sAddress6) ) {
1425 // Query it again, to measure v4 ping vs v6 ping
1426 found->fLastQuery = AbsTime();
1427 }
1428 found->sAddress = ad;
1429 for( size_t i = 0; i < found->ports.size(); i++ )
1430 if( found->ports[i].first == port )
1431 return found;
1432 found->ports.push_back( std::make_pair( port, udpMasterserverIndex ) );
1433 }
1434 }
1435
1436 return found;
1437 }
1438
1439 // Didn't find one, so create it
1440 psServerList.push_back(server_t());
1441 server_t * svr = & psServerList.back();
1442
1443 // Fill in the details
1444 svr->bManual = bManual;
1445 svr->szAddress = tmp_address;
1446 ResetNetAddr(svr->sAddress);
1447 ResetNetAddr(svr->sAddress6);
1448
1449 if( StringToNetAddr(tmp_address, ad) ) {
1450 SetNetAddrPort(ad, port);
1451 if( IsNetAddrV6(tmp_address) ) {
1452 svr->sAddress6 = ad;
1453 if (v4address != "" && StringToNetAddr(v4address, ad)) {
1454 port = GetNetAddrPort(ad);
1455 if( port == 0 )
1456 port = LX_PORT;
1457 SetNetAddrPort(ad, port);
1458 svr->sAddress = ad;
1459 }
1460 } else {
1461 svr->sAddress = ad;
1462 }
1463 } else {
1464 hints << "Menu_SvrList_AddServer(): cannot parse server addr " << svr->szAddress << endl;
1465 }
1466
1467 Menu_SvrList_RefreshServer(svr, bManual);
1468
1469 if( svr->ports.size() > 0 )
1470 svr->ports[0].second = udpMasterserverIndex;
1471
1472 // Default game details
1473 svr->szName = name;
1474 TrimSpaces(svr->szName);
1475 svr->nMaxPlayers = 0;
1476 svr->nNumPlayers = 0;
1477 svr->nState = 0;
1478 svr->nPing = -3; // Put it at the end of server list, after NAT servers
1479 svr->nPing4 = svr->nPing6 = 9999;
1480 if( udpMasterserverIndex >= 0 )
1481 {
1482 svr->bBehindNat = true;
1483 svr->nPing = -2;
1484 }
1485 else
1486 svr->bBehindNat = false;
1487
1488 //hints << "Menu_SvrList_AddServer(): added server " << svr->szName << " svr->szAddress " << svr->szAddress << " v4addr " << v4address <<
1489 // " svr->sAddress " << NetAddrToString(svr->sAddress) << " svr->sAddress6 " << NetAddrToString(svr->sAddress6) << endl;
1490
1491 return svr;
1492 }
1493
1494
1495 ///////////////////
1496 // Remove a server from the server list
Menu_SvrList_RemoveServer(const std::string & szAddress)1497 void Menu_SvrList_RemoveServer(const std::string& szAddress)
1498 {
1499 for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1500 if( it->szAddress == szAddress )
1501 {
1502 psServerList.erase( it );
1503 it = psServerList.begin();
1504 break;
1505 }
1506 }
1507
1508
1509 ///////////////////
1510 // Find a server based on a string address
Menu_SvrList_FindServerStr(const std::string & szAddress,const std::string & name)1511 server_t *Menu_SvrList_FindServerStr(const std::string& szAddress, const std::string & name)
1512 {
1513 NetworkAddr addr;
1514 if( ! StringToNetAddr(szAddress, addr) )
1515 return NULL;
1516
1517 return Menu_SvrList_FindServer(addr, name);
1518 }
1519
1520
1521 ///////////////////
1522 // Fill a listview box with the server list
Menu_SvrList_FillList(CListview * lv)1523 void Menu_SvrList_FillList(CListview *lv)
1524 {
1525 if (!lv)
1526 return;
1527
1528 std::string addr;
1529 static const std::string states[] = {"Open", "Loading", "Playing", "Open/Loading", "Open/Playing"};
1530
1531 // Store the ID of the currently selected item
1532 int curID = lv->getSelectedID();
1533
1534 lv->SaveScrollbarPos();
1535 lv->Clear();
1536
1537 for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1538 {
1539
1540 bool processing = s->bProcessing && Menu_SvrList_GetUdpMasterserverForServer( s->szAddress ) == "";
1541
1542 // Ping Image
1543 int num = 3;
1544 if(s->nPing < 700) num = 2;
1545 if(s->nPing < 400) num = 1;
1546 if(s->nPing < 200) num = 0;
1547
1548 if(s->bIgnore || processing)
1549 num = 3;
1550
1551 if(s->nPing == -2) num = 4; // Server behind a NAT
1552
1553 // Address
1554 //GetRemoteNetAddr(tMenu->tSocket, &s->sAddress);
1555 //NetAddrToString(&s->sAddress, addr);
1556
1557 // show port if special
1558 addr = s->szAddress;
1559 size_t p = addr.rfind(':');
1560 if( IsNetAddrV6(addr) && addr.find("]:") == std::string::npos ) {
1561 p = std::string::npos;
1562 }
1563
1564 if(p != std::string::npos) {
1565 std::string sPort = addr.substr(p + 1);
1566 addr.erase(p);
1567 if(from_string<int>(sPort) != LX_PORT)
1568 addr += ":" + sPort;
1569 }
1570
1571 // State
1572 int state = 0;
1573 if(s->nState >= 0 && s->nState < 3)
1574 state = s->nState;
1575 if( state != 0 && s->bAllowConnectDuringGame && s->nNumPlayers < s->nMaxPlayers )
1576 state += 2;
1577
1578 // Colour
1579 Color colour = tLX->clListView;
1580 if(processing)
1581 colour = tLX->clDisabled;
1582
1583 // Add the server to the list
1584 lv->AddItem(s->szAddress, 0, colour);
1585 lv->AddSubitem(LVS_IMAGE, itoa(num,10), tMenu->bmpConnectionSpeeds[num], NULL);
1586 lv->AddSubitem(LVS_TEXT, s->szName, (DynDrawIntf*)NULL, NULL);
1587 if(processing) {
1588 if(IsNetAddrValid(s->sAddress) || IsNetAddrValid(s->sAddress6))
1589 lv->AddSubitem(LVS_TEXT, "Querying...", (DynDrawIntf*)NULL, NULL);
1590 else
1591 lv->AddSubitem(LVS_TEXT, "Lookup...", (DynDrawIntf*)NULL, NULL);
1592 } else if( num == 3 )
1593 lv->AddSubitem(LVS_TEXT, "Down", (DynDrawIntf*)NULL, NULL);
1594 else
1595 lv->AddSubitem(LVS_TEXT, states[state], (DynDrawIntf*)NULL, NULL);
1596
1597 bool unknownData = ( s->bProcessing || num == 3 ) &&
1598 Menu_SvrList_GetUdpMasterserverForServer( s->szAddress ) == "";
1599
1600 // Players
1601 lv->AddSubitem(LVS_TEXT,
1602 unknownData ? "?" : (itoa(s->nNumPlayers,10)+"/"+itoa(s->nMaxPlayers,10)),
1603 (DynDrawIntf*)NULL, NULL);
1604
1605 if (s->nPing <= -2) // Server behind a NAT or not queried, it will add spaces if s->nPing == -3 so not queried servers will be below NAT ones
1606 lv->AddSubitem(LVS_TEXT, "N/A" + std::string(' ', -2 - s->nPing), (DynDrawIntf*)NULL, NULL);
1607 else
1608 lv->AddSubitem(LVS_TEXT, unknownData ? "∞" : itoa(s->nPing,10), (DynDrawIntf*)NULL, NULL); // TODO: the infinity symbol isn't shown correctly
1609
1610 // Country
1611 if (tLXOptions->bUseIpToCountry && iNetMode == net_internet) {
1612 IpInfo inf = tIpToCountryDB->GetInfoAboutIP(addr);
1613 if( tLXOptions->bShowCountryFlags )
1614 {
1615 SmartPointer<SDL_Surface> flag = tIpToCountryDB->GetCountryFlag(inf.countryCode);
1616 if (flag.get())
1617 lv->AddSubitem(LVS_IMAGE, "", flag, NULL, VALIGN_MIDDLE, inf.countryName);
1618 else
1619 lv->AddSubitem(LVS_TEXT, inf.countryCode, (DynDrawIntf*)NULL, NULL);
1620 }
1621 else
1622 {
1623 lv->AddSubitem(LVS_TEXT, inf.countryName, (DynDrawIntf*)NULL, NULL);
1624 }
1625 }
1626
1627 // Address
1628 lv->AddSubitem(LVS_TEXT, addr, (DynDrawIntf*)NULL, NULL);
1629 }
1630
1631 lv->ReSort();
1632 lv->setSelectedID(curID);
1633 lv->RestoreScrollbarPos();
1634 }
1635
1636 static bool bUpdateFromUdpThread = false;
1637 ///////////////////
1638 // Process the network connection
1639 // Returns true if a server in the list was added/modified
Menu_SvrList_Process()1640 bool Menu_SvrList_Process()
1641 {
1642 CBytestream bs;
1643 bool update = false;
1644
1645
1646 // Process any packets on the net socket
1647 while(bs.Read(tMenu->tSocket[SCK_NET])) {
1648
1649 if( Menu_SvrList_ParsePacket(&bs, tMenu->tSocket[SCK_NET]) )
1650 update = true;
1651
1652 }
1653
1654 // Process any packets on the LAN socket
1655 while(bs.Read(tMenu->tSocket[SCK_LAN])) {
1656
1657 if( Menu_SvrList_ParsePacket(&bs, tMenu->tSocket[SCK_LAN]) )
1658 update = true;
1659 }
1660
1661 if( bUpdateFromUdpThread )
1662 {
1663 bUpdateFromUdpThread = false;
1664 update = true;
1665 }
1666
1667 bool repaint = false;
1668
1669
1670 // Ping or Query any servers in the list that need it
1671 for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1672 {
1673
1674 // Ignore this server? (timed out)
1675 if(s->bIgnore)
1676 continue;
1677
1678 if(!IsNetAddrValid(s->sAddress) && !IsNetAddrValid(s->sAddress6)) {
1679 if(tLX->currentTime - s->fInitTime >= DNS_TIMEOUT) {
1680 s->bIgnore = true; // timeout
1681 update = true;
1682 }
1683 continue;
1684 } else {
1685 if(!s->bAddrReady) {
1686 s->bAddrReady = true;
1687 update = true;
1688 }
1689 }
1690
1691 // Need a pingin'?
1692 if(!s->bgotPong) {
1693 if(tLX->currentTime - s->fLastPing > (float)PingWait / 1000.0f) {
1694
1695 if(s->nPings >= MaxPings) {
1696 s->bIgnore = true;
1697
1698 update = true;
1699 } else {
1700 // Ping the server
1701 Menu_SvrList_PingServer(&(*s));
1702 repaint = true;
1703 }
1704 }
1705 }
1706
1707 // Need a querying?
1708 if(s->bgotPong && (
1709 (IsNetAddrValid(s->sAddress) && !s->bgotQuery) ||
1710 (IsNetAddrValid(s->sAddress6) && !s->bgotQuery6))) {
1711 if(tLX->currentTime - s->fLastQuery > (float)QueryWait / 1000.0f) {
1712
1713 if(s->nQueries >= MaxQueries) {
1714 if( !s->bgotQuery && !s->bgotQuery6 ) {
1715 s->bIgnore = true;
1716 update = true;
1717 }
1718 } else {
1719 // Query the server
1720 Menu_SvrList_QueryServer(&(*s));
1721 repaint = true;
1722 }
1723 }
1724 }
1725
1726 // If we are ignoring this server now, set it to not processing
1727 if(s->bIgnore) {
1728 s->bProcessing = false;
1729 update = true;
1730 }
1731
1732 }
1733
1734 // Make sure the list repaints when the ping/query is received
1735 if (repaint)
1736 Timer("Menu_SvrList_Process ping waiter", null, NULL, PingWait + 100, true).startHeadless();
1737
1738 return update;
1739 }
1740
1741
1742 ///////////////////
1743 // Parse a packet
1744 // Returns true if we should update the list
Menu_SvrList_ParsePacket(CBytestream * bs,const SmartPointer<NetworkSocket> & sock)1745 bool Menu_SvrList_ParsePacket(CBytestream *bs, const SmartPointer<NetworkSocket>& sock)
1746 {
1747 NetworkAddr adrFrom;
1748 bool update = false;
1749 std::string cmd,buf;
1750
1751 // Check for connectionless packet header
1752 if(bs->readInt(4) == -1) {
1753 cmd = bs->readString();
1754
1755 adrFrom = sock->remoteAddress();
1756
1757
1758 // Check for a pong
1759 if(cmd == "lx::pong") {
1760
1761 // Look the the list and find which server returned the ping
1762 server_t *svr = Menu_SvrList_FindServer(adrFrom);
1763 if( svr ) {
1764
1765 // It pinged, so fill in the ping info so it will now be queried
1766 svr->bgotPong = true;
1767 svr->nQueries = 0;
1768 svr->bBehindNat = false;
1769 svr->lastPingedPort = 0;
1770 if( AreNetAddrEqual(adrFrom, svr->sAddress6) ) {
1771 //NetAddrToString(svr->sAddress6, svr->szAddress);
1772 } else {
1773 SetNetAddrPort(svr->sAddress, GetNetAddrPort(adrFrom));
1774 //NetAddrToString(svr->sAddress, svr->szAddress);
1775 }
1776 svr->ports.clear();
1777 svr->ports.push_back( std::make_pair( (int)GetNetAddrPort(adrFrom), -1 ) );
1778
1779 } else {
1780
1781 // If we didn't ping this server directly (eg, subnet), add the server to the list
1782 // HINT: in favourites list, only user should add servers
1783 if (iNetMode != net_favourites) {
1784 NetAddrToString( adrFrom, buf );
1785 svr = Menu_SvrList_AddServer(buf, false);
1786
1787 if( svr ) {
1788
1789 // Only update the list if this is the first ping
1790 if(!svr->bgotPong)
1791 update = true;
1792
1793 // Set it the ponged
1794 svr->bgotPong = true;
1795 svr->nQueries = 0;
1796
1797 //Menu_SvrList_RemoveDuplicateNATServers(svr); // We don't know the name of server yet
1798 }
1799 }
1800 }
1801 }
1802
1803 // Check for a query return
1804 else if(cmd == "lx::queryreturn") {
1805
1806 // Look the the list and find which server returned the ping
1807 server_t *svr = Menu_SvrList_FindServer(adrFrom);
1808 if( svr ) {
1809 bool ipv6 = AreNetAddrEqual(adrFrom, svr->sAddress6);
1810
1811 // Only update the list if this is the first query
1812 if(ipv6) {
1813 if(!svr->bgotQuery6)
1814 update = true;
1815 } else {
1816 if(!svr->bgotQuery)
1817 update = true;
1818 }
1819
1820 svr->bBehindNat = false;
1821 Menu_SvrList_ParseQuery(svr, bs, ipv6);
1822
1823 }
1824
1825 // If we didn't query this server, then we should ignore it
1826 }
1827
1828 else if(cmd == "lx::serverlist2") // This should not happen, we have another thread for polling UDP servers
1829 {
1830 Menu_SvrList_ParseUdpServerlist(bs, 0);
1831 update = true;
1832 }
1833 else if(cmd == "lx::serverlist3") // This should not happen, we have another thread for polling UDP servers
1834 {
1835 Menu_SvrList_ParseUdpServerlist(bs, 0, true);
1836 update = true;
1837 }
1838
1839 }
1840
1841 return update;
1842 }
1843
1844
1845 ///////////////////
1846 // Find a server from the list by address
Menu_SvrList_FindServer(const NetworkAddr & addr,const std::string & name)1847 server_t *Menu_SvrList_FindServer(const NetworkAddr& addr, const std::string & name)
1848 {
1849 for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1850 {
1851 if( AreNetAddrEqual( addr, s->sAddress ) )
1852 return &(*s);
1853 if( AreNetAddrEqual( addr, s->sAddress6 ) )
1854 return &(*s);
1855 }
1856
1857 NetworkAddr addr1 = addr;
1858 SetNetAddrPort(addr1, LX_PORT);
1859
1860 for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
1861 {
1862 // Check if any port number match from the server entry
1863 NetworkAddr addr2 = s->sAddress;
1864 for( size_t i = 0; i < s->ports.size(); i++ )
1865 {
1866 SetNetAddrPort(addr2, s->ports[i].first);
1867 if( AreNetAddrEqual( addr, addr2 ) )
1868 return &(*s);
1869 }
1870
1871 // Check if IP without port and name match
1872 SetNetAddrPort(addr2, LX_PORT);
1873 if( AreNetAddrEqual( addr1, addr2 ) && name == s->szName && name != "Untitled" )
1874 return &(*s);
1875 }
1876
1877 // None found
1878 return NULL;
1879 }
1880
1881
1882 ///////////////////
1883 // Parse the server query return packet
Menu_SvrList_ParseQuery(server_t * svr,CBytestream * bs,bool ipv6)1884 void Menu_SvrList_ParseQuery(server_t *svr, CBytestream *bs, bool ipv6)
1885 {
1886 // TODO: move this net protocol stuff out here
1887
1888 // Don't update the name in favourites
1889 std::string buf = Utf8String(bs->readString());
1890 if(iNetMode != net_favourites)
1891 svr->szName = buf;
1892 TrimSpaces(svr->szName);
1893 //hints << "Menu_SvrList_ParseQuery(): " << svr->szName << " " << svr->szAddress << endl;
1894 svr->nNumPlayers = bs->readByte();
1895 svr->nMaxPlayers = bs->readByte();
1896 svr->nState = bs->readByte();
1897 int num = bs->readByte();
1898 svr->bProcessing = false;
1899 svr->bAllowConnectDuringGame = false;
1900 svr->tVersion.reset();
1901
1902 if(num < 0 || num >= MAX_QUERIES-1)
1903 num=0;
1904
1905 int ping = (int)( (tLX->currentTime - svr->fQueryTimes[num]).milliseconds() );
1906
1907 if (ipv6) {
1908 if (svr->bgotQuery6 && svr->nPing6 > ping)
1909 svr->nPing6 = ping;
1910 svr->bgotQuery6 = true;
1911 } else {
1912 if (svr->bgotQuery && svr->nPing > ping)
1913 svr->nPing4 = ping;
1914 svr->nPing4 = ping;
1915 svr->bgotQuery = true;
1916 }
1917
1918 if (svr->nPing4 < svr->nPing6)
1919 {
1920 svr->nPing = svr->nPing4;
1921 NetAddrToString(svr->sAddress, svr->szAddress);
1922 }
1923 else
1924 {
1925 svr->nPing = svr->nPing6;
1926 NetAddrToString(svr->sAddress6, svr->szAddress);
1927 }
1928
1929 if(svr->nPing < 0)
1930 svr->nPing = 999;
1931 if(svr->nPing > 999)
1932 svr->nPing = 999;
1933
1934 if( !bs->isPosAtEnd() )
1935 {
1936 // Beta8+
1937 svr->tVersion.setByString( bs->readString(64) );
1938 svr->bAllowConnectDuringGame = bs->readBool();
1939 }
1940 //hints << "Menu_SvrList_ParseQuery(): " << svr->szName << " addr " << svr->szAddress << " v4 " << NetAddrToString(svr->sAddress) <<
1941 // " v6 " << NetAddrToString(svr->sAddress6) << " ping " << svr->nPing << " ping4 " << svr->nPing4 << " ping6 " << svr->nPing6 << endl;
1942 // We got server name in a query. let's remove servers with the same name and IP, which we got from UDP masterserver
1943 // Duplicates should be detected in AddServer(), but some servers fail detection because of different port
1944 for(std::list<server_t>::iterator it = psServerList.begin(); it != psServerList.end(); it++)
1945 {
1946 NetworkAddr addr1 = it->sAddress;
1947 SetNetAddrPort(addr1, LX_PORT);
1948 NetworkAddr addr2 = svr->sAddress;
1949 SetNetAddrPort(addr2, LX_PORT);
1950 if( it->szName == svr->szName && AreNetAddrEqual(addr1, addr2) && svr != &(*it) )
1951 {
1952 //Duplicate server - delete it
1953 //hints << "Menu_SvrList_ParseQuery(): removing duplicate " << it->szName << " " << it->szAddress << endl;
1954 if (IsNetAddrValid(it->sAddress6) && !IsNetAddrValid(svr->sAddress6)) {
1955 svr->sAddress6 = it->sAddress6;
1956 svr->nPing6 = it->nPing6;
1957 svr->bgotQuery6 = it->bgotQuery6;
1958 }
1959 psServerList.erase(it);
1960 it = psServerList.begin();
1961 }
1962 }
1963 }
1964
1965 /*************************
1966 *
1967 * UDP server list
1968 *
1969 ************************/
1970
1971 static std::list<std::string> tUdpMasterServers;
1972 static std::map<size_t, ThreadPoolItem *> tUpdateThreads;
1973 static size_t threadId = 0;
1974
1975 struct UdpServerlistData {
1976 CBytestream *bs;
1977 int UdpServerIndex;
1978 bool v4addrIncluded;
UdpServerlistDataDeprecatedGUI::UdpServerlistData1979 UdpServerlistData(CBytestream *b, int _UdpServerIndex, bool _v4addrIncluded) : bs(b), UdpServerIndex(_UdpServerIndex), v4addrIncluded(_v4addrIncluded) {}
1980 };
1981
Menu_UpdateUDPListEventHandler(UdpServerlistData data)1982 void Menu_UpdateUDPListEventHandler(UdpServerlistData data)
1983 {
1984 if (iNetMode == net_internet) // Only add them if the Internet tab is active
1985 Menu_SvrList_ParseUdpServerlist(data.bs, data.UdpServerIndex, data.v4addrIncluded);
1986 delete data.bs;
1987 }
1988
Menu_UpdateUDPListEnd(size_t thread)1989 void Menu_UpdateUDPListEnd(size_t thread)
1990 {
1991 std::map<size_t, ThreadPoolItem *>::iterator it = tUpdateThreads.find(thread);
1992 if (it != tUpdateThreads.end())
1993 threadPool->wait(it->second, NULL);
1994 }
1995
1996 Event<UdpServerlistData> serverlistEvent;
1997 Event<size_t> updateEndEvent;
Menu_SvrList_UpdaterThread(void * id)1998 int Menu_SvrList_UpdaterThread(void *id)
1999 {
2000 // Setup event handlers
2001 updateEndEvent.handler() = getEventHandler(&Menu_UpdateUDPListEnd);
2002 serverlistEvent.handler() = getEventHandler(&Menu_UpdateUDPListEventHandler);
2003
2004 // Open socket for networking
2005 NetworkSocket sock;
2006 if (!sock.OpenUnreliable(0)) {
2007 updateEndEvent.pushToMainQueue((size_t)id);
2008 return -1;
2009 }
2010
2011 // Get serverlist from all the servers in the file
2012 int UdpServerIndex = 0;
2013 for (std::list<std::string>::iterator it = tUdpMasterServers.begin(); it != tUdpMasterServers.end(); ++it, ++UdpServerIndex)
2014 {
2015 std::string& server = *it;
2016 NetworkAddr addr, addr6;
2017 if (server.rfind(':') == std::string::npos)
2018 server += ":23450"; // Default port
2019
2020 // Split to domain and port
2021 std::string domain = server.substr(0, server.rfind(':'));
2022 int port = atoi(server.substr(server.rfind(':') + 1));
2023
2024 // Resolve the address
2025 if( ! GetFromDnsCache( domain, addr, addr6 ) )
2026 {
2027 GetNetAddrFromNameAsync(domain);
2028 }
2029
2030 AbsTime start = GetTime();
2031 while (GetTime() - start <= 5.0f) {
2032 SDL_Delay(40);
2033 if( GetFromDnsCache( domain, addr, addr6 ) )
2034 break;
2035 }
2036
2037 if( !IsNetAddrValid(addr) && !IsNetAddrValid(addr6) )
2038 {
2039 notes << "UDP masterserver failed: cannot resolve domain name " << domain << endl;
2040 continue;
2041 }
2042
2043 // Setup the socket
2044 SetNetAddrPort(addr, port);
2045 SetNetAddrPort(addr6, port);
2046
2047 for (int af = 0; af < 2; af++, addr = addr6)
2048 {
2049 if (!IsNetAddrValid(addr))
2050 continue;
2051
2052 sock.setRemoteAddress(addr);
2053
2054 // Send the getserverlist packet
2055 CBytestream *bs = new CBytestream();
2056 bs->writeInt(-1, 4);
2057 bs->writeString(af ? "lx::getserverlist3" : "lx::getserverlist2");
2058 if(!bs->Send(&sock))
2059 {
2060 delete bs;
2061 warnings << "error while sending data to " << server << (af ? " over IPv6" : " over IPv4") << endl;
2062 continue;
2063 }
2064 bs->Clear();
2065
2066 //notes << "Sent getserverlist to " << server << endl;
2067
2068 // Wait for the reply
2069 AbsTime timeoutTime = GetTime() + 5.0f;
2070 bool firstPacket = true;
2071 while( true ) {
2072
2073 while (GetTime() <= timeoutTime) {
2074 SDL_Delay(40); // TODO: do it event based
2075
2076 // Got a reply?
2077 if (bs->Read(&sock)) {
2078 //notes << "Got a reply from " << server << endl;
2079 break;
2080 }
2081
2082
2083 }
2084
2085 // Parse the reply
2086 std::string response;
2087 if (bs->GetLength() && bs->readInt(4) == -1 && (response = bs->readString()).find("lx::serverlist") == 0) {
2088 serverlistEvent.pushToMainQueue(UdpServerlistData(bs, UdpServerIndex, response == "lx::serverlist3"));
2089 timeoutTime = GetTime() + 0.5f; // Check for another packet
2090 bs = new CBytestream(); // old bs pointer is in mainqueue now
2091 firstPacket = false;
2092 } else {
2093 if( firstPacket )
2094 warnings << "Error getting serverlist from " << server << endl;
2095 delete bs;
2096 break;
2097 }
2098 }
2099 }
2100 }
2101
2102 // Cleanup
2103 sock.Close();
2104
2105 updateEndEvent.pushToMainQueue((size_t)id);
2106 return 0;
2107 }
2108
Menu_SvrList_UpdateUDPList()2109 void Menu_SvrList_UpdateUDPList()
2110 {
2111 if (tUdpMasterServers.size() == 0) { // Load the list of servers only if not already loaded
2112 // Open the masterservers file
2113 FILE *fp1 = OpenGameFile("cfg/udpmasterservers.txt", "rt");
2114 if(!fp1) {
2115 warnings << "could not open udpmasterservers.txt file, NAT traversal will be inaccessible" << endl;
2116 return;
2117 }
2118
2119 // Get the list of servers
2120 while( !feof(fp1) ) {
2121 std::string szLine = ReadUntil(fp1);
2122 TrimSpaces(szLine);
2123
2124 if( szLine.length() == 0 )
2125 continue;
2126
2127 tUdpMasterServers.push_back(szLine);
2128 }
2129 fclose(fp1);
2130 }
2131
2132 // Run the update
2133 ThreadPoolItem *thread = threadPool->start(Menu_SvrList_UpdaterThread, (void *)(++threadId), "serverlist updater");
2134 tUpdateThreads[threadId] = thread;
2135 }
2136
Menu_SvrList_ParseUdpServerlist(CBytestream * bs,int UdpMasterserverIndex,bool v4AddressIncluded)2137 void Menu_SvrList_ParseUdpServerlist(CBytestream *bs, int UdpMasterserverIndex, bool v4AddressIncluded)
2138 {
2139 // Look the the list and find which server returned the ping
2140 int amount = bs->readByte();
2141 //notes << "Menu_SvrList_ParseUdpServerlist " << amount << endl;
2142 for( int f=0; f<amount; f++ )
2143 {
2144 std::string addr = bs->readString();
2145 std::string name = bs->readString();
2146 TrimSpaces(name);
2147 TrimSpaces(addr);
2148 //hints << "Menu_SvrList_ParseUdpServerlist(): " << name << " " << addr << endl;
2149 int players = bs->readByte();
2150 int maxplayers = bs->readByte();
2151 int state = bs->readByte();
2152 Version version = bs->readString(64);
2153 bool allowConnectDuringGame = bs->readBool();
2154 std::string v4address;
2155 if( v4AddressIncluded )
2156 v4address = bs->readString();
2157 // UDP server info is updated once per 40 seconds, so if we have more recent entry - ignore it
2158 server_t *svr = Menu_SvrList_FindServerStr(addr, name);
2159 if( svr != NULL )
2160 {
2161 //hints << "Menu_SvrList_ParseUdpServerlist(): got duplicate " << name << " " << addr << " pong " << svr->bgotPong << " query " << svr->bgotQuery << endl;
2162 if( svr->bgotPong && IsNetAddrValid(svr->sAddress) && IsNetAddrValid(svr->sAddress6) )
2163 continue;
2164 // It will merge existing server with new info
2165 Menu_SvrList_AddServer(addr, false, name, UdpMasterserverIndex, v4address);
2166 continue;
2167 }
2168
2169 // In favourites/LAN only the user should add servers
2170 if (iNetMode == net_internet) {
2171 svr = Menu_SvrList_AddServer( addr, false, name, UdpMasterserverIndex, v4address );
2172 svr->nNumPlayers = players;
2173 svr->nMaxPlayers = maxplayers;
2174 svr->nState = state;
2175 svr->nPing = -2;
2176 svr->nPing4 = svr->nPing6 = 9999;
2177 svr->nQueries = 0;
2178 svr->bgotPong = false;
2179 svr->bgotQuery = false;
2180 svr->bgotQuery6 = false;
2181 svr->bProcessing = false;
2182 svr->tVersion = version;
2183 svr->bAllowConnectDuringGame = allowConnectDuringGame;
2184 svr->bBehindNat = true;
2185 }
2186 };
2187
2188 bUpdateFromUdpThread = true;
2189 // Update the GUI when ping times out
2190 Timer("Menu_SvrList_ParseUdpServerlist ping waiter", null, NULL, PingWait, true).startHeadless();
2191 };
2192
2193 ///////////////////
2194 // Save the server list
Menu_SvrList_SaveList(const std::string & szFilename)2195 void Menu_SvrList_SaveList(const std::string& szFilename)
2196 {
2197 FILE *fp = OpenGameFile(szFilename,"wt");
2198 if( !fp )
2199 return;
2200
2201 for(std::list<server_t>::iterator s = psServerList.begin(); s != psServerList.end(); s++)
2202 {
2203 int UdpMasterServer = -1;
2204 for( size_t port = 0; s->bBehindNat && port < s->ports.size() && UdpMasterServer == -1; port++ )
2205 if( s->ports[port].second >= 0 )
2206 UdpMasterServer = s->ports[port].second;
2207
2208 fprintf(fp, "%s, %s, %s",s->bManual ? "1" : "0", s->szName.c_str(), s->szAddress.c_str() );
2209 if( UdpMasterServer != -1 && !s->bManual )
2210 fprintf(fp, ", %i", UdpMasterServer );
2211 fprintf(fp, "\n" );
2212 }
2213
2214 fclose(fp);
2215 }
2216
2217 ///////////////////
2218 // Add a favourite server
Menu_SvrList_AddFavourite(const std::string & szName,const std::string & szAddress)2219 void Menu_SvrList_AddFavourite(const std::string& szName, const std::string& szAddress)
2220 {
2221 FILE *fp = OpenGameFile("cfg/favourites.dat","a"); // We're appending
2222 if( !fp ) {
2223 fp = OpenGameFile("cfg/favourites.dat","wb"); // Try to create the file
2224 if (!fp)
2225 return;
2226 }
2227
2228 // Append the server
2229 fprintf(fp,"%s, %s, %s\n","1", szName.c_str(), szAddress.c_str());
2230
2231 fclose(fp);
2232 }
2233
2234
2235 ///////////////////
2236 // Load the server list
Menu_SvrList_LoadList(const std::string & szFilename)2237 void Menu_SvrList_LoadList(const std::string& szFilename)
2238 {
2239 FILE *fp = OpenGameFile(szFilename,"rt");
2240 if( !fp )
2241 return;
2242
2243 // Go through every line
2244 while( !feof(fp) ) {
2245 std::string szLine = ReadUntil(fp);
2246 if( szLine == "" )
2247 continue;
2248
2249 // explode and copy it
2250 std::vector<std::string> parsed = explode(szLine,",");
2251
2252 if( parsed.size() >= 3 ) {
2253 TrimSpaces(parsed[0]);
2254 TrimSpaces(parsed[1]);
2255 TrimSpaces(parsed[2]); // Address
2256
2257 int UdpMasterServer = -1;
2258 if( parsed.size() >= 4 )
2259 UdpMasterServer = atoi(parsed[3]);
2260
2261 Menu_SvrList_AddServer(parsed[2], parsed[0] == "1", parsed[1], UdpMasterServer);
2262 }
2263 }
2264
2265 // Update the GUI after the ping timed out
2266 Timer("Menu_SvrList_LoadList ping waiter", null, NULL, PingWait, true).startHeadless();
2267
2268 fclose(fp);
2269 }
2270
Menu_SvrList_GetUdpMasterserverForServer(const std::string & addr)2271 std::string Menu_SvrList_GetUdpMasterserverForServer(const std::string & addr)
2272 {
2273 server_t * svr = Menu_SvrList_FindServerStr(addr);
2274 if( !svr )
2275 return "";
2276 if( !svr->bBehindNat )
2277 return "";
2278
2279 for( size_t port = 0; port < svr->ports.size(); port++ )
2280 {
2281 if( svr->ports[port].second < 0 )
2282 continue;
2283 int idx = 0;
2284 for( std::list<std::string>::iterator it = tUdpMasterServers.begin(); it != tUdpMasterServers.end(); ++it, ++idx )
2285 if( idx == svr->ports[port].second )
2286 return *it;
2287 }
2288
2289 return "";
2290 }
2291
2292
2293 bool bGotDetails = false;
2294 bool bOldLxBug = false;
2295 int nTries = 0;
2296 AbsTime fStart;
2297 CListview lvInfo;
2298
2299
2300 ///////////////////
2301 // Draw a 'server info' box
Menu_SvrList_DrawInfo(const std::string & szAddress,int w,int h)2302 void Menu_SvrList_DrawInfo(const std::string& szAddress, int w, int h)
2303 {
2304 int y = tMenu->bmpBuffer.get()->h/2 - h/2;
2305 int x = tMenu->bmpBuffer.get()->w/2 - w/2;
2306
2307 Menu_redrawBufferRect(x,y,w,h);
2308
2309 Menu_DrawBox(VideoPostProcessor::videoSurface(), x,y, x+w, y+h);
2310 DrawRectFillA(VideoPostProcessor::videoSurface(), x+2,y+2, x+w-1, y+h-1, tLX->clDialogBackground, 230);
2311 tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+5, tLX->clNormalLabel, "Server Details");
2312
2313
2314 server_t* svr = Menu_SvrList_FindServerStr(szAddress);
2315 NetworkAddr origAddr;
2316 if(svr) {
2317 if(IsNetAddrValid(svr->sAddress)) {
2318 origAddr = svr->sAddress;
2319 } else {
2320 tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+h/2-8, tLX->clNormalLabel, "Resolving domain ...");
2321 return;
2322 }
2323 } else {
2324 warnings << "Querying server not from svr list: " << szAddress << endl;
2325 std::string tmp_addr = szAddress;
2326 TrimSpaces(tmp_addr);
2327 if(!StringToNetAddr(tmp_addr, origAddr)) {
2328 // TODO: this happens also, if the server is not in the serverlist
2329 // we should do the domain resolving also here by ourselfs
2330 tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2,y+tLX->cFont.GetHeight()+10, tLX->clError, "DNS not resolved");
2331 return;
2332 }
2333 }
2334
2335
2336 // Get the server details
2337 std::string szName;
2338 int nMaxWorms = 0;
2339 int nState = 0;
2340 std::string szMapName;
2341 std::string szModName;
2342 int nGameMode = 0;
2343 int nLives = 0;
2344 int nMaxKills = 0;
2345 int nLoadingTime = 0;
2346 int nBonuses = 0;
2347 int nNumPlayers = 0;
2348 IpInfo tIpInfo;
2349 std::string sIP;
2350 CWorm cWorms[MAX_WORMS];
2351 bool bHaveLives = false;
2352 std::string sServerVersion;
2353 bool bHaveGameSpeed = false;
2354 float fGameSpeed = 1.0f;
2355 FeatureCompatibleSettingList features;
2356
2357 CBytestream inbs;
2358 NetworkAddr addr;
2359
2360 if(nTries < 3 && !bGotDetails && !bOldLxBug) {
2361
2362 tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2, y+h/2-8, tLX->clNormalLabel, "Loading info...");
2363
2364 if (inbs.Read(tMenu->tSocket[SCK_NET])) {
2365 // Check for connectionless packet header
2366 if(inbs.readInt(4) == -1) {
2367 std::string cmd = inbs.readString();
2368
2369 addr = tMenu->tSocket[SCK_NET]->remoteAddress();
2370
2371 if(cmd == "lx::traverse") // Response from UDP masterserver
2372 {
2373 sIP = inbs.readString();
2374 StringToNetAddr(sIP, addr);
2375 if( !inbs.isPosAtEnd() )
2376 cmd = inbs.readString();
2377 }
2378
2379 // Check for server info
2380 if(cmd == "lx::serverinfo") {
2381 bGotDetails = true;
2382
2383 sServerVersion = "LieroX 0.56";
2384
2385 // Get the IP info
2386 if (NetAddrToString(addr, sIP))
2387 tIpInfo = tIpToCountryDB->GetInfoAboutIP(sIP);
2388 else {
2389 tIpInfo.countryName = "Hackerland";
2390 tIpInfo.continent = "Hackerland";
2391 sIP = "x.x.x.x";
2392 }
2393
2394
2395 // Read the info
2396 szName = Utf8String(inbs.readString(64));
2397 nMaxWorms = MIN(MAX_PLAYERS, MAX((int)inbs.readByte(), 0));
2398 nState = inbs.readByte();
2399
2400 if (nState < 0) {
2401 bOldLxBug = true;
2402 }
2403
2404 szMapName = inbs.readString(256);
2405 // Adjust the map name
2406 if (szMapName.find("levels/") == 0)
2407 szMapName.erase(0,7); // Remove the path if present
2408 szMapName = CMap::GetLevelName(szMapName);
2409
2410
2411 szModName = inbs.readString(256);
2412 nGameMode = inbs.readByte();
2413 nLives = inbs.readInt16();
2414 nMaxKills = inbs.readInt16();
2415 nLoadingTime = inbs.readInt16();
2416 if(nLoadingTime < 0) {
2417 bOldLxBug = true;
2418 }
2419 nBonuses = inbs.readByte();
2420
2421 // Worms
2422 nNumPlayers = inbs.readByte();
2423 if (nNumPlayers < 0) {
2424 bOldLxBug = true;
2425 }
2426
2427 // Check
2428 nNumPlayers = MIN(nMaxWorms,nNumPlayers);
2429
2430 int i;
2431 for(i=0; i<nNumPlayers; i++) {
2432 cWorms[i].setName(inbs.readString());
2433 cWorms[i].setKills(inbs.readInt(2));
2434 }
2435
2436 if (nState == 1 && !bOldLxBug) { // Loading and no bug? Must be a fixed version -> LXP/OLX b1 or 2
2437 sServerVersion = "LXP or OLX beta1/beta2";
2438 }
2439
2440 // Lives (only OLX servers)
2441 if(!inbs.isPosAtEnd()) {
2442 sServerVersion = "OpenLieroX/0.57_Beta3";
2443 bHaveLives = true;
2444 for(i=0; i<nNumPlayers; i++)
2445 cWorms[i].setLives(inbs.readInt(2));
2446 }
2447
2448 // IPs
2449 if(!inbs.isPosAtEnd()) {
2450 sServerVersion = "OpenLieroX/0.57_Beta4";
2451 for(i=0; i<nNumPlayers; i++)
2452 inbs.readString(); // ignore
2453 }
2454
2455 if(!inbs.isPosAtEnd()) {
2456 sServerVersion = inbs.readString();
2457 }
2458
2459 if(!inbs.isPosAtEnd()) {
2460 bHaveGameSpeed = true;
2461 fGameSpeed = inbs.readFloat();
2462 }
2463
2464 // since Beta9
2465 if(!inbs.isPosAtEnd()) {
2466 int ftC = inbs.readInt(2);
2467 for(int i = 0; i < ftC; ++i) {
2468 std::string name = inbs.readString();
2469 std::string humanName = inbs.readString();
2470 ScriptVar_t value; inbs.readVar(value);
2471 bool olderClientsSupported = inbs.readBool();
2472 Feature* f = featureByName(name);
2473 if(f) {
2474 features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_SUPPORTED);
2475 } else if(!olderClientsSupported) {
2476 features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_JUSTUNKNOWN);
2477 } else {
2478 features.set(name, humanName, value, FeatureCompatibleSettingList::Feature::FCSL_INCOMPATIBLE);
2479 }
2480 }
2481 }
2482
2483 }
2484 }
2485 }
2486
2487 if((tLX->currentTime - fStart > 1) && !bGotDetails) {
2488 nTries++;
2489 fStart = tLX->currentTime;
2490 bGotDetails = false;
2491 bOldLxBug = false;
2492
2493 if(svr)
2494 Menu_SvrList_GetServerInfo(svr);
2495 }
2496
2497 // Got details, fill in the listview
2498 if (bGotDetails && !bOldLxBug) {
2499
2500 // States and gamemodes
2501 const std::string states[] = {"Open", "Loading", "Playing", "Unknown"};
2502 const std::string gamemodes[] = {"Deathmatch","Team Deathmatch", "Tag", "Demolitions", "Unknown"};
2503
2504
2505 // Checks
2506 if (nState < 0 || nState > 2)
2507 nState = 3;
2508 if (nGameMode < 0 || nGameMode > 3)
2509 nGameMode = 4;
2510 nNumPlayers = MIN(nNumPlayers, MAX_WORMS-1);
2511
2512
2513 // Setup the listview
2514 lvInfo.Setup(0, x + 15, y+5, w - 30, h - 25);
2515 lvInfo.setDrawBorder(false);
2516 lvInfo.setRedrawMenu(false);
2517 lvInfo.setShowSelect(false);
2518 lvInfo.setOldStyle(true);
2519 // TODO: will the listview have scrollbars if too long? if not, please add this
2520
2521 lvInfo.Destroy(); // Clear any old info
2522
2523 // Columns
2524 int first_column_width = tLX->cFont.GetWidth("Loading Times:") + 30; // Width of the widest item in this column + some space
2525 int last_column_width = tLX->cFont.GetWidth("999"); // Kills width
2526 lvInfo.AddColumn("", first_column_width);
2527 lvInfo.AddColumn("", lvInfo.getWidth() - first_column_width - (last_column_width*2) - gfxGUI.bmpScrollbar.get()->w); // The rest
2528 lvInfo.AddColumn("", last_column_width);
2529 lvInfo.AddColumn("", last_column_width);
2530
2531 int index = 0; // Current item index
2532
2533 // Server name
2534 lvInfo.AddItem("servername", index, tLX->clNormalLabel);
2535 lvInfo.AddSubitem(LVS_TEXT, "Server name:", (DynDrawIntf*)NULL, NULL);
2536 lvInfo.AddSubitem(LVS_TEXT, szName, (DynDrawIntf*)NULL, NULL);
2537
2538 // server version
2539 lvInfo.AddItem("serverversion", index, tLX->clNormalLabel);
2540 lvInfo.AddSubitem(LVS_TEXT, "Server version:", (DynDrawIntf*)NULL, NULL);
2541 lvInfo.AddSubitem(LVS_TEXT, sServerVersion, (DynDrawIntf*)NULL, NULL);
2542
2543 // Country and continent
2544 lvInfo.AddItem("country", ++index, tLX->clNormalLabel);
2545 lvInfo.AddSubitem(LVS_TEXT, "Location:", (DynDrawIntf*)NULL, NULL);
2546 if (tIpInfo.hasCityLevel)
2547 lvInfo.AddSubitem(LVS_TEXT, tIpInfo.city + ", " + tIpInfo.countryName + " (" + tIpInfo.continent + ")", (DynDrawIntf*)NULL, NULL);
2548 else
2549 lvInfo.AddSubitem(LVS_TEXT, tIpInfo.countryName + " (" + tIpInfo.continent + ")", (DynDrawIntf*)NULL, NULL);
2550
2551 // IP address
2552 lvInfo.AddItem("ip", ++index, tLX->clNormalLabel);
2553 lvInfo.AddSubitem(LVS_TEXT, "IP Address:", (DynDrawIntf*)NULL, NULL);
2554 lvInfo.AddSubitem(LVS_TEXT, sIP, (DynDrawIntf*)NULL, NULL);
2555
2556 // Map name
2557 lvInfo.AddItem("mapname", ++index, tLX->clNormalLabel);
2558 lvInfo.AddSubitem(LVS_TEXT, "Level name:", (DynDrawIntf*)NULL, NULL);
2559 lvInfo.AddSubitem(LVS_TEXT, szMapName, (DynDrawIntf*)NULL, NULL);
2560
2561 // Mod name
2562 lvInfo.AddItem("modname", ++index, tLX->clNormalLabel);
2563 lvInfo.AddSubitem(LVS_TEXT, "Mod name:", (DynDrawIntf*)NULL, NULL);
2564 lvInfo.AddSubitem(LVS_TEXT, szModName, (DynDrawIntf*)NULL, NULL);
2565
2566 // State
2567 lvInfo.AddItem("state", ++index, tLX->clNormalLabel);
2568 lvInfo.AddSubitem(LVS_TEXT, "State:", (DynDrawIntf*)NULL, NULL);
2569 lvInfo.AddSubitem(LVS_TEXT, states[nState], (DynDrawIntf*)NULL, NULL);
2570
2571 // Playing
2572 lvInfo.AddItem("playing", ++index, tLX->clNormalLabel);
2573 lvInfo.AddSubitem(LVS_TEXT, "Playing:", (DynDrawIntf*)NULL, NULL);
2574 lvInfo.AddSubitem(LVS_TEXT, itoa(nNumPlayers) + " / " + itoa(nMaxWorms), (DynDrawIntf*)NULL, NULL);
2575
2576 // Game type
2577 lvInfo.AddItem("game type", ++index, tLX->clNormalLabel);
2578 lvInfo.AddSubitem(LVS_TEXT, "Game Type:", (DynDrawIntf*)NULL, NULL);
2579 lvInfo.AddSubitem(LVS_TEXT, gamemodes[nGameMode], (DynDrawIntf*)NULL, NULL);
2580
2581 // Lives
2582 lvInfo.AddItem("lives", ++index, tLX->clNormalLabel);
2583 lvInfo.AddSubitem(LVS_TEXT, "Lives:", (DynDrawIntf*)NULL, NULL);
2584 if (nLives < 0)
2585 lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2586 else
2587 lvInfo.AddSubitem(LVS_TEXT, itoa(nLives), (DynDrawIntf*)NULL, NULL);
2588
2589 // Max kills
2590 lvInfo.AddItem("maxkills", ++index, tLX->clNormalLabel);
2591 lvInfo.AddSubitem(LVS_TEXT, "Max Kills:", (DynDrawIntf*)NULL, NULL);
2592 if (nMaxKills < 0)
2593 lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2594 else
2595 lvInfo.AddSubitem(LVS_TEXT, itoa(nMaxKills), (DynDrawIntf*)NULL, NULL);
2596
2597 // Loading times
2598 lvInfo.AddItem("loading", ++index, tLX->clNormalLabel);
2599 lvInfo.AddSubitem(LVS_TEXT, "Loading Times:", (DynDrawIntf*)NULL, NULL);
2600 lvInfo.AddSubitem(LVS_TEXT, itoa(nLoadingTime) + " %", (DynDrawIntf*)NULL, NULL);
2601
2602 // Separator
2603 lvInfo.AddItem("", ++index, tLX->clNormalLabel);
2604
2605 // Players / kills / lives
2606 lvInfo.AddItem("players", ++index, tLX->clNormalLabel);
2607 if (nState) {
2608 lvInfo.AddSubitem(LVS_TEXT, "Players/Kills/Lives:", (DynDrawIntf*)NULL, NULL);
2609
2610 // First player (located next to the Players/Kills/Lives label)
2611 lvInfo.AddSubitem(LVS_TEXT, cWorms[0].getName(), (DynDrawIntf*)NULL, NULL);
2612 lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[0].getKills()), (DynDrawIntf*)NULL, NULL);
2613 if (bHaveLives) {
2614 switch ((short)cWorms[0].getLives()) {
2615 case -1: // Out
2616 lvInfo.AddSubitem(LVS_TEXT, "Out", (DynDrawIntf*)NULL, NULL);
2617 break;
2618 case -2: // Unlim
2619 lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2620 break;
2621 default:
2622 lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[0].getLives()), (DynDrawIntf*)NULL, NULL);
2623 }
2624 }
2625
2626 // Rest of the players
2627 for (int i=1; i < nNumPlayers; i++) {
2628 lvInfo.AddItem("players"+itoa(i+1), ++index, tLX->clNormalLabel);
2629 lvInfo.AddSubitem(LVS_TEXT, "", (DynDrawIntf*)NULL, NULL);
2630 lvInfo.AddSubitem(LVS_TEXT, cWorms[i].getName(), (DynDrawIntf*)NULL, NULL);
2631 lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[i].getKills()), (DynDrawIntf*)NULL, NULL);
2632 if (bHaveLives) {
2633 switch ((short)cWorms[i].getLives()) {
2634 case -1: // Out
2635 lvInfo.AddSubitem(LVS_TEXT, "Out", (DynDrawIntf*)NULL, NULL);
2636 break;
2637 case -2: // Unlim
2638 lvInfo.AddSubitem(LVS_IMAGE, "", gfxGame.bmpInfinite, NULL);
2639 break;
2640 default:
2641 lvInfo.AddSubitem(LVS_TEXT, itoa(cWorms[i].getLives()), (DynDrawIntf*)NULL, NULL);
2642 }
2643 }
2644 }
2645 }
2646
2647 else { // Don't draw kills when the server is open
2648 lvInfo.AddSubitem(LVS_TEXT, "Players:", (DynDrawIntf*)NULL, NULL);
2649
2650 // First player (located next to the Players/Kills label)
2651 lvInfo.AddSubitem(LVS_TEXT, cWorms[0].getName(), (DynDrawIntf*)NULL, NULL);
2652
2653 // Rest of the players
2654 for (int i = 1; i < nNumPlayers; i++) {
2655 lvInfo.AddItem("players"+itoa(i+1), ++index, tLX->clNormalLabel);
2656 lvInfo.AddSubitem(LVS_TEXT, "", (DynDrawIntf*)NULL, NULL);
2657 lvInfo.AddSubitem(LVS_TEXT, cWorms[i].getName(), (DynDrawIntf*)NULL, NULL);
2658 }
2659 }
2660
2661 // Separator
2662 lvInfo.AddItem("", ++index, tLX->clNormalLabel);
2663
2664 // Bonuses
2665 lvInfo.AddItem("bonuses", ++index, tLX->clNormalLabel);
2666 lvInfo.AddSubitem(LVS_TEXT, "Bonuses:", (DynDrawIntf*)NULL, NULL);
2667 lvInfo.AddSubitem(LVS_TEXT, nBonuses ? "On" : "Off", (DynDrawIntf*)NULL, NULL);
2668
2669 if(bHaveGameSpeed) {
2670 // Loading times
2671 lvInfo.AddItem("gamespeed", ++index, tLX->clNormalLabel);
2672 lvInfo.AddSubitem(LVS_TEXT, "Game speed:", (DynDrawIntf*)NULL, NULL);
2673 lvInfo.AddSubitem(LVS_TEXT, ftoa(fGameSpeed), (DynDrawIntf*)NULL, NULL);
2674 }
2675
2676 foreach( FeatureCompatibleSettingList::Feature&, it, features.list ){
2677 Color col;
2678 switch(it->get().type) {
2679 case FeatureCompatibleSettingList::Feature::FCSL_JUSTUNKNOWN: col = tLX->clDisabled; break;
2680 case FeatureCompatibleSettingList::Feature::FCSL_INCOMPATIBLE: col = tLX->clError; break;
2681 default: col = tLX->clNormalLabel;
2682 }
2683 lvInfo.AddItem("feature:" + it->get().name, ++index, col);
2684 if(tLX->cFont.GetWidth(it->get().humanName + ":") + 20 <= first_column_width) {
2685 lvInfo.AddSubitem(LVS_TEXT, it->get().humanName + ":", (DynDrawIntf*)NULL, NULL);
2686 lvInfo.AddSubitem(LVS_TEXT, it->get().var.toString(), (DynDrawIntf*)NULL, NULL);
2687 } else
2688 lvInfo.AddSubitem(LVS_TEXT, it->get().humanName + ": " + it->get().var.toString(), (DynDrawIntf*)NULL, NULL);
2689 }
2690
2691 }
2692 else // No details yet
2693 return;
2694 }
2695
2696 // No details, server down
2697 if(!bGotDetails) {
2698 tLX->cFont.DrawCentre(VideoPostProcessor::videoSurface(), x+w/2,y+tLX->cFont.GetHeight()+10, tLX->clError, "Unable to query server");
2699 return;
2700 }
2701
2702 // Old bug
2703 if(bOldLxBug) {
2704 tLX->cFont.Draw(VideoPostProcessor::videoSurface(), x+15,y+tLX->cFont.GetHeight()+10, tLX->clError, "You can't view details\nof this server because\nLieroX v0.56 contains a bug.\n\nPlease wait until the server\nchanges its state to Playing\nand try again.");
2705 return;
2706 }
2707
2708 // Process the listview events
2709 mouse_t *Mouse = GetMouse();
2710 if (lvInfo.InBox(Mouse->X, Mouse->Y)) {
2711 lvInfo.MouseOver(Mouse);
2712 if (Mouse->Down)
2713 lvInfo.MouseDown(Mouse, true);
2714 else if (Mouse->Up)
2715 lvInfo.MouseUp(Mouse, false);
2716
2717 if (Mouse->WheelScrollUp)
2718 lvInfo.MouseWheelUp(Mouse);
2719 else if (Mouse->WheelScrollDown)
2720 lvInfo.MouseWheelDown(Mouse);
2721 }
2722
2723 // All ok, draw the details
2724 lvInfo.Draw( VideoPostProcessor::videoSurface() );
2725 }
2726
Menu_Current_Shutdown()2727 void Menu_Current_Shutdown() {
2728 if(!tMenu) return;
2729
2730 // Shutdown all sub-menus
2731 if(!bDedicated)
2732 switch(tMenu->iMenuType) {
2733
2734 // Main
2735 case MNU_MAIN:
2736 Menu_MainShutdown();
2737 break;
2738
2739 // Local
2740 case MNU_LOCAL:
2741 Menu_LocalShutdown();
2742 break;
2743
2744 // News
2745 case MNU_NETWORK:
2746 Menu_NetShutdown();
2747 break;
2748
2749 // Player
2750 case MNU_PLAYER:
2751 Menu_PlayerShutdown();
2752 break;
2753
2754 // Map editor
2755 case MNU_MAPED:
2756 Menu_MapEdShutdown();
2757 break;
2758
2759 // Options
2760 case MNU_OPTIONS:
2761 Menu_OptionsShutdown();
2762 break;
2763 }
2764
2765 /*Menu_MainShutdown();
2766 Menu_LocalShutdown();
2767 Menu_PlayerShutdown();
2768 Menu_MapEdShutdown();
2769 Menu_GameSettingsShutdown();
2770 Menu_WeaponsRestrictionsShutdown();
2771 Menu_WeaponPresetsShutdown();
2772 Menu_BanListShutdown();
2773 Menu_ServerSettingsShutdown();
2774 Menu_OptionsShutdown();
2775 Menu_FloatingOptionsShutdown();
2776 Menu_SpeedTest_Shutdown();
2777 Menu_NetShutdown();
2778 Menu_Net_MainShutdown();
2779 Menu_Net_HostPlyShutdown();
2780 Menu_Net_HostLobbyShutdown();
2781 Menu_Net_LANShutdown();
2782 Menu_Net_JoinShutdown();
2783 Menu_Net_FavouritesShutdown();
2784 Menu_Net_NewsShutdown();
2785 Menu_Net_JoinShutdown();
2786 Menu_Net_ChatShutdown();
2787 Menu_Net_JoinConnectionShutdown();
2788 Menu_Net_JoinLobbyShutdown();
2789 Menu_Net_NETShutdown();
2790 Menu_CGuiSkinShutdown();*/
2791 }
2792
2793 } // namespace DeprecatedGUI
2794