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