123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "app/mainLoop.h"
- #include "app/game.h"
- #include "platform/platformTimer.h"
- #include "platform/platformRedBook.h"
- #include "platform/platformVolume.h"
- #include "platform/platformMemory.h"
- #include "platform/platformTimer.h"
- #include "platform/platformNet.h"
- #include "platform/nativeDialogs/fileDialog.h"
- #include "platform/threads/thread.h"
- #include "core/module.h"
- #include "core/threadStatic.h"
- #include "core/iTickable.h"
- #include "core/stream/fileStream.h"
- #include "windowManager/platformWindowMgr.h"
- #include "core/util/journal/process.h"
- #include "util/fpsTracker.h"
- #include "console/debugOutputConsumer.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "gfx/bitmap/gBitmap.h"
- #include "gfx/gFont.h"
- #include "gfx/video/videoCapture.h"
- #include "gfx/gfxTextureManager.h"
- #include "sim/netStringTable.h"
- #include "sim/actionMap.h"
- #include "sim/netInterface.h"
- #include "util/sampler.h"
- #include "platform/threads/threadPool.h"
- // For the TickMs define... fix this for T2D...
- #include "T3D/gameBase/processList.h"
- #include "cinterface/cinterface.h"
- #ifdef TORQUE_ENABLE_VFS
- #include "platform/platformVFS.h"
- #endif
- #ifndef _MODULE_MANAGER_H
- #include "module/moduleManager.h"
- #endif
- #ifndef _ASSET_MANAGER_H_
- #include "assets/assetManager.h"
- #endif
- DITTS( F32, gTimeScale, 1.0 );
- DITTS( U32, gTimeAdvance, 0 );
- DITTS( U32, gFrameSkip, 0 );
- extern S32 sgBackgroundProcessSleepTime;
- extern S32 sgTimeManagerProcessInterval;
- extern FPSTracker gFPS;
- TimeManager* tm = NULL;
- static bool gRequiresRestart = false;
- #ifdef TORQUE_DEBUG
- /// Temporary timer used to time startup times.
- static PlatformTimer* gStartupTimer;
- #endif
- #if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
- StringTableEntry gMiniDumpDir;
- StringTableEntry gMiniDumpExec;
- StringTableEntry gMiniDumpParams;
- StringTableEntry gMiniDumpExecDir;
- #endif
- namespace engineAPI
- {
- // This is the magic switch for deciding which interop the engine
- // should use. It will go away when we drop the console system
- // entirely but for now it is necessary for several behaviors that
- // differ between the interops to decide what to do.
- bool gUseConsoleInterop = true;
-
- bool gIsInitialized = false;
- }
- // The following are some tricks to make the memory leak checker run after global
- // dtors have executed by placing some code in the termination segments.
- #if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
- #ifdef TORQUE_COMPILER_VISUALC
- # pragma data_seg( ".CRT$XTU" )
-
- static void* sCheckMemBeforeTermination = &Memory::ensureAllFreed;
-
- # pragma data_seg()
- #elif defined( TORQUE_COMPILER_GCC )
-
- __attribute__ ( ( destructor ) ) static void _ensureAllFreed()
- {
- Memory::ensureAllFreed();
- }
-
- #endif
- #endif
- // Process a time event and update all sub-processes
- void processTimeEvent(S32 elapsedTime)
- {
- PROFILE_START(ProcessTimeEvent);
- // If recording a video and not playinb back a journal, override the elapsedTime
- if (VIDCAP->isRecording() && !Journal::IsPlaying())
- elapsedTime = VIDCAP->getMsPerFrame();
-
- // cap the elapsed time to one second
- // if it's more than that we're probably in a bad catch-up situation
- if(elapsedTime > 1024)
- elapsedTime = 1024;
-
- U32 timeDelta;
- if(ATTS(gTimeAdvance))
- timeDelta = ATTS(gTimeAdvance);
- else
- timeDelta = (U32) (elapsedTime * ATTS(gTimeScale));
-
- Platform::advanceTime(elapsedTime);
-
- // Don't build up more time than a single tick... this makes the sim
- // frame rate dependent but is a useful hack for singleplayer.
- if ( ATTS(gFrameSkip) )
- if ( timeDelta > TickMs )
- timeDelta = TickMs;
- bool tickPass;
-
- PROFILE_START(ServerProcess);
- tickPass = serverProcess(timeDelta);
- PROFILE_END();
-
- PROFILE_START(ServerNetProcess);
- // only send packets if a tick happened
- if(tickPass)
- GNet->processServer();
- // Used to indicate if server was just ticked.
- Con::setBoolVariable( "$pref::hasServerTicked", tickPass );
- PROFILE_END();
-
- PROFILE_START(SimAdvanceTime);
- Sim::advanceTime(timeDelta);
- PROFILE_END();
-
- PROFILE_START(ClientProcess);
- tickPass = clientProcess(timeDelta);
- // Used to indicate if client was just ticked.
- Con::setBoolVariable( "$pref::hasClientTicked", tickPass );
- PROFILE_END_NAMED(ClientProcess);
-
- PROFILE_START(ClientNetProcess);
- if(tickPass)
- GNet->processClient();
- PROFILE_END();
-
- GNet->checkTimeouts();
-
- gFPS.update();
- // Give the texture manager a chance to cleanup any
- // textures that haven't been referenced for a bit.
- if( GFX )
- TEXMGR->cleanupCache( 5 );
- PROFILE_END();
-
- // Update the console time
- Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000);
- }
- void StandardMainLoop::init()
- {
- #ifdef TORQUE_DEBUG
- gStartupTimer = PlatformTimer::create();
- #endif
-
- #ifdef TORQUE_DEBUG_GUARD
- Memory::flagCurrentAllocs( Memory::FLAG_Global );
- #endif
- Platform::setMathControlStateKnown();
-
- // Asserts should be created FIRST
- PlatformAssert::create();
-
- ManagedSingleton< ThreadManager >::createSingleton();
- FrameAllocator::init(TORQUE_FRAME_SIZE); // See comments in torqueConfig.h
- // Yell if we can't initialize the network.
- if(!Net::init())
- {
- AssertISV(false, "StandardMainLoop::initCore - could not initialize networking!");
- }
- _StringTable::create();
- // Set up the resource manager and get some basic file types in it.
- Con::init();
- Platform::initConsole();
- NetStringTable::create();
- // Use debug output logging on the Xbox and OSX builds
- #if defined( _XBOX ) || defined( TORQUE_OS_MAC )
- DebugOutputConsumer::init();
- #endif
- // init Filesystem first, so we can actually log errors for all components that follow
- Platform::FS::InstallFileSystems(); // install all drives for now until we have everything using the volume stuff
- Platform::FS::MountDefaults();
- // Set our working directory.
- Torque::FS::SetCwd( "game:/" );
- // Set our working directory.
- Platform::setCurrentDirectory( Platform::getMainDotCsDir() );
- Processor::init();
- Math::init();
- Platform::init(); // platform specific initialization
- RedBook::init();
- Platform::initConsole();
-
- ThreadPool::GlobalThreadPool::createSingleton();
- // Set engineAPI initialized to true
- engineAPI::gIsInitialized = true;
- // Initialize modules.
-
- EngineModuleManager::initializeSystem();
-
- // Initialise ITickable.
- #ifdef TORQUE_TGB_ONLY
- ITickable::init( 4 );
- #endif
- #ifdef TORQUE_ENABLE_VFS
- // [tom, 10/28/2006] Load the VFS here so that it stays loaded
- Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
- gResourceManager->addVFSRoot(vfs);
- #endif
- Con::addVariable("timeScale", TypeF32, &ATTS(gTimeScale), "Animation time scale.\n"
- "@ingroup platform");
- Con::addVariable("timeAdvance", TypeS32, &ATTS(gTimeAdvance), "The speed at which system processing time advances.\n"
- "@ingroup platform");
- Con::addVariable("frameSkip", TypeS32, &ATTS(gFrameSkip), "Sets the number of frames to skip while rendering the scene.\n"
- "@ingroup platform");
- Con::setVariable( "defaultGame", StringTable->insert("scripts") );
- 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"
- "@ingroup platform" );
- #if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
- Con::addVariable("MiniDump::Dir", TypeString, &gMiniDumpDir);
- Con::addVariable("MiniDump::Exec", TypeString, &gMiniDumpExec);
- Con::addVariable("MiniDump::Params", TypeString, &gMiniDumpParams);
- Con::addVariable("MiniDump::ExecDir", TypeString, &gMiniDumpExecDir);
- #endif
- // Register the module manager.
- ModuleDatabase.registerObject("ModuleDatabase");
- // Register the asset database.
- AssetDatabase.registerObject("AssetDatabase");
- // Register the asset database as a module listener.
- ModuleDatabase.addListener(&AssetDatabase);
-
- ActionMap* globalMap = new ActionMap;
- globalMap->registerObject("GlobalActionMap");
- Sim::getActiveActionMapSet()->pushObject(globalMap);
-
- // Do this before we init the process so that process notifiees can get the time manager
- tm = new TimeManager;
- tm->timeEvent.notify(&::processTimeEvent);
-
- // Start up the Input Event Manager
- INPUTMGR->start();
- Sampler::init();
- // Hook in for UDP notification
- Net::getPacketReceiveEvent().notify(GNet, &NetInterface::processPacketReceiveEvent);
- #ifdef TORQUE_DEBUG_GUARD
- Memory::flagCurrentAllocs( Memory::FLAG_Static );
- #endif
- }
- void StandardMainLoop::shutdown()
- {
- // Stop the Input Event Manager
- INPUTMGR->stop();
- delete tm;
- preShutdown();
- // Unregister the module database.
- ModuleDatabase.unregisterObject();
- // Unregister the asset database.
- AssetDatabase.unregisterObject();
-
- // Shut down modules.
-
- EngineModuleManager::shutdownSystem();
-
- ThreadPool::GlobalThreadPool::deleteSingleton();
- #ifdef TORQUE_ENABLE_VFS
- closeEmbeddedVFSArchive();
- #endif
- RedBook::destroy();
- Platform::shutdown();
-
- #if defined( _XBOX ) || defined( TORQUE_OS_MAC )
- DebugOutputConsumer::destroy();
- #endif
- NetStringTable::destroy();
- Con::shutdown();
- _StringTable::destroy();
- FrameAllocator::destroy();
- Net::shutdown();
- Sampler::destroy();
-
- ManagedSingleton< ThreadManager >::deleteSingleton();
- // asserts should be destroyed LAST
- PlatformAssert::destroy();
- #if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER )
- Memory::validate();
- #endif
- }
- void StandardMainLoop::preShutdown()
- {
- #ifdef TORQUE_TOOLS
- // Tools are given a chance to do pre-quit processing
- // - This is because for tools we like to do things such
- // as prompting to save changes before shutting down
- // and onExit is packaged which means we can't be sure
- // where in the shutdown namespace chain we are when using
- // onExit since some components of the tools may already be
- // destroyed that may be vital to saving changes to avoid
- // loss of work [1/5/2007 justind]
- if( Con::isFunction("onPreExit") )
- Con::executef( "onPreExit");
- #endif
- //exec the script onExit() function
- if ( Con::isFunction( "onExit" ) )
- Con::executef("onExit");
- }
- bool StandardMainLoop::handleCommandLine( S32 argc, const char **argv )
- {
- // Allow the window manager to process command line inputs; this is
- // done to let web plugin functionality happen in a fairly transparent way.
- PlatformWindowManager::get()->processCmdLineArgs(argc, argv);
- Process::handleCommandLine( argc, argv );
- // Set up the command line args for the console scripts...
- Con::setIntVariable("Game::argc", argc);
- U32 i;
- for (i = 0; i < argc; i++)
- Con::setVariable(avar("Game::argv%d", i), argv[i]);
- #ifdef TORQUE_PLAYER
- if(argc > 2 && dStricmp(argv[1], "-project") == 0)
- {
- char playerPath[1024];
- Platform::makeFullPathName(argv[2], playerPath, sizeof(playerPath));
- Platform::setCurrentDirectory(playerPath);
- argv += 2;
- argc -= 2;
- // Re-locate the game:/ asset mount.
- Torque::FS::Unmount( "game" );
- Torque::FS::Mount( "game", Platform::FS::createNativeFS( playerPath ) );
- }
- #endif
- // Executes an entry script file. This is "main.tscript"
- // by default, but any file name (with no whitespace
- // in it) may be run if it is specified as the first
- // command-line parameter. The script used, default
- // or otherwise, is not compiled and is loaded here
- // directly because the resource system restricts
- // access to the "root" directory.
- bool foundExternalMain = false;
- CInterface::CallMain(&foundExternalMain);
- if (foundExternalMain)
- return true;
- #ifdef TORQUE_ENABLE_VFS
- Zip::ZipArchive *vfs = openEmbeddedVFSArchive();
- bool useVFS = vfs != NULL;
- #endif
- Stream *mainCsStream = NULL;
- // The working filestream.
- FileStream str;
- const char *defaultScriptName = "main." TORQUE_SCRIPT_EXTENSION;
- bool useDefaultScript = true;
- // Check if any command-line parameters were passed (the first is just the app name).
- if (argc > 1)
- {
- // If so, check if the first parameter is a file to open.
- if ( (String::compare(argv[1], "") != 0 ) && (str.open(argv[1], Torque::FS::File::Read)) )
- {
- // If it opens, we assume it is the script to run.
- useDefaultScript = false;
- #ifdef TORQUE_ENABLE_VFS
- useVFS = false;
- #endif
- mainCsStream = &str;
- }
- }
- if (useDefaultScript)
- {
- bool success = false;
- #ifdef TORQUE_ENABLE_VFS
- if(useVFS)
- success = (mainCsStream = vfs->openFile(defaultScriptName, Zip::ZipArchive::Read)) != NULL;
- else
- #endif
- success = str.open(defaultScriptName, Torque::FS::File::Read);
- #if defined( TORQUE_DEBUG ) && defined (TORQUE_TOOLS) && !defined(TORQUE_DEDICATED) && !defined( _XBOX )
- if (!success)
- {
- OpenFileDialog ofd;
- FileDialogData &fdd = ofd.getData();
- fdd.mFilters = StringTable->insert("Main Entry Script (main." TORQUE_SCRIPT_EXTENSION ")|main." TORQUE_SCRIPT_EXTENSION "|");
- fdd.mTitle = StringTable->insert("Locate Game Entry Script");
- // Get the user's selection
- if( !ofd.Execute() )
- return false;
- // Process and update CWD so we can run the selected main.tscript
- S32 pathLen = dStrlen( fdd.mFile );
- FrameTemp<char> szPathCopy( pathLen + 1);
- dStrcpy( szPathCopy, fdd.mFile, pathLen + 1 );
- //forwardslash( szPathCopy );
- const char *path = dStrrchr(szPathCopy, '/');
- if(path)
- {
- U32 len = path - (const char*)szPathCopy;
- szPathCopy[len+1] = 0;
- Platform::setCurrentDirectory(szPathCopy);
- // Re-locate the game:/ asset mount.
- Torque::FS::Unmount( "game" );
- Torque::FS::Mount( "game", Platform::FS::createNativeFS( ( const char* ) szPathCopy ) );
- success = str.open(fdd.mFile, Torque::FS::File::Read);
- if(success)
- defaultScriptName = fdd.mFile;
- }
- }
- #endif
- if( !success )
- {
- char msg[1024];
- dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName);
- Platform::AlertOK("Error", msg);
- #ifdef TORQUE_ENABLE_VFS
- closeEmbeddedVFSArchive();
- #endif
- return false;
- }
- #ifdef TORQUE_ENABLE_VFS
- if(! useVFS)
- #endif
- mainCsStream = &str;
- }
- // This should rarely happen, but lets deal with
- // it gracefully if it does.
- if ( mainCsStream == NULL )
- return false;
- U32 size = mainCsStream->getStreamSize();
- char *script = new char[size + 1];
- mainCsStream->read(size, script);
- #ifdef TORQUE_ENABLE_VFS
- if(useVFS)
- vfs->closeFile(mainCsStream);
- else
- #endif
- str.close();
- script[size] = 0;
- char buffer[1024], *ptr;
- Platform::makeFullPathName(useDefaultScript ? defaultScriptName : argv[1], buffer, sizeof(buffer), Platform::getCurrentDirectory());
- ptr = dStrrchr(buffer, '/');
- if(ptr != NULL)
- *ptr = 0;
- Platform::setMainDotCsDir(buffer);
- Platform::setCurrentDirectory(buffer);
- Con::setVariable("TorqueScriptFileExtension", TORQUE_SCRIPT_EXTENSION);
- Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]);
- delete[] script;
- #ifdef TORQUE_ENABLE_VFS
- closeEmbeddedVFSArchive();
- #endif
- return true;
- }
- bool StandardMainLoop::doMainLoop()
- {
- #ifdef TORQUE_DEBUG
- if( gStartupTimer )
- {
- Con::printf( "Started up in %.2f seconds...",
- F32( gStartupTimer->getElapsedMs() ) / 1000.f );
- SAFE_DELETE( gStartupTimer );
- }
- #endif
-
- bool keepRunning = true;
- // while(keepRunning)
- {
- tm->setBackgroundThreshold(mClamp(sgBackgroundProcessSleepTime, 1, 200));
- tm->setForegroundThreshold(mClamp(sgTimeManagerProcessInterval, 1, 200));
- // update foreground/background status
- if(WindowManager->getFirstWindow())
- {
- static bool lastFocus = false;
- bool newFocus = ( WindowManager->getFocusedWindow() != NULL );
- if(lastFocus != newFocus)
- {
- #ifndef TORQUE_SHIPPING
- Con::printf("Window focus status changed: focus: %d", newFocus);
- if (!newFocus)
- Con::printf(" Using background sleep time: %u", Platform::getBackgroundSleepTime());
- #endif
- #ifdef TORQUE_OS_MAC
- if (newFocus)
- WindowManager->getFirstWindow()->show();
-
- #endif
- lastFocus = newFocus;
- }
-
- // under the web plugin do not sleep the process when the child window loses focus as this will cripple the browser perfomance
- if (!Platform::getWebDeployment())
- tm->setBackground(!newFocus);
- else
- tm->setBackground(false);
- }
- else
- {
- tm->setBackground(false);
- }
-
- PROFILE_START(MainLoop);
- Sampler::beginFrame();
- if(!Process::processEvents())
- keepRunning = false;
- ThreadPool::processMainThreadWorkItems();
- Sampler::endFrame();
- ConsoleValue::resetConversionBuffer();
- PROFILE_END_NAMED(MainLoop);
- }
-
- return keepRunning;
- }
- S32 StandardMainLoop::getReturnStatus()
- {
- return Process::getReturnStatus();
- }
- void StandardMainLoop::setRestart(bool restart )
- {
- gRequiresRestart = restart;
- }
- bool StandardMainLoop::requiresRestart()
- {
- return gRequiresRestart;
- }
|