tge/engine/game/main.cc
2017-04-17 06:17:10 -06:00

725 lines
22 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// 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<const char**>(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);
}