1 // OpenLieroX
2
3 // Main entry point
4 // Created 28/6/02
5 // Jason Boettcher
6
7 // code under LGPL
8
9
10
11 #include <cassert>
12 #include <setjmp.h>
13 #include <sstream> // for print_binary_string
14 #include <set>
15 #include <string>
16
17 #include "LieroX.h"
18 #include "IpToCountryDB.h"
19 #include "AuxLib.h"
20 #include "CClient.h"
21 #include "CServer.h"
22 #include "ConfigHandler.h"
23 #include "console.h"
24 #include "GfxPrimitives.h"
25 #include "FindFile.h"
26 #include "InputEvents.h"
27 #include "StringUtils.h"
28 #include "Entity.h"
29 #include "Error.h"
30 #include "DedicatedControl.h"
31 #include "Physics.h"
32 #include "Version.h"
33 #include "OLXG15.h"
34 #include "CrashHandler.h"
35 #include "Cursor.h"
36 #include "CssParser.h"
37 #include "FontHandling.h"
38 #include "Timer.h"
39 #include "CChannel.h"
40 #include "Cache.h"
41 #include "ProfileSystem.h"
42 #include "IRC.h"
43 #include "Music.h"
44 #include "Debug.h"
45 #include "TaskManager.h"
46 #include "CGameMode.h"
47 #include "ConversationLogger.h"
48 #include "Command.h"
49
50 #include "DeprecatedGUI/CBar.h"
51 #include "DeprecatedGUI/Graphics.h"
52 #include "DeprecatedGUI/Menu.h"
53 #include "DeprecatedGUI/CChatWidget.h"
54
55 #include "breakpad/ExtractInfo.h"
56
57 #ifndef WIN32
58 #include <dirent.h>
59 #include <sys/stat.h>
60 #endif
61
62 #ifdef __ANDROID__
63 #include <SDL/SDL_android.h>
64 #endif
65
66 #include <libxml/parser.h>
67
68 // TODO: i hate globals ...
69 // we have to create a basic class Game or something
70
71 lierox_t *tLX = NULL;
72
73 bool bDisableSound = false;
74 #ifdef DEDICATED_ONLY
75 bool bDedicated = true;
76 bool bJoystickSupport = false;
77 #else //DEDICATED_ONLY
78 bool bDedicated = false;
79 #ifdef DISABLE_JOYSTICK
80 bool bJoystickSupport = false;
81 #else
82 bool bJoystickSupport = true;
83 #endif
84 #endif //DEDICATED_ONLY
85 bool bRestartGameAfterQuit = false;
86 TStartFunction startFunction = NULL;
87 void* startFunctionData = NULL;
88 ConversationLogger *convoLogger = NULL;
89
90
91 keyboard_t *kb = NULL;
92 IpToCountryDB *tIpToCountryDB = NULL;
93
94 static std::list<std::string> startupCommands;
95
96
97 //
98 // Loading screen info and functions
99 //
100 class LoadingScreen {
101 public:
LoadingScreen()102 LoadingScreen() {
103 iBackgroundX = iBackgroundY = 0;
104 bmpBackground = NULL;
105 cBar = NULL;
106 }
107
108 int iBackgroundX;
109 int iBackgroundY;
110 int iLabelX;
111 int iLabelY;
112 Uint32 clLabel;
113 SmartPointer<SDL_Surface> bmpBackground;
114 DeprecatedGUI::CBar *cBar;
115 };
116
117 static LoadingScreen cLoading;
118
119 static void InitializeLoading();
120 static void DrawLoading(byte percentage, const std::string &text);
121 static void ShutdownLoading();
122
123
124
125
126 // TODO: move this out here
print_binary_string(const std::string & txt)127 void print_binary_string(const std::string& txt) {
128 std::string buf;
129 std::stringstream str(buf);
130 for(std::string::const_iterator it = txt.begin(); it != txt.end(); it++) {
131 str << std::hex << (ushort)(uchar)(*it) << " ";
132 }
133 notes << buf << endl;
134 }
135
DoSystemChecks()136 static void DoSystemChecks() {
137 // sadly, these sizeof are directly used in CGameScript.cpp/CMap.cpp
138 // TODO: fix this issue
139 static_assert(sizeof(char) == 1, "sizeof_char__equals1");
140 static_assert(sizeof(short) == 2, "sizeof_short__equals2");
141 static_assert(sizeof(int) == 4, "sizeof_int__equals4");
142 static_assert(sizeof(float) == 4, "sizeof_float__equals4");
143 // sometimes the return value of SendMessage is used as a pointer
144 static_assert(sizeof(DWORD) == sizeof(void*), "sizeof_dword__equals_p");
145 }
146
147
148 char binaryfilename[2048] = {0};
149
GetBinaryFilename()150 const char* GetBinaryFilename() { return binaryfilename; }
151
152 #ifndef WIN32
153 sigjmp_buf longJumpBuffer;
154 #endif
155
156 static ThreadPoolItem* mainLoopThread = NULL;
157 static int MainLoopThread(void*);
158
159
QuitEventThreadEvent()160 static SDL_Event QuitEventThreadEvent() {
161 SDL_Event ev;
162 ev.type = SDL_USEREVENT;
163 ev.user.code = UE_QuitEventThread;
164 return ev;
165 }
166
167
startMainLockDetector()168 static void startMainLockDetector() {
169 struct MainLockDetector : Action {
170 bool wait(Uint32 time) {
171 if(!tLX) return false;
172 AbsTime oldTime = tLX->currentTime;
173 for(Uint32 t = 0; t < time; t += 100) {
174 if(!tLX) return false;
175 if(tLX->bQuitGame) return false;
176 if(oldTime != tLX->currentTime) return true;
177 SDL_Delay(100);
178 }
179 return true;
180 }
181 int handle() {
182 // We should always have tLX!=NULL here as we uninit it after the thread-shutdown now.
183 while(tLX && !tLX->bQuitGame) {
184 AbsTime oldTime = tLX->currentTime;
185 if(!wait(1000)) return 0;
186 if(!tLX) return 0;
187 if(tLX->bQuitGame) return 0;
188 if(IsWaitingForEvent()) continue;
189
190 // HINT: Comment that out and you'll find a lot of things in OLX which could be improved.
191 // WARNING: This is the only code here which could lead to very rare crashes.
192 //if(!cClient || cClient->getStatus() != NET_PLAYING) continue;
193
194 // check if the mainthread is hanging
195 if(oldTime == tLX->currentTime) {
196 warnings << "possible lock of game thread detected" << endl;
197 //OlxWriteCoreDump("mainlock");
198 //RaiseDebugger();
199
200 if(!wait(5*1000)) return 0;
201 if(tLX && !tLX->bQuitGame && oldTime == tLX->currentTime) {
202 hints << "Still locked after 5 seconds. Current threads:" << endl;
203 threadPool->dumpState(stdoutCLI());
204 hints << "Free system memory: " << (GetFreeSysMemory() / 1024) << " KB" << endl;
205 hints << "Cache size: " << (cCache.GetCacheSize() / 1024) << " KB" << endl;
206 }
207 else continue;
208
209 // pause for a while, don't be so hard
210 if(!wait(25*1000)) return 0;
211 if(tLX && !tLX->bQuitGame && oldTime == tLX->currentTime) {
212 warnings << "we still are locked after 30 seconds" << endl;
213 if(tLXOptions && tLXOptions->bFullscreen) {
214 notes << "we are in fullscreen, going to window mode now" << endl;
215 tLXOptions->bFullscreen = false;
216 doSetVideoModeInMainThread();
217 notes << "setting window mode sucessfull" << endl;
218 }
219 }
220 else continue;
221
222 // pause for a while, don't be so hard
223 if(!wait(25*1000)) return 0;
224 if(tLX && !tLX->bQuitGame && oldTime == tLX->currentTime) {
225 errors << "we still are locked after 60 seconds" << endl;
226 if(!AmIBeingDebugged()) {
227 errors << "aborting now" << endl;
228 abort();
229 }
230 }
231 }
232 }
233 return 0;
234 }
235 };
236 threadPool->start(new MainLockDetector(), "main lock detector", true);
237 }
238
239
240 static bool menu_startgame = false;
241
242 struct VideoHandler {
243 SDL_mutex* mutex;
244 SDL_cond* sign;
245 int framesInQueue;
246 bool videoModeReady;
247
VideoHandlerVideoHandler248 VideoHandler() : mutex(NULL), sign(NULL), framesInQueue(0), videoModeReady(true) {
249 mutex = SDL_CreateMutex();
250 sign = SDL_CreateCond();
251 }
252
~VideoHandlerVideoHandler253 ~VideoHandler() {
254 SDL_DestroyMutex(mutex); mutex = NULL;
255 SDL_DestroyCond(sign); sign = NULL;
256 }
257
frameVideoHandler258 void frame() {
259 {
260 ScopedLock lock(mutex);
261 if(!videoModeReady) return;
262 SDL_CondBroadcast(sign);
263 if(framesInQueue > 0) framesInQueue--;
264 else return;
265 VideoPostProcessor::process();
266 }
267
268 flipRealVideo();
269 }
270
setVideoModeVideoHandler271 void setVideoMode() {
272 bool makeFrame = false;
273 {
274 ScopedLock lock(mutex);
275 SetVideoMode();
276 videoModeReady = true;
277 SDL_CondBroadcast(sign);
278
279 if(framesInQueue > 0) {
280 framesInQueue = 0;
281 makeFrame = true;
282 VideoPostProcessor::process();
283 }
284 }
285
286 if(makeFrame)
287 flipRealVideo();
288 }
289
pushFrameVideoHandler290 void pushFrame() {
291 // wait for current drawing
292 ScopedLock lock(mutex);
293
294 while(framesInQueue > 0)
295 SDL_CondWait(sign, mutex);
296
297 SDL_Event ev;
298 ev.type = SDL_USEREVENT;
299 ev.user.code = UE_DoVideoFrame;
300 if(SDL_PushEvent(&ev) == 0) {
301 framesInQueue++;
302 VideoPostProcessor::flipBuffers();
303 } else
304 warnings << "failed to push videoframeevent" << endl;
305
306 }
307
requestSetVideoModeVideoHandler308 void requestSetVideoMode() {
309 ScopedLock lock(mutex);
310 SDL_Event ev;
311 ev.type = SDL_USEREVENT;
312 ev.user.code = UE_DoSetVideoMode;
313 if(SDL_PushEvent(&ev) == 0) {
314 videoModeReady = false;
315 while(!videoModeReady)
316 SDL_CondWait(sign, mutex);
317 }
318 else
319 warnings << "failed to push setvideomode event" << endl;
320 }
321 };
322
323 static VideoHandler videoHandler;
324
325 #ifndef WIN32
326
327 #include <sys/types.h>
328 #include <sys/mman.h>
329 #include <fcntl.h>
330
331 static char* teeOlxOutputFile = NULL;
332 static const size_t MAXFILENAMESIZE = 2048;
333
GetLogFilename()334 const char* GetLogFilename() { if(teeOlxOutputFile) return teeOlxOutputFile; return ""; }
335
336 #include <unistd.h>
337 #include <signal.h>
338
teeOlxOutputToFileHandler(int c)339 static void teeOlxOutputToFileHandler(int c) {
340 static std::string buffer;
341 static std::string currentOlxOutputFilename = "";
342 static FILE* out = NULL;
343
344 if(c == EOF) {
345 if(out) fclose(out);
346 out = NULL;
347 return;
348 }
349
350 char ch = c;
351 if(out)
352 fwrite(&ch, 1, 1, out);
353 else
354 buffer += c;
355
356 if(ch == '\n' && currentOlxOutputFilename != teeOlxOutputFile) {
357 if(out) fclose(out);
358 out = NULL;
359 currentOlxOutputFilename = teeOlxOutputFile;
360 if(currentOlxOutputFilename != "") {
361 out = fopen(currentOlxOutputFilename.c_str(), "a");
362 if(out)
363 // We want to have everything written immediatly, to have the last output when it crashes
364 setvbuf(out, NULL, _IONBF, 0);
365 if(out && !buffer.empty()) {
366 fwrite(buffer.c_str(), buffer.size(), 1, out);
367 buffer = "";
368 }
369 }
370 }
371 }
372
teeOlxOutputHandler(int in,int out)373 static void teeOlxOutputHandler(int in, int out) {
374 unsigned long c = 0;
375 const static bool printlinenum = false; // just for debugging
376 bool newline = true;
377
378 // The main process will quit this fork by closing the pipe.
379 // There will be problems if this fork terminates earlier.
380 #ifndef __ANDROID__
381 signal(SIGINT, SIG_IGN);
382 signal(SIGTERM, SIG_IGN);
383 #endif
384 while( true ) {
385 char ch = 0;
386 if(read(in, &ch, 1) <= 0) break;
387 if(printlinenum && newline) {
388 char tmp[10];
389 sprintf(tmp, "%06lu: ", c);
390 write(out, &tmp, 8);
391 c++;
392 newline = false;
393 }
394 write( out, &ch, 1 );
395 if(ch == '\n')
396 newline = true;
397 teeOlxOutputToFileHandler((unsigned char)ch);
398 }
399
400 teeOlxOutputToFileHandler(EOF);
401 }
402
403 struct TeeStdoutInfo {
404 pid_t proc;
405 int pipeend;
406 int oldstdout;
407 int oldstderr;
408 };
409
410 static TeeStdoutInfo teeStdoutInfo = {0,0,0,0};
411
412 #include <sys/errno.h>
413 #include <cstdio>
414 #include <cstring>
415
teeStdoutInit()416 void teeStdoutInit() {
417 int pipe_to_handler[2];
418
419 #ifdef __APPLE__
420 teeOlxOutputFile = (char*) mmap(0, MAXFILENAMESIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, 0, 0);
421 #else
422 int fd = open("/dev/zero", O_RDWR);
423 if(fd < 0) {
424 errors << "teeStdout: cannot open dummy file" << endl;
425 return;
426 }
427
428 teeOlxOutputFile = (char*) mmap(0, MAXFILENAMESIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
429 #endif
430
431 if(!teeOlxOutputFile || teeOlxOutputFile == (char*)-1) {
432 teeOlxOutputFile = NULL;
433 errors << "teeStdout: cannot mmap: " << strerror(errno) << endl;
434 return;
435 }
436
437 if(pipe(pipe_to_handler) != 0) { // error creating pipe
438 errors << "teeStdout: cannot create pipe: " << strerror(errno) << endl;
439 return;
440 }
441
442 pid_t p = fork();
443 if(p < 0) { // error forking
444 errors << "teeStdout: cannot fork: " << strerror(errno) << endl;
445 return;
446 }
447 else if(p == 0) { // fork
448 close(pipe_to_handler[1]);
449 teeOlxOutputHandler(pipe_to_handler[0], STDOUT_FILENO);
450 _exit(0);
451 }
452 else { // parent
453 close(pipe_to_handler[0]);
454 teeStdoutInfo.proc = p;
455 teeStdoutInfo.pipeend = pipe_to_handler[1];
456 teeStdoutInfo.oldstdout = dup(STDOUT_FILENO);
457 teeStdoutInfo.oldstderr = dup(STDERR_FILENO);
458 dup2(pipe_to_handler[1], STDOUT_FILENO);
459 dup2(pipe_to_handler[1], STDERR_FILENO);
460 setvbuf(stdout, NULL, _IONBF, 0);
461 setvbuf(stderr, NULL, _IONBF, 0);
462 }
463 }
464
teeStdoutFile(const std::string & file)465 void teeStdoutFile(const std::string& file) {
466 if(!teeOlxOutputFile) return;
467
468 if(file.size() >= MAXFILENAMESIZE - 1) {
469 errors << "teeStdoutFile: filename " << file << " too big" << endl;
470 strcpy(teeOlxOutputFile, "");
471 return;
472 }
473
474 strcpy(teeOlxOutputFile, file.c_str());
475 }
476
477 #include <sys/wait.h>
478
479 // NOTE: We are calling this also when we crashed, so be sure that we only do save operations here!
teeStdoutQuit(bool wait=true)480 void teeStdoutQuit(bool wait = true) {
481 if(teeStdoutInfo.proc) {
482 close(STDOUT_FILENO);
483 close(STDERR_FILENO);
484 close(teeStdoutInfo.pipeend);
485 // The forked process should quit itself now.
486 if(wait) waitpid(teeStdoutInfo.proc, NULL, 0);
487
488 // Recover stdout/err
489 dup2(teeStdoutInfo.oldstdout, STDOUT_FILENO);
490 dup2(teeStdoutInfo.oldstderr, STDERR_FILENO);
491 close(teeStdoutInfo.oldstdout);
492 close(teeStdoutInfo.oldstderr);
493 printf("Standard Out/Err recovered\n");
494 }
495
496 if(teeOlxOutputFile) {
497 munmap(teeOlxOutputFile, MAXFILENAMESIZE);
498 teeOlxOutputFile = NULL;
499 }
500 }
501
502 #else
503
504 static char teeLogfile[2048] = "stdout.txt";
505
teeStdoutInit()506 void teeStdoutInit() {}
teeStdoutFile(const std::string & f)507 void teeStdoutFile(const std::string& f) {
508 if(f.size() < sizeof(teeLogfile) - 1)
509 strcpy(teeLogfile, f.c_str());
510 else
511 errors << "teeStdoutFile: filename " << f << " is too long" << endl;
512
513 // If you would just see the old output, it would look kind of strange
514 // that it suddenly stops, so print where we continue.
515 notes << "Logfile: " << f << endl;
516
517 if(freopen(Utf8ToSystemNative(f).c_str(), "a", stdout) == NULL) {
518 freopen("stdout.txt", "a", stdout); // reopen fallback
519 errors << "Could not open logfile" << endl;
520 return;
521 }
522 // no caching for stdout/logfile, it should be written immediatly to
523 // have the important information in case of a crash
524 setvbuf(stdout, NULL, _IONBF, 0);
525
526 // print some messages again because they are missing in the logfile
527 hints << GetFullGameName() << " is starting ..." << endl;
528 #ifdef DEBUG
529 hints << "This is a DEBUG build." << endl;
530 #endif
531 #ifdef DEDICATED_ONLY
532 hints << "This is a DEDICATED_ONLY build." << endl;
533 #endif
534 notes << "Free memory: " << (GetFreeSysMemory() / 1024 / 1024) << " MB" << endl;
535 notes << "Current time: " << GetDateTimeText() << endl;
536
537 // print the searchpaths, this may be very usefull for the user
538 notes << "Searchpaths (in this order):\n";
539 for(searchpathlist::const_iterator p2 = tSearchPaths.begin(); p2 != tSearchPaths.end(); p2++) {
540 std::string path = *p2;
541 ReplaceFileVariables(path);
542 notes << " " << path << "\n";
543 }
544 notes << "Searchpaths finished." << endl;
545
546 // we still miss some more output but let's hope that this is enough
547 }
teeStdoutQuit(bool wait=true)548 void teeStdoutQuit(bool wait = true) {}
GetLogFilename()549 const char* GetLogFilename() { return teeLogfile; }
550
551 #endif
552
saveSetBinFilename(const std::string & f)553 static void saveSetBinFilename(const std::string& f) {
554 if(f.size() < sizeof(binaryfilename) - 1)
555 strcpy(binaryfilename, f.c_str());
556 }
557
setBinaryDirAndName(char * argv0)558 void setBinaryDirAndName(char* argv0) {
559 saveSetBinFilename(SystemNativeToUtf8(argv0)); // set system native binary filename
560 binary_dir = SystemNativeToUtf8(argv0);
561 size_t slashpos = findLastPathSep(binary_dir);
562 if(slashpos != std::string::npos) {
563 binary_dir.erase(slashpos);
564
565 } else {
566 binary_dir = ".";
567
568 // We where called somewhere and located in some PATH.
569 // Search which one.
570 #ifdef WIN32
571 static const char* PATHENTRYSEPERATOR = ";";
572 #else
573 static const char* PATHENTRYSEPERATOR = ":";
574 #endif
575 std::vector<std::string> paths = explode(getenv("PATH"), PATHENTRYSEPERATOR);
576 for(std::vector<std::string>::iterator p = paths.begin(); p != paths.end(); ++p) {
577 if(IsFileAvailable(SystemNativeToUtf8(*p) + "/" + binaryfilename, true)) {
578 binary_dir = SystemNativeToUtf8(*p);
579 saveSetBinFilename(binary_dir + "/" + binaryfilename);
580 return;
581 }
582 }
583
584 // Hm, nothing found. Nothing we can do about it...
585 }
586 }
587
588 // ParseArguments will set this eventually to true
589 static bool afterCrash = false;
590 static bool afterCrashInformedUser = false;
591
592 ///////////////////
593 // Main entry point
main(int argc,char * argv[])594 int main(int argc, char *argv[])
595 {
596 #ifndef __ANDROID__
597 if(DoCrashReport(argc, argv)) return 0;
598 #endif
599
600 teeStdoutInit();
601
602 setCurThreadName("Main Thread");
603 setCurThreadPriority(0.5f);
604
605 hints << GetFullGameName() << " is starting ..." << endl;
606 #ifdef DEBUG
607 hints << "This is a DEBUG build." << endl;
608 #endif
609 #ifdef DEDICATED_ONLY
610 hints << "This is a DEDICATED_ONLY build." << endl;
611 #endif
612 notes << "Free memory: " << (GetFreeSysMemory() / 1024 / 1024) << " MB" << endl;
613 notes << "Current time: " << GetDateTimeText() << endl;
614 {
615 char cwd[PATH_MAX] = "";
616 getcwd(cwd, sizeof(cwd));
617 notes << "Current directory: " << cwd << endl;
618 }
619
620 // Initialize the LieroX structure
621 tLX = new lierox_t;
622
623 DoSystemChecks();
624 if(!InitNetworkSystem())
625 errors << "Failed to initialize the network library" << endl;
626 InitThreadPool();
627
628 setBinaryDirAndName(argv[0]);
629
630 // this has to be done before GameOptions::Init
631 InitGameModes();
632
633 startpoint:
634
635 InitTaskManager();
636
637 // Load options and other settings
638 if(!GameOptions::Init()) {
639 SystemError("Could not load options");
640 return -1;
641 }
642
643 teeStdoutFile(GetWriteFullFileName("logs/OpenLieroX - " + GetDateTimeFilename() + ".txt", true));
644 #ifndef __ANDROID__
645 CrashHandler::init();
646 #endif
647
648 if(!NetworkTexts::Init()) {
649 SystemError("Could not load network strings.");
650 return -1;
651 }
652
653 CSSParser::test_css();
654
655 // Parse the arguments
656 // do it after the loading of the options as this can
657 // overwrite the default options
658 ParseArguments(argc, argv);
659
660 // Start the G15 support, it's suitable that the display is showing while loading.
661 #ifdef WITH_G15
662 OLXG15 = new OLXG15_t;
663 if (!OLXG15->init()) {
664 // Error when initialising, can't use it.
665 delete OLXG15;
666 OLXG15 = NULL;
667 }
668 #endif //WITH_G15
669
670 // Start loading the IP to country database
671 // HINT: we do it as soon as possible because the loading has more time then which means better results
672 // HINT: the database won't load if it is disabled in options
673 tIpToCountryDB = new IpToCountryDB(IP_TO_COUNTRY_FILE);
674
675 // Initialize LX
676 if(!InitializeLieroX()) {
677 SystemError("Could not initialize LieroX.");
678 return -1;
679 }
680
681 kb = GetKeyboard();
682 if (!bDedicated && !VideoPostProcessor::videoSurface()) {
683 SystemError("Could not find screen.");
684 return -1;
685 }
686
687 // Music
688 if( !bDedicated && tLXOptions->iMusicVolume > 0 )
689 InitializeBackgroundMusic();
690
691 tLX->currentTime = GetTime();
692
693 DrawLoading(60, "Initializing menu system");
694
695 // Initialize menu
696 if(!DeprecatedGUI::Menu_Initialize(&menu_startgame)) {
697 SystemError("Error: Could not initialize the menu system.\nError when loading graphics files");
698 return -1;
699 }
700
701 // speedup for Menu_Start()
702 DrawLoading(85, "Loading main menu");
703 DeprecatedGUI::iSkipStart = true;
704 DeprecatedGUI::tMenu->iMenuType = DeprecatedGUI::MNU_MAIN;
705 DeprecatedGUI::Menu_MainInitialize();
706
707 // Initialize chat logging
708 convoLogger = new ConversationLogger();
709 if (tLXOptions->bLogConvos)
710 convoLogger->startLogging();
711
712 // Setup the global keys
713 tLX->setupInputs();
714
715 DrawLoading(99, "Loading Physics Engine");
716 PhysicsEngine::Init();
717
718 DrawLoading(100, "Done! Starting menu");
719
720 // Everything loaded, this is not needed anymore
721 ShutdownLoading();
722
723 if(startFunction != NULL) {
724 if(!startFunction(startFunctionData)) {
725 SystemError("ERROR: startfunction not successfull");
726 return -1;
727 }
728 // reset the data; the startfunction is intended to be called only once
729 startFunction = NULL;
730 startFunctionData = NULL;
731 }
732
733 for(std::list<std::string>::iterator i = startupCommands.begin(); i != startupCommands.end(); ++i) {
734 // execute as if it would have been entered in ingame console
735 notes << "startup command: " << *i << endl;
736 Con_Execute(*i);
737 }
738 startupCommands.clear(); // don't execute them again
739
740 mainLoopThread = threadPool->start(MainLoopThread, NULL, "mainloop");
741
742 startMainLockDetector();
743
744 if(!bDedicated) {
745 // Get all SDL events and push them to our event queue.
746 // We have to do that in the same thread where we inited the video because of SDL.
747 SDL_Event ev;
748 memset( &ev, 0, sizeof(ev) );
749 while(true) {
750 while( SDL_WaitEvent(&ev) ) {
751 if(ev.type == SDL_USEREVENT) {
752 switch(ev.user.code) {
753 case UE_QuitEventThread:
754 goto quit;
755 case UE_DoVideoFrame:
756 videoHandler.frame();
757 continue;
758 case UE_DoSetVideoMode:
759 videoHandler.setVideoMode();
760 continue;
761 case UE_DoActionInMainThread:
762 ((Action*)ev.user.data1)->handle();
763 delete (Action*)ev.user.data1;
764 continue;
765 }
766 }
767 if( ev.type == SDL_SYSWMEVENT ) {
768 EvHndl_SysWmEvent_MainThread( &ev );
769 continue;
770 }
771 mainQueue->push(ev);
772 }
773
774 notes << "error while waiting for next event" << endl;
775 SDL_Delay(200);
776 }
777 }
778
779 quit:
780 threadPool->wait(mainLoopThread, NULL);
781 mainLoopThread = NULL;
782
783 if(!bRestartGameAfterQuit)
784 CrashHandler::restartAfterCrash = false;
785
786 PhysicsEngine::UnInit();
787
788 ShutdownLieroX();
789
790 notes << "waiting for all left threads and tasks" << endl;
791 #ifdef __ANDROID__
792 if(!bRestartGameAfterQuit) {
793 exit(0); // Force quit without waiting to hide the app window
794 }
795 #endif
796
797 taskManager->finishQueuedTasks();
798 threadPool->waitAll(); // do that before uniniting task manager because some threads could access it
799
800 // do that after shutting down the timers and other threads
801 ShutdownEventQueue();
802
803 UnInitTaskManager();
804
805 if(bRestartGameAfterQuit) {
806 bRestartGameAfterQuit = false;
807 hints << "-- Restarting game --" << endl;
808 #ifdef __ANDROID__
809 SDL_ANDROID_RestartMyself("");
810 #endif
811
812 goto startpoint;
813 }
814
815 UnInitThreadPool();
816
817 // Network
818 QuitNetworkSystem();
819
820 // LieroX structure
821 // HINT: must be after end of all threads because we could access it
822 if(tLX) {
823 delete tLX;
824 tLX = NULL;
825 }
826
827 notes << "Good Bye and enjoy your day..." << endl;
828
829 // Uninit the crash handler after all other code
830 #ifndef __ANDROID__
831 CrashHandler::uninit();
832 #endif
833
834 teeStdoutQuit();
835 return 0;
836 }
837
838 // note: important to have const char* here because std::string is too dynamic, could be screwed up when returning
SetCrashHandlerReturnPoint(const char * name)839 void SetCrashHandlerReturnPoint(const char* name) {
840 #if !defined(WIN32) && !defined(__ANDROID__)
841 if(sigsetjmp(longJumpBuffer, true) != 0) {
842 hints << "returned from sigsetjmp in " << name << endl;
843 if(!tLXOptions) {
844 notes << "we already have tLXOptions uninitialised, exiting now" << endl;
845 exit(10);
846 return;
847 }
848 if(tLXOptions->bFullscreen) {
849 notes << "we are in fullscreen, going to window mode now" << endl;
850 tLXOptions->bFullscreen = false;
851 doSetVideoModeInMainThread();
852 notes << "back in window mode" << endl;
853 }
854 }
855 #endif
856 }
857
doVideoFrameInMainThread()858 void doVideoFrameInMainThread() {
859 if(bDedicated) return;
860 videoHandler.pushFrame();
861 }
862
doSetVideoModeInMainThread()863 void doSetVideoModeInMainThread() {
864 if(bDedicated) return;
865 videoHandler.requestSetVideoMode();
866 }
867
doVppOperation(Action * act)868 void doVppOperation(Action* act) {
869 {
870 ScopedLock lock(videoHandler.mutex);
871 act->handle();
872 }
873 delete act;
874 }
875
doActionInMainThread(Action * act)876 void doActionInMainThread(Action* act) {
877 if(bDedicated) {
878 warnings << "doActionInMainThread cannot work correctly in dedicated mode" << endl;
879 // we will just put it to the main queue instead
880 mainQueue->push(act);
881 return;
882 }
883
884 SDL_Event ev;
885 ev.type = SDL_USEREVENT;
886 ev.user.code = UE_DoActionInMainThread;
887 ev.user.data1 = act;
888 if(SDL_PushEvent(&ev) != 0) {
889 errors << "failed to push custom action event" << endl;
890 }
891 }
892
893
894 static std::string quitEngineFlagReason;
895 static bool inMainGameLoop = false;
896
MainLoopThread(void *)897 static int MainLoopThread(void*) {
898 setCurThreadPriority(0.5f);
899 tLX->bQuitGame = false;
900
901 if(afterCrash && !afterCrashInformedUser) {
902 afterCrashInformedUser = true;
903 DeprecatedGUI::Menu_MessageBox("Sorry",
904 "The game has crashed. This should not have happend. "
905 "But it did.\nWe hope we can fix this problem in a future version. "
906 "Or perhaps there is already a new version; check out our "
907 "homepage for more information:\nhttp://openlierox.net\n\n"
908 "If you have an idea why this have happend, please write "
909 "us a mail or post in our forum. This may help us a lot "
910 "for fixing the problem.\n\nThanks!", DeprecatedGUI::LMB_OK);
911 }
912
913 while(!tLX->bQuitGame) {
914 SetCrashHandlerReturnPoint("MainLoopThread before lobby");
915
916 cClient->SetSocketWithEvents(true);
917 cServer->SetSocketWithEvents(true);
918 menu_startgame = false; // the menu has a reference to this variable
919 ResetQuitEngineFlag();
920 DeprecatedGUI::Menu_Start(); // Start and run the menu, won't return 'till user joins game / exits
921 cClient->SetSocketWithEvents(false);
922 cServer->SetSocketWithEvents(false);
923
924 if(!menu_startgame) {
925 // Quit
926 tLX->bQuitGame = true;
927 break;
928 }
929
930 if(tLX->bQuitEngine)
931 // If we already set the quitengine flag, we want to go back to the menu.
932 // Sometimes, when we get both a PrepareGame and a GotoLobby packet in
933 // a single menu-frame, we quit the menu and set the quitengine flag
934 continue;
935
936 // Pre-game initialization
937 if(!bDedicated) FillSurface(VideoPostProcessor::videoSurface(), tLX->clBlack);
938
939 ClearEntities();
940
941 ProcessEvents();
942 notes << "MaxFPS is " << tLXOptions->nMaxFPS << endl;
943
944 //cCache.ClearExtraEntries(); // Do not clear anything before game started, it may be slow
945
946 notes << "GameLoopStart" << endl;
947 inMainGameLoop = true;
948 if( DedicatedControl::Get() )
949 DedicatedControl::Get()->GameLoopStart_Signal();
950
951 CrashHandler::recoverAfterCrash = tLXOptions->bRecoverAfterCrash && GetGameVersion().releasetype == Version::RT_NORMAL;
952
953 //
954 // Main game loop
955 //
956 ResetQuitEngineFlag();
957 AbsTime oldtime = GetTime();
958 while(!tLX->bQuitEngine) {
959
960 tLX->currentTime = GetTime();
961 SetCrashHandlerReturnPoint("main game loop");
962
963 // Timing
964 tLX->fDeltaTime = tLX->currentTime - oldtime;
965 tLX->fRealDeltaTime = tLX->fDeltaTime;
966 oldtime = tLX->currentTime;
967
968 // cap the delta
969 if(tLX->fDeltaTime.seconds() > 0.5f) {
970 warnings << "deltatime " << tLX->fDeltaTime.seconds() << " is too high" << endl;
971 tLX->fDeltaTime = 0.5f; // don't simulate more than 500ms, it could crash the game
972 }
973
974 ProcessEvents();
975
976 // Main frame
977 GameLoopFrame();
978
979 doVideoFrameInMainThread();
980 CapFPS();
981 }
982
983 CrashHandler::recoverAfterCrash = false;
984
985 PhysicsEngine::Get()->uninitGame();
986
987 notes << "GameLoopEnd: " << quitEngineFlagReason << endl;
988 inMainGameLoop = false;
989 if( DedicatedControl::Get() )
990 DedicatedControl::Get()->GameLoopEnd_Signal();
991
992 cCache.ClearExtraEntries(); // Game ended - clear cache
993
994 }
995
996 SDL_Event quitEv = QuitEventThreadEvent();
997 if(!bDedicated)
998 while(SDL_PushEvent(&quitEv) < 0) {}
999
1000 return 0;
1001 }
1002
1003
1004
1005 ///////////////////
1006 // Parse the arguments
ParseArguments(int argc,char * argv[])1007 void ParseArguments(int argc, char *argv[])
1008 {
1009 // Parameters passed to OpenLieroX overwrite the loaded options
1010 char *a;
1011 for(int i=1; i<argc; i++) {
1012 a = argv[i];
1013
1014 // -opengl
1015 // Turns OpenGL on
1016 if( stricmp(a, "-opengl") == 0 ) {
1017 tLXOptions->bOpenGL = true;
1018 } else
1019
1020 // -nojoystick
1021 // Turns joystick off
1022 if( stricmp(a, "-nojoystick") == 0 ) {
1023 bJoystickSupport = false;
1024 } else
1025
1026
1027 // -noopengl
1028 // Turns OpenGL off
1029 if( stricmp(a, "-noopengl") == 0 ) {
1030 tLXOptions->bOpenGL = false;
1031 } else
1032
1033 // -nosound
1034 // Turns off the sound
1035 if( stricmp(a, "-nosound") == 0 ) {
1036 bDisableSound = true;
1037 tLXOptions->bSoundOn = false;
1038 } else
1039
1040 // -dedicated
1041 // Turns on dedicated mode (no gfx, no sound)
1042 if( stricmp(a, "-dedicated") == 0 ) {
1043 bDedicated = true;
1044 bDisableSound = true;
1045 // this setting will be temporarly because we don't save options at end in dedicated mode
1046 tLXOptions->bSoundOn = false;
1047 } else
1048
1049 // -dedscript
1050 // set dedicated script (next param)
1051 if( stricmp(a, "-script") == 0 ) {
1052 if(argv[i + 1] != NULL) {
1053 bDedicated = true;
1054 bDisableSound = true;
1055 // these settings will be temporarly because we don't save options at end in dedicated mode
1056 tLXOptions->bSoundOn = false;
1057 tLXOptions->sDedicatedScript = argv[++i];
1058 }
1059 else
1060 warnings << "-script needs an additinal parameter" << endl;
1061 } else
1062
1063 // -connect
1064 // connect to server (next param)
1065 if( stricmp(a, "-connect") == 0 ) {
1066 if(argv[i + 1] != NULL) {
1067 startupCommands.push_back("connect \"" + std::string(argv[++i]) + "\"");
1068 }
1069 else
1070 warnings << "-connect needs an additinal parameter" << endl;
1071 } else
1072
1073 // -exec
1074 // pushes a startup command
1075 if( stricmp(a, "-exec") == 0 ) {
1076 if(argv[i + 1] != NULL)
1077 startupCommands.push_back(argv[++i]);
1078 else
1079 warnings << "-exec needs an additinal parameter" << endl;
1080 } else
1081
1082 // -window
1083 // Turns fullscreen off
1084 if( stricmp(a, "-window") == 0 ) {
1085 tLXOptions->bFullscreen = false;
1086 } else
1087
1088 // -fullscreen
1089 // Turns fullscreen on
1090 if( stricmp(a, "-fullscreen") == 0 ) {
1091 tLXOptions->bFullscreen = true;
1092 } else
1093
1094 if( stricmp(a, "-aftercrash") == 0) {
1095 afterCrash = true;
1096 }
1097
1098 #ifdef WIN32
1099 // -console
1100 // Attaches a console window to the main LX window
1101 if (stricmp(a, "-console") == 0) {
1102 if (AllocConsole()) {
1103 FILE *con = freopen("CONOUT$", "w", stdout);
1104 if (con) {
1105 *stdout = *con;
1106 setvbuf(stdout, NULL, _IONBF, 0);
1107 }
1108 SetConsoleTitle("OpenLieroX Console");
1109 }
1110 } else
1111 #endif
1112
1113 // -help
1114 // Displays help and quits
1115 if( !stricmp(a, "-h") || !stricmp(a, "-help") || !stricmp(a, "--help") || !stricmp(a, "/?")) {
1116 printf("available parameters:\n");
1117 printf(" -connect srv Connects to the server\n");
1118 printf(" -exec cmd Executes the command in console\n");
1119 printf(" -opengl OpenLieroX will use OpenGL for drawing\n");
1120 printf(" -noopengl Explicitly disable using OpenGL\n");
1121 printf(" -dedicated Dedicated mode\n");
1122 printf(" -nojoystick Disable Joystick support\n");
1123 printf(" -nosound Disable sound\n");
1124 printf(" -window Run in window mode\n");
1125 printf(" -fullscreen Run in fullscreen mode\n");
1126 #ifdef WIN32
1127 printf(" -console Attach a console window to the main OpenLieroX window\n");
1128 #endif
1129 #ifdef DEBUG
1130 printf(" -nettest Test CChannel reliability\n");
1131 #endif
1132 printf(" -skin Turns on new skinned GUI - it's unfinished yet\n");
1133 printf(" -noskin Turns off new skinned GUI\n");
1134
1135 // Shutdown and quit
1136 // ShutdownLieroX() works only correct when everything was inited because ProcessEvents() is used.
1137 // Therefore we just ignore a good clean up and just quit here.
1138 // This is not nice but still nicer than getting a segfault.
1139 // It is not worth to fix this in a nicer way as it is fixed anyway when we use the new engine system.
1140 exit(0);
1141 }
1142 #ifdef DEBUG
1143 if( !stricmp(a, "-nettest") )
1144 {
1145 InitializeLieroX();
1146 TestCChannelRobustness();
1147 ShutdownLieroX();
1148 exit(0);
1149 }
1150 #endif
1151 }
1152 if (getenv("SDL_RESTART_PARAMS") != NULL) {
1153 startupCommands.push_back("connect \"" + std::string(getenv("SDL_RESTART_PARAMS")) + "\"");
1154 }
1155 }
1156
1157
1158 ///////////////////
1159 // Initialize the game
InitializeLieroX()1160 int InitializeLieroX()
1161 {
1162 notes << "Hello there, I am initializing me now..." << endl;
1163
1164 LIBXML_TEST_VERSION;
1165
1166 // Initialize the aux library
1167 if(!InitializeAuxLib("config.cfg",16,0)) {
1168 SystemError("strange problems with the aux library");
1169 return false;
1170 }
1171
1172 // Setup the HTTP proxy
1173 AutoSetupHTTPProxy();
1174
1175 tLX->bVideoModeChanged = false;
1176 tLX->bQuitGame = false;
1177 tLX->bQuitCtrlC = false;
1178 tLX->debug_string = "";
1179 tLX->currentTime = 0;
1180 tLX->fDeltaTime = 0;
1181 tLX->bHosted = false;
1182
1183 // Initialize the game colors (must be called after SDL_GetVideoSurface is not NULL and tLX is not NULL)
1184 DeprecatedGUI::InitializeColors();
1185
1186 // Load the fonts (must be after colors, because colors are used inside CFont::Load)
1187 if (!DeprecatedGUI::LoadFonts()) {
1188 SystemError("Could not load the fonts");
1189 return false;
1190 }
1191
1192 // Initialize the loading screen
1193 InitializeLoading();
1194
1195 //DrawLoading(0, "Initializing network");
1196
1197 DrawLoading(5, "Initializing client and server");
1198
1199 // Allocate the client & server
1200 cClient = new CClient;
1201 if(cClient == NULL) {
1202 SystemError("Error: InitializeLieroX() Out of memory");
1203 return false;
1204 }
1205 cClient->Clear();
1206 cClient->setLocalClient(true);
1207
1208
1209
1210 cServer = new GameServer;
1211 if(cServer == NULL) {
1212 SystemError("Error: InitializeLieroX() Out of memory on creating GameServer");
1213 return false;
1214 }
1215
1216 DrawLoading(10, "Initializing game entities");
1217
1218 // Initialize the entities
1219 if(!InitializeEntities()) {
1220 SystemError("Error: InitializeEntities() Out of memory on initializing the entities");
1221 return false;
1222 }
1223
1224 DrawLoading(15, "Loading graphics");
1225
1226
1227 // Load the graphics
1228 if(!DeprecatedGUI::LoadGraphics()) {
1229 SystemError("Error: Error loading graphics");
1230 return false;
1231 }
1232
1233 DrawLoading(40, "Initializing console");
1234
1235 // Initialize the console GUI
1236 if(!Con_Initialize()) {
1237 SystemError("Error: Could not initialize the console");
1238 return false;
1239 }
1240
1241 DrawLoading(45, "Loading sounds");
1242
1243 // Load the sounds
1244 LoadSounds();
1245
1246 DrawLoading(55, "Loading player profiles");
1247
1248 // Load the profiles
1249 LoadProfiles();
1250
1251 if(bDedicated)
1252 if(!DedicatedControl::Init()) {
1253 errors << "couldn't init dedicated control" << endl;
1254 return false;
1255 }
1256
1257 updateFileListCaches();
1258
1259 notes << "Initializing ready" << endl;
1260
1261 return true;
1262 }
1263
1264
1265 ///////////////////
1266 // Game loop
GameLoopFrame()1267 void GameLoopFrame()
1268 {
1269 HandlePendingCommands();
1270
1271 if(bDedicated)
1272 DedicatedControl::Get()->GameLoop_Frame();
1273
1274 if(tLX->bQuitEngine)
1275 return;
1276
1277 // Check if user pressed screenshot key
1278 if (tLX->cTakeScreenshot.isDownOnce()) {
1279 PushScreenshot("scrshots", "");
1280 }
1281
1282 // Switch between window and fullscreen mode
1283 // Switch only if delta time is low enough. This is because when the game does not
1284 // respond for >30secs and the user presses cSwitchMode in the meantime, the mainlock-detector
1285 // would switch to window and here we would switch again to fullscreen which is stupid.
1286 if( tLX->cSwitchMode.isUp() && tLX && tLX->fRealDeltaTime < 1.0f ) {
1287 // Set to fullscreen
1288 tLXOptions->bFullscreen = !tLXOptions->bFullscreen;
1289
1290 // Set the new video mode
1291 doSetVideoModeInMainThread();
1292
1293 tLX->cSwitchMode.reset();
1294 }
1295
1296 #ifdef WITH_G15
1297 if (OLXG15)
1298 OLXG15->gameFrame();
1299 #endif //WITH_G15
1300
1301 if(tLXOptions->bEnableChat)
1302 ProcessIRC();
1303
1304 // Local
1305 switch (tLX->iGameType) {
1306 case GME_LOCAL:
1307 cClient->Frame();
1308 cServer->Frame();
1309
1310 // If we are connected, just start the game straight away (bypass lobby in local)
1311 if(cClient->getStatus() == NET_CONNECTED) {
1312 if(cServer->getState() == SVS_LOBBY) {
1313 std::string errMsg;
1314 if(!cServer->StartGame(&errMsg)) {
1315 errors << "starting game in local game failed for reason: " << errMsg << endl;
1316 DeprecatedGUI::Menu_MessageBox("Error", "Error while starting game: " + errMsg);
1317 GotoLocalMenu();
1318 return;
1319 }
1320 }
1321 }
1322
1323 if(tLX && !tLX->bQuitEngine)
1324 cClient->Draw(VideoPostProcessor::videoSurface());
1325 break;
1326
1327
1328 // Hosting
1329 case GME_HOST:
1330 cClient->Frame();
1331 cServer->Frame();
1332
1333 if(tLX && !tLX->bQuitEngine)
1334 cClient->Draw(VideoPostProcessor::videoSurface());
1335 break;
1336
1337 // Joined
1338 case GME_JOIN:
1339 cClient->Frame();
1340 if(tLX && !tLX->bQuitEngine)
1341 cClient->Draw(VideoPostProcessor::videoSurface());
1342 break;
1343
1344 } // SWITCH
1345
1346 cClient->resetDebugStr();
1347
1348 EnableSystemMouseCursor(false);
1349 }
1350
1351
1352 ///////////////////
1353 // Quit back to the menu
QuittoMenu()1354 void QuittoMenu()
1355 {
1356 SetQuitEngineFlag("QuittoMenu");
1357 DeprecatedGUI::Menu_SetSkipStart(false);
1358 cClient->Disconnect();
1359 }
1360
1361 //////////////////
1362 // Go to local menu
GotoLocalMenu()1363 void GotoLocalMenu()
1364 {
1365 if(tLX->iGameType == GME_HOST) {
1366 warnings << "called GotoLocalMenu as host, ignoring..." << endl;
1367 return;
1368 }
1369
1370 if(tLX->iGameType == GME_JOIN) {
1371 warnings << "called GotoLocalMenu as client, ignoring..." << endl;
1372 return;
1373 }
1374
1375 SetQuitEngineFlag("GotoLocalMenu");
1376 cClient->Disconnect();
1377 cServer->Shutdown();
1378 cClient->Shutdown();
1379 if(!bDedicated) {
1380 DeprecatedGUI::Menu_SetSkipStart(true);
1381 DeprecatedGUI::Menu_LocalInitialize();
1382 }
1383 }
1384
1385 //////////////////
1386 // Go to local menu
GotoNetMenu()1387 void GotoNetMenu()
1388 {
1389 notes << "GotoNetMenu" << endl;
1390 SetQuitEngineFlag("GotoNetMenu");
1391 cClient->Disconnect();
1392 if(!bDedicated) {
1393 DeprecatedGUI::Menu_SetSkipStart(true);
1394 DeprecatedGUI::Menu_NetInitialize();
1395 }
1396 }
1397
1398 ////////////////////
1399 // Initialize the loading screen
InitializeLoading()1400 static void InitializeLoading() {
1401 if(bDedicated) return; // ignore this case
1402
1403 FillSurface(VideoPostProcessor::videoSurface(), Color(0,0,0));
1404
1405 int bar_x, bar_y, bar_label_x, bar_label_y,bar_dir;
1406 bool bar_visible;
1407 std::string bar_direction;
1408
1409 // Load the details
1410 ReadInteger("data/frontend/frontend.cfg", "Loading", "LoadingBarX", &bar_x, 210);
1411 ReadInteger("data/frontend/frontend.cfg", "Loading", "LoadingBarY", &bar_y, 230);
1412 ReadString ("data/frontend/frontend.cfg", "Loading", "LoadingBarDirection", bar_direction, "lefttoright");
1413 ReadInteger("data/frontend/frontend.cfg", "Loading", "PercentageLabelX", &bar_label_x, 0);
1414 ReadInteger("data/frontend/frontend.cfg", "Loading", "PercentageLabelY", &bar_label_y, 0);
1415 ReadKeyword("data/frontend/frontend.cfg", "Loading", "PercentageLabelVisible", &bar_visible, false);
1416 ReadInteger("data/frontend/frontend.cfg", "Loading", "BackgroundX", &cLoading.iBackgroundX, 190);
1417 ReadInteger("data/frontend/frontend.cfg", "Loading", "BackgroundY", &cLoading.iBackgroundY, 170);
1418 ReadInteger("data/frontend/frontend.cfg", "Loading", "LabelX", &cLoading.iLabelX, 235);
1419 ReadInteger("data/frontend/frontend.cfg", "Loading", "LabelY", &cLoading.iLabelY, 190);
1420
1421 // Convert the loading direction
1422 if (!stringcasecmp(bar_direction,"lefttoright"))
1423 bar_dir = DeprecatedGUI::BAR_LEFTTORIGHT;
1424 else if (!stringcasecmp(bar_direction,"righttoleft"))
1425 bar_dir = DeprecatedGUI::BAR_RIGHTTOLEFT;
1426 else if (!stringcasecmp(bar_direction,"toptobottom"))
1427 bar_dir = DeprecatedGUI::BAR_TOPTOBOTTOM;
1428 else if (!stringcasecmp(bar_direction,"bottomtotop"))
1429 bar_dir = DeprecatedGUI::BAR_BOTTOMTOTOP;
1430 else
1431 bar_dir = DeprecatedGUI::BAR_LEFTTORIGHT;
1432
1433
1434 // Allocate bar
1435 cLoading.cBar = new DeprecatedGUI::CBar(LoadGameImage("./data/frontend/loading_bar.png",true), bar_x, bar_y, bar_label_x, bar_label_y, bar_dir);
1436 if (cLoading.cBar)
1437 cLoading.cBar->SetLabelVisible(bar_visible);
1438
1439 // Load the background
1440 cLoading.bmpBackground = LoadGameImage("./data/frontend/background_loading.png", true);
1441 }
1442
1443 /////////////////////
1444 // Draw the loading
DrawLoading(byte percentage,const std::string & text)1445 static void DrawLoading(byte percentage, const std::string &text) {
1446 if(bDedicated) {
1447 notes << "Loading: " << text << endl;
1448 return;
1449 }
1450
1451 if (cLoading.bmpBackground.get() == NULL)
1452 return;
1453
1454 // Update the repainted area
1455 int x = MIN(cLoading.iBackgroundX, cLoading.cBar->GetX());
1456 int y = MIN(cLoading.cBar->GetY(), cLoading.iBackgroundY);
1457 int w = MAX(cLoading.bmpBackground.get()->w, cLoading.cBar->GetWidth());
1458 int h = MAX(cLoading.bmpBackground.get()->h, cLoading.cBar->GetHeight());
1459 DrawRectFill(VideoPostProcessor::videoSurface(), x, y, x+w, y+h, Color(0,0,0));
1460
1461 if (cLoading.bmpBackground.get() != NULL)
1462 DrawImage(VideoPostProcessor::videoSurface(), cLoading.bmpBackground, cLoading.iBackgroundX, cLoading.iBackgroundY);
1463
1464 if (cLoading.cBar) {
1465 cLoading.cBar->SetPosition(percentage);
1466 cLoading.cBar->Draw( VideoPostProcessor::videoSurface() );
1467 }
1468
1469 tLX->cFont.Draw(VideoPostProcessor::videoSurface(), cLoading.iLabelX, cLoading.iLabelY, tLX->clLoadingLabel, text);
1470
1471 // we are in the main thread, so we can call this directly
1472 VideoPostProcessor::flipBuffers();
1473 VideoPostProcessor::process();
1474 flipRealVideo();
1475 }
1476
1477 ////////////////////
1478 // Shutdown the loading screen
ShutdownLoading()1479 static void ShutdownLoading() {
1480 if (cLoading.cBar)
1481 delete cLoading.cBar;
1482 cLoading.cBar = NULL;
1483 }
1484
1485 ///////////////////
1486 // Shutdown the game
ShutdownLieroX()1487 void ShutdownLieroX()
1488 {
1489 notes << "Shutting me down..." << endl;
1490
1491 // Options
1492 // Save already here in case some other method crashes
1493 if(!bDedicated) // only save if not in dedicated mode
1494 tLXOptions->SaveToDisc();
1495
1496 DeprecatedGUI::CChatWidget::GlobalDestroy();
1497
1498 ShutdownIRC(); // Disconnect from IRC
1499
1500 if(bDedicated)
1501 DedicatedControl::Uninit();
1502
1503 if( ! bDedicated )
1504 ShutdownBackgroundMusic();
1505
1506 Con_Shutdown();
1507
1508 ShutdownLoading(); // In case we're called when an error occured
1509
1510 DeprecatedGUI::ShutdownGraphics();
1511
1512 ShutdownFontCache();
1513
1514 DeprecatedGUI::Menu_Shutdown();
1515 ShutdownProfiles();
1516
1517 // Free the IP to Country DB
1518 if (tIpToCountryDB) {
1519 delete tIpToCountryDB;
1520 tIpToCountryDB = NULL;
1521 }
1522
1523 // Free the client & server
1524 if(cClient) {
1525 delete cClient;
1526 cClient = NULL;
1527 }
1528
1529 if(cServer) {
1530 cServer->Shutdown();
1531 delete cServer;
1532 cServer = NULL;
1533 }
1534
1535 // End logging
1536 if (convoLogger) {
1537 if (tLXOptions->bLogConvos)
1538 convoLogger->endLogging();
1539 delete convoLogger;
1540 convoLogger = NULL;
1541 }
1542
1543 #ifdef WITH_G15
1544 if (OLXG15)
1545 {
1546 delete OLXG15;
1547 OLXG15 = NULL;
1548 }
1549 #endif //WITH_G15
1550 // Entitites
1551 ShutdownEntities();
1552
1553 // Event system
1554 ShutdownEventSystem();
1555
1556 // SDL, Cache and other small stuff
1557 ShutdownAuxLib();
1558
1559 // HINT: must be after shutting down the event system to free the timer correctly
1560 #ifdef DEBUG
1561 ShutdownCacheDebug();
1562 #endif
1563
1564 // Save and clear options
1565
1566 // HINT: save the options again because some could get changed in CServer/CClient destructors and shutdown functions
1567 // TODO: like what changes? why are there options saved both in CServer/CClient structure and in options?
1568 if(!bDedicated) // only save if not in dedicated mode
1569 tLXOptions->SaveToDisc();
1570
1571 ShutdownOptions();
1572
1573 // Only do the deregistration for variables if we are not restarting.
1574 // The problem is that we have registered most vars globally (not by any init-function)
1575 // so we would not reinit them.
1576 // Multiple registration of a var is also not a problem because we are
1577 // using a map for all vars and with a new registration, we would just overwrite
1578 // the old registration.
1579 if(!bRestartGameAfterQuit)
1580 {
1581 CScriptableVars::DeInit();
1582 }
1583
1584 // Shutdown the timers
1585 ShutdownTimers();
1586
1587 xmlCleanupParser();
1588
1589 notes << "Everything was shut down" << endl;
1590 }
1591
1592
ResetQuitEngineFlag()1593 void ResetQuitEngineFlag() {
1594 tLX->bQuitEngine = false;
1595 }
1596
SetQuitEngineFlag(const std::string & reason)1597 void SetQuitEngineFlag(const std::string& reason) {
1598 Warning_QuitEngineFlagSet("SetQuitEngineFlag(" + reason + "): ");
1599 quitEngineFlagReason = reason;
1600 tLX->bQuitEngine = true;
1601 // If we call this from within the menu, the menu should shutdown.
1602 // It will be restarted then in the next frame.
1603 // If we are not in the menu (i.e. in maingameloop), this has no
1604 // effect as we set it to true in Menu_Start().
1605 if(DeprecatedGUI::tMenu)
1606 DeprecatedGUI::tMenu->bMenuRunning = false;
1607 // If we were in menu, because we forced the menu restart above,
1608 // we must set this, otherwise OLX would quit (because of current maingamelogic).
1609 if(DeprecatedGUI::bGame)
1610 *DeprecatedGUI::bGame = true;
1611 }
1612
Warning_QuitEngineFlagSet(const std::string & preText)1613 bool Warning_QuitEngineFlagSet(const std::string& preText) {
1614 if(tLX->bQuitEngine) {
1615 hints << preText << endl;
1616 warnings << "bQuitEngine is set because: " << quitEngineFlagReason << endl;
1617 return true;
1618 }
1619 return false;
1620 }
1621
1622
1623 struct CheckFileForMap {
1624 typedef FileListCacheIntf::FileList List;
operator ()CheckFileForMap1625 void operator()(List& filelist, const std::string& abs_filename) {
1626 std::string mapName = CMap::GetLevelName(abs_filename, true);
1627 if(mapName != "") filelist.insert( List::value_type(GetBaseFilename(abs_filename), mapName) );
1628 }
1629 };
1630 static FileListCache<CheckFileForMap> mapListInstance("map", "levels");
1631 FileListCacheIntf* mapList = &mapListInstance;
1632
1633
1634 struct CheckDirForMod {
1635 typedef FileListCacheIntf::FileList List;
operator ()CheckDirForMod1636 void operator()(List& filelist, const std::string& abs_filename) {
1637 size_t sep = findLastPathSep(abs_filename);
1638 if(sep != std::string::npos) {
1639 std::string name;
1640 if(CGameScript::CheckFile(abs_filename, name, true))
1641 filelist.insert( List::value_type(abs_filename.substr(sep+1), name) );
1642 }
1643 }
1644 };
1645 static FileListCache<CheckDirForMod> modListInstance("mod", "", false, FM_DIR);
1646 FileListCacheIntf* modList = &modListInstance;
1647
1648
1649 struct CheckFileForSkin {
1650 typedef FileListCacheIntf::FileList List;
operator ()CheckFileForSkin1651 void operator()(List& filelist, const std::string& abs_filename) {
1652 std::string ext = GetFileExtension(abs_filename);
1653 if(stringcasecmp(ext, "png")==0
1654 || stringcasecmp(ext, "bmp")==0
1655 || stringcasecmp(ext, "tga")==0
1656 || stringcasecmp(ext, "pcx")==0) {
1657 std::string file = abs_filename;
1658 size_t slash = findLastPathSep(file);
1659 if(slash != std::string::npos)
1660 file.erase(0, slash+1);
1661 std::string name = file.substr(0, file.size()-4); // the size-calcing here is safe
1662 filelist.insert( List::value_type(file, name) );
1663 }
1664 }
1665 };
1666 static FileListCache<CheckFileForSkin> skinListInstance("skin", "skins");
1667 FileListCacheIntf* skinList = &skinListInstance;
1668
1669
1670 struct CheckFileForSettingsPreset {
1671 typedef FileListCacheIntf::FileList List;
operator ()CheckFileForSettingsPreset1672 void operator()(List& filelist, const std::string& abs_filename) {
1673 if(stringcaseequal(GetFileExtension(abs_filename), "cfg")) {
1674 std::string savedConfigSection;
1675 ReadString(abs_filename, "ConfigFileInfo", "Section", savedConfigSection, "", true);
1676 if(stringcaseequal(savedConfigSection, "GameOptions.GameInfo"))
1677 filelist.insert( List::value_type(GetBaseFilename(abs_filename), GetBaseFilenameWithoutExt(abs_filename)) );
1678 }
1679 }
1680 };
1681 static FileListCache<CheckFileForSettingsPreset> settingsPresetListInstance("settings preset", "cfg/presets");
1682 FileListCacheIntf* settingsPresetList = &settingsPresetListInstance;
1683
1684
updateFileListCaches()1685 void updateFileListCaches() {
1686 // just create one single task for all because it wouldn't make it faster by doing that parallel
1687 struct Updater : Task {
1688 Updater() { name = "updateFileListCaches"; }
1689 int handle() {
1690 mapList->update();
1691 modList->update();
1692 skinList->update();
1693 settingsPresetList->update();
1694 return 0;
1695 }
1696 };
1697 taskManager->start(new Updater(), false);
1698 }
1699
1700
1701
currentGameState()1702 GameState currentGameState() {
1703 if(!cClient || cClient->getStatus() == NET_DISCONNECTED) return S_INACTIVE;
1704 if(tLX->iGameType == GME_JOIN) {
1705 if(cClient->getStatus() == NET_CONNECTING) return S_CLICONNECTING;
1706 if(!cClient->getGameReady()) return S_CLILOBBY;
1707 if(cClient->getStatus() == NET_PLAYING) return S_CLIPLAYING;
1708 return S_CLIWEAPONS;
1709 }
1710 if(!cServer->isServerRunning()) return S_INACTIVE;
1711 //if(!DeprecatedGUI::tMenu || DeprecatedGUI::tMenu->bMenuRunning);
1712 if(cServer->getState() == SVS_LOBBY) return S_SVRLOBBY;
1713 if(cServer->getState() == SVS_GAME) return S_SVRWEAPONS;
1714 return S_SVRPLAYING;
1715 }
1716
1717