mainLoop.cpp

Engine/source/app/mainLoop.cpp

More...

Namespaces:

namespace

Public Functions

DITTS(F32 , gTimeScale , 1. 0)
DITTS(U32 , gFrameSkip , 0 )
DITTS(U32 , gTimeAdvance , 0 )
processTimeEvent(S32 elapsedTime)

Detailed Description

Public Variables

FPSTracker gFPS 
bool gRequiresRestart 
S32 sgBackgroundProcessSleepTime 
S32 sgTimeManagerProcessInterval 
TimeManager * tm 

Public Functions

DITTS(F32 , gTimeScale , 1. 0)

DITTS(U32 , gFrameSkip , 0 )

DITTS(U32 , gTimeAdvance , 0 )

processTimeEvent(S32 elapsedTime)

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