mainLoop.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "app/mainLoop.h"
  23. #include "app/game.h"
  24. #include "platform/platformTimer.h"
  25. #include "platform/platformRedBook.h"
  26. #include "platform/platformVolume.h"
  27. #include "platform/platformMemory.h"
  28. #include "platform/platformTimer.h"
  29. #include "platform/platformNet.h"
  30. #include "platform/nativeDialogs/fileDialog.h"
  31. #include "platform/threads/thread.h"
  32. #include "core/module.h"
  33. #include "core/threadStatic.h"
  34. #include "core/iTickable.h"
  35. #include "core/stream/fileStream.h"
  36. #include "windowManager/platformWindowMgr.h"
  37. #include "core/util/journal/process.h"
  38. #include "util/fpsTracker.h"
  39. #include "console/debugOutputConsumer.h"
  40. #include "console/consoleTypes.h"
  41. #include "console/engineAPI.h"
  42. #include "console/codeInterpreter.h"
  43. #include "gfx/bitmap/gBitmap.h"
  44. #include "gfx/gFont.h"
  45. #include "gfx/video/videoCapture.h"
  46. #include "gfx/gfxTextureManager.h"
  47. #include "sim/netStringTable.h"
  48. #include "sim/actionMap.h"
  49. #include "sim/netInterface.h"
  50. #include "util/sampler.h"
  51. #include "platform/threads/threadPool.h"
  52. // For the TickMs define... fix this for T2D...
  53. #include "T3D/gameBase/processList.h"
  54. #include "cinterface/cinterface.h"
  55. #ifdef TORQUE_ENABLE_VFS
  56. #include "platform/platformVFS.h"
  57. #endif
  58. #ifndef _MODULE_MANAGER_H
  59. #include "module/moduleManager.h"
  60. #endif
  61. #ifndef _ASSET_MANAGER_H_
  62. #include "assets/assetManager.h"
  63. #endif
  64. DITTS( F32, gTimeScale, 1.0 );
  65. DITTS( U32, gTimeAdvance, 0 );
  66. DITTS( U32, gFrameSkip, 0 );
  67. extern S32 sgBackgroundProcessSleepTime;
  68. extern S32 sgTimeManagerProcessInterval;
  69. extern FPSTracker gFPS;
  70. TimeManager* tm = NULL;
  71. static bool gRequiresRestart = false;
  72. #ifdef TORQUE_DEBUG
  73. /// Temporary timer used to time startup times.
  74. static PlatformTimer* gStartupTimer;
  75. #endif
  76. #if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
  77. StringTableEntry gMiniDumpDir;
  78. StringTableEntry gMiniDumpExec;
  79. StringTableEntry gMiniDumpParams;
  80. StringTableEntry gMiniDumpExecDir;
  81. #endif
  82. namespace engineAPI
  83. {
  84. // This is the magic switch for deciding which interop the engine
  85. // should use. It will go away when we drop the console system
  86. // entirely but for now it is necessary for several behaviors that
  87. // differ between the interops to decide what to do.
  88. bool gUseConsoleInterop = true;
  89. bool gIsInitialized = false;
  90. }
  91. // The following are some tricks to make the memory leak checker run after global
  92. // dtors have executed by placing some code in the termination segments.
  93. #if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
  94. #ifdef TORQUE_COMPILER_VISUALC
  95. # pragma data_seg( ".CRT$XTU" )
  96. static void* sCheckMemBeforeTermination = &Memory::ensureAllFreed;
  97. # pragma data_seg()
  98. #elif defined( TORQUE_COMPILER_GCC )
  99. __attribute__ ( ( destructor ) ) static void _ensureAllFreed()
  100. {
  101. Memory::ensureAllFreed();
  102. }
  103. #endif
  104. #endif
  105. // Process a time event and update all sub-processes
  106. void processTimeEvent(S32 elapsedTime)
  107. {
  108. PROFILE_START(ProcessTimeEvent);
  109. // If recording a video and not playinb back a journal, override the elapsedTime
  110. if (VIDCAP->isRecording() && !Journal::IsPlaying())
  111. elapsedTime = VIDCAP->getMsPerFrame();
  112. // cap the elapsed time to one second
  113. // if it's more than that we're probably in a bad catch-up situation
  114. if(elapsedTime > 1024)
  115. elapsedTime = 1024;
  116. U32 timeDelta;
  117. if(ATTS(gTimeAdvance))
  118. timeDelta = ATTS(gTimeAdvance);
  119. else
  120. timeDelta = (U32) (elapsedTime * ATTS(gTimeScale));
  121. Platform::advanceTime(elapsedTime);
  122. // Don't build up more time than a single tick... this makes the sim
  123. // frame rate dependent but is a useful hack for singleplayer.
  124. if ( ATTS(gFrameSkip) )
  125. if ( timeDelta > TickMs )
  126. timeDelta = TickMs;
  127. bool tickPass;
  128. PROFILE_START(ServerProcess);
  129. tickPass = serverProcess(timeDelta);
  130. PROFILE_END();
  131. PROFILE_START(ServerNetProcess);
  132. // only send packets if a tick happened
  133. if(tickPass)
  134. GNet->processServer();
  135. // Used to indicate if server was just ticked.
  136. Con::setBoolVariable( "$pref::hasServerTicked", tickPass );
  137. PROFILE_END();
  138. PROFILE_START(SimAdvanceTime);
  139. Sim::advanceTime(timeDelta);
  140. PROFILE_END();
  141. PROFILE_START(ClientProcess);
  142. tickPass = clientProcess(timeDelta);
  143. // Used to indicate if client was just ticked.
  144. Con::setBoolVariable( "$pref::hasClientTicked", tickPass );
  145. PROFILE_END_NAMED(ClientProcess);
  146. PROFILE_START(ClientNetProcess);
  147. if(tickPass)
  148. GNet->processClient();
  149. PROFILE_END();
  150. GNet->checkTimeouts();
  151. gFPS.update();
  152. // Give the texture manager a chance to cleanup any
  153. // textures that haven't been referenced for a bit.
  154. if( GFX )
  155. TEXMGR->cleanupCache( 5 );
  156. PROFILE_END();
  157. // Update the console time
  158. Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000);
  159. }
  160. void StandardMainLoop::init()
  161. {
  162. #ifdef TORQUE_DEBUG
  163. gStartupTimer = PlatformTimer::create();
  164. #endif
  165. #ifdef TORQUE_DEBUG_GUARD
  166. Memory::flagCurrentAllocs( Memory::FLAG_Global );
  167. #endif
  168. Platform::setMathControlStateKnown();
  169. // Asserts should be created FIRST
  170. PlatformAssert::create();
  171. ManagedSingleton< ThreadManager >::createSingleton();
  172. FrameAllocator::init(TORQUE_FRAME_SIZE); // See comments in torqueConfig.h
  173. // Initialize the TorqueScript interpreter.
  174. CodeInterpreter::init();
  175. // Yell if we can't initialize the network.
  176. if(!Net::init())
  177. {
  178. AssertISV(false, "StandardMainLoop::initCore - could not initialize networking!");
  179. }
  180. _StringTable::create();
  181. // Set up the resource manager and get some basic file types in it.
  182. Con::init();
  183. Platform::initConsole();
  184. NetStringTable::create();
  185. // Use debug output logging on the Xbox and OSX builds
  186. #if defined( _XBOX ) || defined( TORQUE_OS_MAC )
  187. DebugOutputConsumer::init();
  188. #endif
  189. // init Filesystem first, so we can actually log errors for all components that follow
  190. Platform::FS::InstallFileSystems(); // install all drives for now until we have everything using the volume stuff
  191. Platform::FS::MountDefaults();
  192. // Set our working directory.
  193. Torque::FS::SetCwd( "game:/" );
  194. // Set our working directory.
  195. Platform::setCurrentDirectory( Platform::getMainDotCsDir() );
  196. Processor::init();
  197. Math::init();
  198. Platform::init(); // platform specific initialization
  199. RedBook::init();
  200. Platform::initConsole();
  201. ThreadPool::GlobalThreadPool::createSingleton();
  202. // Set engineAPI initialized to true
  203. engineAPI::gIsInitialized = true;
  204. // Initialize modules.
  205. EngineModuleManager::initializeSystem();
  206. // Initialise ITickable.
  207. #ifdef TORQUE_TGB_ONLY
  208. ITickable::init( 4 );
  209. #endif
  210. #ifdef TORQUE_ENABLE_VFS
  211. // [tom, 10/28/2006] Load the VFS here so that it stays loaded
  212. Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
  213. gResourceManager->addVFSRoot(vfs);
  214. #endif
  215. Con::addVariable("timeScale", TypeF32, &ATTS(gTimeScale), "Animation time scale.\n"
  216. "@ingroup platform");
  217. Con::addVariable("timeAdvance", TypeS32, &ATTS(gTimeAdvance), "The speed at which system processing time advances.\n"
  218. "@ingroup platform");
  219. Con::addVariable("frameSkip", TypeS32, &ATTS(gFrameSkip), "Sets the number of frames to skip while rendering the scene.\n"
  220. "@ingroup platform");
  221. Con::setVariable( "defaultGame", StringTable->insert("scripts") );
  222. Con::addVariable( "_forceAllMainThread", TypeBool, &ThreadPool::getForceAllMainThread(), "Force all work items to execute on main thread. turns this into a single-threaded system. Primarily useful to find whether malfunctions are caused by parallel execution or not.\n"
  223. "@ingroup platform" );
  224. #if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
  225. Con::addVariable("MiniDump::Dir", TypeString, &gMiniDumpDir);
  226. Con::addVariable("MiniDump::Exec", TypeString, &gMiniDumpExec);
  227. Con::addVariable("MiniDump::Params", TypeString, &gMiniDumpParams);
  228. Con::addVariable("MiniDump::ExecDir", TypeString, &gMiniDumpExecDir);
  229. #endif
  230. // Register the module manager.
  231. ModuleDatabase.registerObject("ModuleDatabase");
  232. // Register the asset database.
  233. AssetDatabase.registerObject("AssetDatabase");
  234. // Register the asset database as a module listener.
  235. ModuleDatabase.addListener(&AssetDatabase);
  236. ActionMap* globalMap = new ActionMap;
  237. globalMap->registerObject("GlobalActionMap");
  238. Sim::getActiveActionMapSet()->pushObject(globalMap);
  239. // Do this before we init the process so that process notifiees can get the time manager
  240. tm = new TimeManager;
  241. tm->timeEvent.notify(&::processTimeEvent);
  242. // Start up the Input Event Manager
  243. INPUTMGR->start();
  244. Sampler::init();
  245. // Hook in for UDP notification
  246. Net::getPacketReceiveEvent().notify(GNet, &NetInterface::processPacketReceiveEvent);
  247. #ifdef TORQUE_DEBUG_GUARD
  248. Memory::flagCurrentAllocs( Memory::FLAG_Static );
  249. #endif
  250. }
  251. void StandardMainLoop::shutdown()
  252. {
  253. // Stop the Input Event Manager
  254. INPUTMGR->stop();
  255. delete tm;
  256. preShutdown();
  257. // Unregister the module database.
  258. ModuleDatabase.unregisterObject();
  259. // Unregister the asset database.
  260. AssetDatabase.unregisterObject();
  261. // Shut down modules.
  262. EngineModuleManager::shutdownSystem();
  263. ThreadPool::GlobalThreadPool::deleteSingleton();
  264. #ifdef TORQUE_ENABLE_VFS
  265. closeEmbeddedVFSArchive();
  266. #endif
  267. RedBook::destroy();
  268. Platform::shutdown();
  269. #if defined( _XBOX ) || defined( TORQUE_OS_MAC )
  270. DebugOutputConsumer::destroy();
  271. #endif
  272. NetStringTable::destroy();
  273. Con::shutdown();
  274. _StringTable::destroy();
  275. FrameAllocator::destroy();
  276. Net::shutdown();
  277. Sampler::destroy();
  278. ManagedSingleton< ThreadManager >::deleteSingleton();
  279. // asserts should be destroyed LAST
  280. PlatformAssert::destroy();
  281. #if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
  282. Memory::validate();
  283. #endif
  284. }
  285. void StandardMainLoop::preShutdown()
  286. {
  287. #ifdef TORQUE_TOOLS
  288. // Tools are given a chance to do pre-quit processing
  289. // - This is because for tools we like to do things such
  290. // as prompting to save changes before shutting down
  291. // and onExit is packaged which means we can't be sure
  292. // where in the shutdown namespace chain we are when using
  293. // onExit since some components of the tools may already be
  294. // destroyed that may be vital to saving changes to avoid
  295. // loss of work [1/5/2007 justind]
  296. if( Con::isFunction("onPreExit") )
  297. Con::executef( "onPreExit");
  298. #endif
  299. //exec the script onExit() function
  300. if ( Con::isFunction( "onExit" ) )
  301. Con::executef("onExit");
  302. }
  303. bool StandardMainLoop::handleCommandLine( S32 argc, const char **argv )
  304. {
  305. // Allow the window manager to process command line inputs; this is
  306. // done to let web plugin functionality happen in a fairly transparent way.
  307. PlatformWindowManager::get()->processCmdLineArgs(argc, argv);
  308. Process::handleCommandLine( argc, argv );
  309. // Set up the command line args for the console scripts...
  310. Con::setIntVariable("Game::argc", argc);
  311. U32 i;
  312. for (i = 0; i < argc; i++)
  313. Con::setVariable(avar("Game::argv%d", i), argv[i]);
  314. #ifdef TORQUE_PLAYER
  315. if(argc > 2 && dStricmp(argv[1], "-project") == 0)
  316. {
  317. char playerPath[1024];
  318. Platform::makeFullPathName(argv[2], playerPath, sizeof(playerPath));
  319. Platform::setCurrentDirectory(playerPath);
  320. argv += 2;
  321. argc -= 2;
  322. // Re-locate the game:/ asset mount.
  323. Torque::FS::Unmount( "game" );
  324. Torque::FS::Mount( "game", Platform::FS::createNativeFS( playerPath ) );
  325. }
  326. #endif
  327. // Executes an entry script file. This is "main.tscript"
  328. // by default, but any file name (with no whitespace
  329. // in it) may be run if it is specified as the first
  330. // command-line parameter. The script used, default
  331. // or otherwise, is not compiled and is loaded here
  332. // directly because the resource system restricts
  333. // access to the "root" directory.
  334. bool foundExternalMain = false;
  335. CInterface::CallMain(&foundExternalMain);
  336. if (foundExternalMain)
  337. return true;
  338. #ifdef TORQUE_ENABLE_VFS
  339. Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
  340. bool useVFS = vfs != NULL;
  341. #endif
  342. Stream *mainCsStream = NULL;
  343. // The working filestream.
  344. FileStream str;
  345. const char *defaultScriptName = "main." TORQUE_SCRIPT_EXTENSION;
  346. bool useDefaultScript = true;
  347. // Check if any command-line parameters were passed (the first is just the app name).
  348. if (argc > 1)
  349. {
  350. // If so, check if the first parameter is a file to open.
  351. if ( (String::compare(argv[1], "") != 0 ) && (str.open(argv[1], Torque::FS::File::Read)) )
  352. {
  353. // If it opens, we assume it is the script to run.
  354. useDefaultScript = false;
  355. #ifdef TORQUE_ENABLE_VFS
  356. useVFS = false;
  357. #endif
  358. mainCsStream = &str;
  359. }
  360. }
  361. if (useDefaultScript)
  362. {
  363. bool success = false;
  364. #ifdef TORQUE_ENABLE_VFS
  365. if(useVFS)
  366. success = (mainCsStream = vfs->openFile(defaultScriptName, Zip::ZipArchive::Read)) != NULL;
  367. else
  368. #endif
  369. success = str.open(defaultScriptName, Torque::FS::File::Read);
  370. #if defined( TORQUE_DEBUG ) && defined (TORQUE_TOOLS) && !defined(TORQUE_DEDICATED) && !defined( _XBOX )
  371. if (!success)
  372. {
  373. OpenFileDialog ofd;
  374. FileDialogData &fdd = ofd.getData();
  375. fdd.mFilters = StringTable->insert("Main Entry Script (main." TORQUE_SCRIPT_EXTENSION ")|main." TORQUE_SCRIPT_EXTENSION "|");
  376. fdd.mTitle = StringTable->insert("Locate Game Entry Script");
  377. // Get the user's selection
  378. if( !ofd.Execute() )
  379. return false;
  380. // Process and update CWD so we can run the selected main.tscript
  381. S32 pathLen = dStrlen( fdd.mFile );
  382. FrameTemp<char> szPathCopy( pathLen + 1);
  383. dStrcpy( szPathCopy, fdd.mFile, pathLen + 1 );
  384. //forwardslash( szPathCopy );
  385. const char *path = dStrrchr(szPathCopy, '/');
  386. if(path)
  387. {
  388. U32 len = path - (const char*)szPathCopy;
  389. szPathCopy[len+1] = 0;
  390. Platform::setCurrentDirectory(szPathCopy);
  391. // Re-locate the game:/ asset mount.
  392. Torque::FS::Unmount( "game" );
  393. Torque::FS::Mount( "game", Platform::FS::createNativeFS( ( const char* ) szPathCopy ) );
  394. success = str.open(fdd.mFile, Torque::FS::File::Read);
  395. if(success)
  396. defaultScriptName = fdd.mFile;
  397. }
  398. }
  399. #endif
  400. if( !success )
  401. {
  402. char msg[1024];
  403. dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName);
  404. Platform::AlertOK("Error", msg);
  405. #ifdef TORQUE_ENABLE_VFS
  406. closeEmbeddedVFSArchive();
  407. #endif
  408. return false;
  409. }
  410. #ifdef TORQUE_ENABLE_VFS
  411. if(! useVFS)
  412. #endif
  413. mainCsStream = &str;
  414. }
  415. // This should rarely happen, but lets deal with
  416. // it gracefully if it does.
  417. if ( mainCsStream == NULL )
  418. return false;
  419. U32 size = mainCsStream->getStreamSize();
  420. char *script = new char[size + 1];
  421. mainCsStream->read(size, script);
  422. #ifdef TORQUE_ENABLE_VFS
  423. if(useVFS)
  424. vfs->closeFile(mainCsStream);
  425. else
  426. #endif
  427. str.close();
  428. script[size] = 0;
  429. char buffer[1024], *ptr;
  430. Platform::makeFullPathName(useDefaultScript ? defaultScriptName : argv[1], buffer, sizeof(buffer), Platform::getCurrentDirectory());
  431. ptr = dStrrchr(buffer, '/');
  432. if(ptr != NULL)
  433. *ptr = 0;
  434. Platform::setMainDotCsDir(buffer);
  435. Platform::setCurrentDirectory(buffer);
  436. Con::setVariable("TorqueScriptFileExtension", TORQUE_SCRIPT_EXTENSION);
  437. Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]);
  438. delete[] script;
  439. #ifdef TORQUE_ENABLE_VFS
  440. closeEmbeddedVFSArchive();
  441. #endif
  442. return true;
  443. }
  444. bool StandardMainLoop::doMainLoop()
  445. {
  446. #ifdef TORQUE_DEBUG
  447. if( gStartupTimer )
  448. {
  449. Con::printf( "Started up in %.2f seconds...",
  450. F32( gStartupTimer->getElapsedMs() ) / 1000.f );
  451. SAFE_DELETE( gStartupTimer );
  452. }
  453. #endif
  454. bool keepRunning = true;
  455. // while(keepRunning)
  456. {
  457. tm->setBackgroundThreshold(mClamp(sgBackgroundProcessSleepTime, 1, 200));
  458. tm->setForegroundThreshold(mClamp(sgTimeManagerProcessInterval, 1, 200));
  459. // update foreground/background status
  460. if(WindowManager->getFirstWindow())
  461. {
  462. static bool lastFocus = false;
  463. bool newFocus = ( WindowManager->getFocusedWindow() != NULL );
  464. if(lastFocus != newFocus)
  465. {
  466. #ifndef TORQUE_SHIPPING
  467. Con::printf("Window focus status changed: focus: %d", newFocus);
  468. if (!newFocus)
  469. Con::printf(" Using background sleep time: %u", Platform::getBackgroundSleepTime());
  470. #endif
  471. #ifdef TORQUE_OS_MAC
  472. if (newFocus)
  473. WindowManager->getFirstWindow()->show();
  474. #endif
  475. lastFocus = newFocus;
  476. }
  477. // under the web plugin do not sleep the process when the child window loses focus as this will cripple the browser perfomance
  478. if (!Platform::getWebDeployment())
  479. tm->setBackground(!newFocus);
  480. else
  481. tm->setBackground(false);
  482. }
  483. else
  484. {
  485. tm->setBackground(false);
  486. }
  487. PROFILE_START(MainLoop);
  488. Sampler::beginFrame();
  489. if(!Process::processEvents())
  490. keepRunning = false;
  491. ThreadPool::processMainThreadWorkItems();
  492. Sampler::endFrame();
  493. PROFILE_END_NAMED(MainLoop);
  494. }
  495. return keepRunning;
  496. }
  497. S32 StandardMainLoop::getReturnStatus()
  498. {
  499. return Process::getReturnStatus();
  500. }
  501. void StandardMainLoop::setRestart(bool restart )
  502. {
  503. gRequiresRestart = restart;
  504. }
  505. bool StandardMainLoop::requiresRestart()
  506. {
  507. return gRequiresRestart;
  508. }