//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platform/platform.h" #include "platform/platformVideo.h" #include "platform/platformInput.h" #include "platform/platformAudio.h" #include "platform/event.h" #include "platform/gameInterface.h" #include "core/tVector.h" #include "core/chunkFile.h" #include "math/mMath.h" #include "dgl/dgl.h" #include "dgl/gBitmap.h" #include "core/resManager.h" #include "core/fileStream.h" #include "dgl/gTexManager.h" #include "dgl/gFont.h" #include "console/console.h" #include "console/simBase.h" #include "gui/core/guiCanvas.h" #include "sim/actionMap.h" #include "core/dnet.h" #include "game/game.h" #include "core/bitStream.h" #include "console/telnetConsole.h" #include "console/telnetDebugger.h" #include "console/consoleTypes.h" #include "math/mathTypes.h" #include "dgl/gTexManager.h" #include "core/resManager.h" #include "interior/interiorRes.h" #include "interior/interiorInstance.h" #include "interior/interiorMapRes.h" #include "ts/tsShapeInstance.h" #include "terrain/terrData.h" #include "terrain/terrRender.h" #include "editor/terraformer.h" #include "sceneGraph/sceneGraph.h" #include "dgl/materialList.h" #include "sceneGraph/sceneRoot.h" #include "game/moveManager.h" #include "platform/platformVideo.h" #include "dgl/materialPropertyMap.h" #include "sim/netStringTable.h" #include "sim/pathManager.h" #include "game/gameFunctions.h" #include "game/fx/particleEngine.h" #include "platform/platformRedBook.h" #include "game/demoGame.h" #include "sim/decalManager.h" #include "core/frameAllocator.h" #include "sceneGraph/detailManager.h" #include "interior/interiorLMManager.h" #include "game/version.h" #include "platform/profiler.h" #include "game/shapeBase.h" #include "game/objectTypes.h" #include "game/net/serverQuery.h" #include "game/badWordFilter.h" #ifndef BUILD_TOOLS DemoGame GameObject; DemoNetInterface GameNetInterface; #endif extern ResourceInstance *constructTerrainFile(Stream &stream); extern ResourceInstance *constructTSShape(Stream &); ConsoleFunctionGroupBegin( Platform , "General platform functions."); ConsoleFunction( lockMouse, void, 2, 2, "(bool isLocked)" "Lock the mouse (or not, depending on the argument's value) to the window.") { Platform::setWindowLocked(dAtob(argv[1])); } ConsoleFunction( setNetPort, bool, 2, 2, "(int port)" "Set the network port for the game to use.") { return Net::openPort(dAtoi(argv[1])); } ConsoleFunction( saveJournal, void, 2, 2, "(string filename)" "Save the journal to the specified file.") { Game->saveJournal(argv[1]); } ConsoleFunction( playJournal, void, 2, 3, "(string filename, bool break=false)" "Begin playback of a journal from a specified field, optionally breaking at the start.") { bool jBreak = (argc > 2)? dAtob(argv[2]): false; Game->playJournal(argv[1],jBreak); } extern void netInit(); extern void processConnectedReceiveEvent( ConnectedReceiveEvent * event ); extern void processConnectedNotifyEvent( ConnectedNotifyEvent * event ); extern void processConnectedAcceptEvent( ConnectedAcceptEvent * event ); extern void ShowInit(); /// Initalizes the components of the game like the TextureManager, ResourceManager /// console...etc. static bool initLibraries() { if(!Net::init()) { Platform::AlertOK("Network Error", "Unable to initialize the network... aborting."); return false; } // asserts should be created FIRST PlatformAssert::create(); FrameAllocator::init(3 << 20); // 3 meg frame allocator buffer // // Cryptographic pool next // CryptRandomPool::init(); _StringTable::create(); TextureManager::create(); ResManager::create(); // Register known file types here ResourceManager->registerExtension(".jpg", constructBitmapJPEG); ResourceManager->registerExtension(".png", constructBitmapPNG); ResourceManager->registerExtension(".gif", constructBitmapGIF); ResourceManager->registerExtension(".dbm", constructBitmapDBM); ResourceManager->registerExtension(".bmp", constructBitmapBMP); ResourceManager->registerExtension(".bm8", constructBitmapBM8); ResourceManager->registerExtension(".uft", constructFont); ResourceManager->registerExtension(".dif", constructInteriorDIF); ResourceManager->registerExtension(".ter", constructTerrainFile); ResourceManager->registerExtension(".dts", constructTSShape); ResourceManager->registerExtension(".dml", constructMaterialList); ResourceManager->registerExtension(".map", constructInteriorMAP); Con::init(); NetStringTable::create(); TelnetConsole::create(); TelnetDebugger::create(); Processor::init(); Math::init(); Platform::init(); // platform specific initialization InteriorLMManager::init(); InteriorInstance::init(); TSShapeInstance::init(); RedBook::init(); Platform::initConsole(); return true; } /// Destroys all the things initalized in initLibraries static void shutdownLibraries() { // Purge any resources on the timeout list... if (ResourceManager) ResourceManager->purge(); RedBook::destroy(); TSShapeInstance::destroy(); InteriorInstance::destroy(); InteriorLMManager::destroy(); TextureManager::preDestroy(); Platform::shutdown(); TelnetDebugger::destroy(); TelnetConsole::destroy(); NetStringTable::destroy(); Con::shutdown(); ResManager::destroy(); TextureManager::destroy(); _StringTable::destroy(); // asserts should be destroyed LAST FrameAllocator::destroy(); PlatformAssert::destroy(); Net::shutdown(); } ConsoleFunction( getSimTime, S32, 1, 1, "Return the current sim time in milliseconds.\n\n" "Sim time is time since the game started.") { return Sim::getCurrentTime(); } ConsoleFunction( getRealTime, S32, 1, 1, "Return the current real time in milliseconds.\n\n" "Real time is platform defined; typically time since the computer booted.") { return Platform::getRealMilliseconds(); } ConsoleFunctionGroupEnd(Platform); static F32 gTimeScale = 1.0; static U32 gTimeAdvance = 0; static U32 gFrameSkip = 0; static U32 gFrameCount = 0; // Executes an entry script; can be controlled by command-line options. bool runEntryScript (int argc, const char **argv) { // Executes an entry script file. This is "main.cs" // 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. FileStream str; // The working filestream. const char* defaultScriptName = "main.cs"; 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 ( (str.open(argv[1], FileStream::Read)) && (argv[1] != "") ) { // If it opens, we assume it is the script to run. useDefaultScript = false; } } if (useDefaultScript) { if (!str.open(defaultScriptName, FileStream::Read)) { char msg[1024]; dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName); Con::errorf(msg); Platform::AlertOK("Error", msg); return false; } } U32 size = str.getStreamSize(); char *script = new char[size + 1]; str.read(size, script); str.close(); script[size] = 0; Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]); delete[] script; return true; } /// Initalize game, run the specified startup script bool initGame(int argc, const char **argv) { Con::setFloatVariable("Video::texResidentPercentage", -1.0f); Con::setIntVariable("Video::textureCacheMisses", -1); Con::addVariable("timeScale", TypeF32, &gTimeScale); Con::addVariable("timeAdvance", TypeS32, &gTimeAdvance); Con::addVariable("frameSkip", TypeS32, &gFrameSkip); // Stuff game types into the console Con::setIntVariable("$TypeMasks::StaticObjectType", StaticObjectType); Con::setIntVariable("$TypeMasks::EnvironmentObjectType", EnvironmentObjectType); Con::setIntVariable("$TypeMasks::TerrainObjectType", TerrainObjectType); Con::setIntVariable("$TypeMasks::InteriorObjectType", InteriorObjectType); Con::setIntVariable("$TypeMasks::WaterObjectType", WaterObjectType); Con::setIntVariable("$TypeMasks::TriggerObjectType", TriggerObjectType); Con::setIntVariable("$TypeMasks::MarkerObjectType", MarkerObjectType); Con::setIntVariable("$TypeMasks::GameBaseObjectType", GameBaseObjectType); Con::setIntVariable("$TypeMasks::ShapeBaseObjectType", ShapeBaseObjectType); Con::setIntVariable("$TypeMasks::CameraObjectType", CameraObjectType); Con::setIntVariable("$TypeMasks::StaticShapeObjectType", StaticShapeObjectType); Con::setIntVariable("$TypeMasks::PlayerObjectType", PlayerObjectType); Con::setIntVariable("$TypeMasks::ItemObjectType", ItemObjectType); Con::setIntVariable("$TypeMasks::VehicleObjectType", VehicleObjectType); Con::setIntVariable("$TypeMasks::VehicleBlockerObjectType", VehicleBlockerObjectType); Con::setIntVariable("$TypeMasks::ProjectileObjectType", ProjectileObjectType); Con::setIntVariable("$TypeMasks::ExplosionObjectType", ExplosionObjectType); Con::setIntVariable("$TypeMasks::CorpseObjectType", CorpseObjectType); Con::setIntVariable("$TypeMasks::DebrisObjectType", DebrisObjectType); Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType", PhysicalZoneObjectType); Con::setIntVariable("$TypeMasks::StaticTSObjectType", StaticTSObjectType); Con::setIntVariable("$TypeMasks::StaticRenderedObjectType", StaticRenderedObjectType); Con::setIntVariable("$TypeMasks::DamagableItemObjectType", DamagableItemObjectType); Con::setIntVariable("$TypeMasks::InteriorMapObjectType", InteriorMapObjectType); // #ifdef TORQUE_GATHER_METRICS Con::addVariable("Video::numTexelsLoaded", TypeS32, &TextureManager::smTextureSpaceLoaded); #else static U32 sBogusNTL = 0; Con::addVariable("Video::numTexelsLoaded", TypeS32, &sBogusNTL); #endif TerrainRender::init(); netInit(); GameInit(); ShowInit(); MoveManager::init(); Sim::init(); ActionMap* globalMap = new ActionMap; globalMap->registerObject("GlobalActionMap"); Sim::getActiveActionMapSet()->pushObject(globalMap); MaterialPropertyMap *map = new MaterialPropertyMap; map->registerObject("MaterialPropertyMap"); Sim::getRootGroup()->addObject(map); gClientSceneGraph = new SceneGraph(true); gClientSceneRoot = new SceneRoot; gClientSceneGraph->addObjectToScene(gClientSceneRoot); gServerSceneGraph = new SceneGraph(false); gServerSceneRoot = new SceneRoot; gServerSceneGraph->addObjectToScene(gServerSceneRoot); gDecalManager = new DecalManager; gClientContainer.addObject(gDecalManager); gClientSceneGraph->addObjectToScene(gDecalManager); DetailManager::init(); PathManager::init(); ParticleEngine::init(); BadWordFilter::create(); SimChunk::initChunkMappings(); // run the entry script and return. return runEntryScript(argc, argv); } /// Shutdown the game and delete core objects void shutdownGame() { //exec the script onExit() function Con::executef(1, "onExit"); BadWordFilter::destroy(); ParticleEngine::destroy(); PathManager::destroy(); DetailManager::shutdown(); // Note: tho the SceneGraphs are created after the Manager, delete them after, rather // than before to make sure that all the objects are removed from the graph. Sim::shutdown(); gClientSceneGraph->removeObjectFromScene(gDecalManager); gClientContainer.removeObject(gDecalManager); gClientSceneGraph->removeObjectFromScene(gClientSceneRoot); gServerSceneGraph->removeObjectFromScene(gServerSceneRoot); delete gClientSceneRoot; delete gServerSceneRoot; delete gClientSceneGraph; delete gServerSceneGraph; delete gDecalManager; gClientSceneRoot = NULL; gServerSceneRoot = NULL; gClientSceneGraph = NULL; gServerSceneGraph = NULL; gDecalManager = NULL; TerrainRender::shutdown(); } extern bool gDGLRender; bool gShuttingDown = false; /// Main loop of the game int DemoGame::main(int argc, const char **argv) { // if (argc == 1) { // static const char* argvFake[] = { "dtest.exe", "-jload", "test.jrn" }; // argc = 3; // argv = argvFake; // } // Memory::enableLogging("testMem.log"); // Memory::setBreakAlloc(104717); if(!initLibraries()) return 0; #ifdef IHVBUILD char* pVer = new char[sgVerStringLen + 1]; U32 hi; for (hi = 0; hi < sgVerStringLen; hi++) pVer[hi] = sgVerString[hi] ^ 0xFF; pVer[hi] = '\0'; SHA1Context hashCTX; hashCTX.init(); hashCTX.hashBytes(pVer, sgVerStringLen); hashCTX.finalize(); U8 hash[20]; hashCTX.getHash(hash); for (hi = 0; hi < 20; hi++) if (U8(hash[hi]) != U8(sgHashVer[hi])) return 0; #endif // 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]); if (initGame(argc, argv) == false) { Platform::AlertOK("Error", "Failed to initialize game, shutting down."); shutdownGame(); shutdownLibraries(); gShuttingDown = true; return 0; } #ifdef IHVBUILD char* pPrint = new char[dStrlen(sgVerPrintString) + 1]; for (U32 pi = 0; pi < dStrlen(sgVerPrintString); pi++) pPrint[pi] = sgVerPrintString[pi] ^ 0xff; pPrint[dStrlen(sgVerPrintString)] = '\0'; Con::printf(""); Con::errorf(ConsoleLogEntry::General, pPrint, pVer); delete [] pVer; #endif while(Game->isRunning()) { PROFILE_START(MainLoop); PROFILE_START(JournalMain); Game->journalProcess(); PROFILE_END(); PROFILE_START(NetProcessMain); Net::process(); // read in all events PROFILE_END(); PROFILE_START(PlatformProcessMain); Platform::process(); // keys, etc. PROFILE_END(); PROFILE_START(TelconsoleProcessMain); TelConsole->process(); PROFILE_END(); PROFILE_START(TelDebuggerProcessMain); TelDebugger->process(); PROFILE_END(); PROFILE_START(TimeManagerProcessMain); TimeManager::process(); // guaranteed to produce an event PROFILE_END(); PROFILE_START(GameProcessEvents); Game->processEvents(); // process all non-sim posted events. PROFILE_END(); PROFILE_END(); } shutdownGame(); shutdownLibraries(); gShuttingDown = true; #if 0 // tg: Argh! This OS version check should be part of Platform, not here... // // check os OSVERSIONINFO osInfo; dMemset(&osInfo, 0, sizeof(OSVERSIONINFO)); osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // see if osversioninfoex fails if(!GetVersionEx((OSVERSIONINFO*)&osInfo)) { osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(!GetVersionEx((OSVERSIONINFO*)&osInfo)) return 0; } // terminate the process if win95 only! if((osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && // 95, 98, ME (osInfo.dwMajorVersion == 4) && // 95, 98, ME, NT (osInfo.dwMinorVersion == 0)) // 95 { AssertWarn(0, "Forcing termination of app (Win95)! Upgrade your OS now!"); TerminateProcess(GetCurrentProcess(), 0xffffffff); } #endif return 0; } static bool serverTick = false; static F32 fpsRealStart; static F32 fpsRealLast; //static F32 fpsRealTotal; static F32 fpsReal; static F32 fpsVirtualStart; static F32 fpsVirtualLast; //static F32 fpsVirtualTotal; static F32 fpsVirtual; static F32 fpsFrames; static F32 fpsNext; static bool fpsInit = false; const F32 UPDATE_INTERVAL = 0.25f; //-------------------------------------- /// Resets the FPS variables void fpsReset() { fpsRealStart = (F32)Platform::getRealMilliseconds()/1000.0f; // Real-World Tick Count fpsVirtualStart = (F32)Platform::getVirtualMilliseconds()/1000.0f; // Engine Tick Count (does not vary between frames) fpsNext = fpsRealStart + UPDATE_INTERVAL; // fpsRealTotal= 0.0f; fpsRealLast = 0.0f; fpsReal = 0.0f; // fpsVirtualTotal = 0.0f; fpsVirtualLast = 0.0f; fpsVirtual = 0.0f; fpsFrames = 0; fpsInit = true; } //-------------------------------------- /// Updates the FPS variables void fpsUpdate() { if (!fpsInit) fpsReset(); const float alpha = 0.07f; F32 realSeconds = (F32)Platform::getRealMilliseconds()/1000.0f; F32 virtualSeconds = (F32)Platform::getVirtualMilliseconds()/1000.0f; fpsFrames++; if (fpsFrames > 1) { fpsReal = fpsReal*(1.0-alpha) + (realSeconds-fpsRealLast)*alpha; fpsVirtual = fpsVirtual*(1.0-alpha) + (virtualSeconds-fpsVirtualLast)*alpha; } // fpsRealTotal = fpsFrames/(realSeconds - fpsRealStart); // fpsVirtualTotal = fpsFrames/(virtualSeconds - fpsVirtualStart); fpsRealLast = realSeconds; fpsVirtualLast = virtualSeconds; // update variables every few frames F32 update = fpsRealLast - fpsNext; if (update > 0.5f) { // Con::setVariable("fps::realTotal", avar("%4.1f", fpsRealTotal)); // Con::setVariable("fps::virtualTotal", avar("%4.1f", fpsVirtualTotal)); Con::setVariable("fps::real", avar("%4.1f", 1.0f/fpsReal)); Con::setVariable("fps::virtual", avar("%4.1f", 1.0f/fpsVirtual)); if (update > UPDATE_INTERVAL) fpsNext = fpsRealLast + UPDATE_INTERVAL; else fpsNext += UPDATE_INTERVAL; } } /// Process a mouse movement event, essentially pass to the canvas for handling void DemoGame::processMouseMoveEvent(MouseMoveEvent * mEvent) { if (Canvas) Canvas->processMouseMoveEvent(mEvent); } /// Process an input event, pass to canvas for handling void DemoGame::processInputEvent(InputEvent *event) { PROFILE_START(ProcessInputEvent); if (!ActionMap::handleEventGlobal(event)) { // Other input consumers here... if (!(Canvas && Canvas->processInputEvent(event))) ActionMap::handleEvent(event); } PROFILE_END(); } /// Process a quit event void DemoGame::processQuitEvent() { setRunning(false); } /// Refresh the game window, ask the canvas to set all regions to dirty (need to be updated) void DemoGame::refreshWindow() { if(Canvas) Canvas->resetUpdateRegions(); } /// Process a console event void DemoGame::processConsoleEvent(ConsoleEvent *event) { char *argv[2]; argv[0] = "eval"; argv[1] = event->data; Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, const_cast(argv), false)); } /// Process a time event and update all sub-processes void DemoGame::processTimeEvent(TimeEvent *event) { PROFILE_START(ProcessTimeEvent); U32 elapsedTime = event->elapsedTime; // 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(gTimeAdvance) timeDelta = gTimeAdvance; else timeDelta = (U32) (elapsedTime * gTimeScale); Platform::advanceTime(elapsedTime); bool tickPass; PROFILE_START(ServerProcess); tickPass = serverProcess(timeDelta); PROFILE_END(); PROFILE_START(ServerNetProcess); // only send packets if a tick happened if(tickPass) GNet->processServer(); PROFILE_END(); PROFILE_START(SimAdvanceTime); Sim::advanceTime(timeDelta); PROFILE_END(); PROFILE_START(ClientProcess); tickPass = clientProcess(timeDelta); PROFILE_END(); PROFILE_START(ClientNetProcess); if(tickPass) GNet->processClient(); PROFILE_END(); sgObjectShadowMonitor::sgCleanupUnused(); if(Canvas && gDGLRender) { bool preRenderOnly = false; if(gFrameSkip && gFrameCount % gFrameSkip) preRenderOnly = true; PROFILE_START(RenderFrame); ShapeBase::incRenderFrame(); Canvas->renderFrame(preRenderOnly); PROFILE_END(); gFrameCount++; } GNet->checkTimeouts(); fpsUpdate(); PROFILE_END(); // Update the console time Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000); } /// Re-activate the game from, say, a minimized state void GameReactivate() { if ( !Input::isEnabled() ) Input::enable(); if ( !Input::isActive() ) Input::reactivate(); gDGLRender = true; if ( Canvas ) Canvas->resetUpdateRegions(); } /// De-activate the game in responce to, say, a minimize event void GameDeactivate( bool noRender ) { if ( Input::isActive() ) Input::deactivate(); if ( Input::isEnabled() ) Input::disable(); if ( noRender ) gDGLRender = false; } /// Invalidate all the textures void DemoGame::textureKill() { TextureManager::makeZombie(); } /// Reaquire all textures void DemoGame::textureResurrect() { TextureManager::resurrect(); } /// Process recieved net-packets void DemoGame::processPacketReceiveEvent(PacketReceiveEvent * prEvent) { GNet->processPacketReceiveEvent(prEvent); }