1010 lines
41 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// GameEngine.cpp /////////////////////////////////////////////////////////////////////////////////
// Implementation of the Game Engine singleton
// Author: Michael S. Booth, April 2001
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
#include "Common/ActionManager.h"
#include "Common/AudioAffect.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
#include "Common/Radar.h"
#include "Common/PlayerTemplate.h"
#include "Common/Team.h"
#include "Common/PlayerList.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/INI.h"
#include "Common/INIException.h"
#include "Common/MessageStream.h"
#include "Common/ThingFactory.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/LocalFileSystem.h"
#include "Common/CDManager.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/RandomValue.h"
#include "Common/NameKeyGenerator.h"
#include "Common/ModuleFactory.h"
#include "Common/Debug.h"
#include "Common/GameState.h"
#include "Common/GameStateMap.h"
#include "Common/Science.h"
#include "Common/FunctionLexicon.h"
#include "Common/CommandLine.h"
#include "Common/DamageFX.h"
#include "Common/MultiplayerSettings.h"
#include "Common/Recorder.h"
#include "Common/SpecialPower.h"
#include "Common/TerrainTypes.h"
#include "Common/Upgrade.h"
#include "Common/UserPreferences.h"
#include "Common/Xfer.h"
#include "Common/XferCRC.h"
#include "Common/GameLOD.h"
#include "Common/Registry.h"
#include "Common/GameCommon.h" // FOR THE ALLOW_DEBUG_CHEATS_IN_RELEASE #define
#include "GameLogic/Armor.h"
#include "GameLogic/AI.h"
#include "GameLogic/CaveSystem.h"
#include "GameLogic/CrateSystem.h"
#include "GameLogic/Damage.h"
#include "GameLogic/VictoryConditions.h"
#include "GameLogic/ObjectCreationList.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Locomotor.h"
#include "GameLogic/RankInfo.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/SidesList.h"
#include "GameClient/Display.h"
#include "GameClient/FXList.h"
#include "GameClient/GameClient.h"
#include "GameClient/Keyboard.h"
#include "GameClient/Shell.h"
#include "GameClient/GameText.h"
#include "GameClient/ParticleSys.h"
#include "GameClient/Water.h"
#include "GameClient/TerrainRoads.h"
#include "GameClient/MetaEvent.h"
#include "GameClient/MapUtil.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GlobalLanguage.h"
#include "GameClient/Drawable.h"
#include "GameClient/GUICallbacks.h"
#include "GameNetwork/NetworkInterface.h"
#include "GameNetwork/WOLBrowser/WebBrowser.h"
#include "GameNetwork/LANAPI.h"
#include "GameNetwork/GameSpy/GameResultsThread.h"
#include "Common/Version.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
#ifdef DEBUG_CRC
class DeepCRCSanityCheck : public SubsystemInterface
{
public:
DeepCRCSanityCheck() {}
virtual ~DeepCRCSanityCheck() {}
virtual void init(void) {}
virtual void reset(void);
virtual void update(void) {}
protected:
};
DeepCRCSanityCheck *TheDeepCRCSanityCheck = NULL;
void DeepCRCSanityCheck::reset(void)
{
static Int timesThrough = 0;
static UnsignedInt lastCRC = 0;
AsciiString fname;
fname.format("%sCRCAfter%dMaps.dat", TheGlobalData->getPath_UserData().str(), timesThrough);
UnsignedInt thisCRC = TheGameLogic->getCRC( CRC_RECALC, fname );
DEBUG_LOG(("DeepCRCSanityCheck: CRC is %X\n", thisCRC));
DEBUG_ASSERTCRASH(timesThrough == 0 || thisCRC == lastCRC,
("CRC after reset did not match beginning CRC!\nNetwork games won't work after this.\nOld: 0x%8.8X, New: 0x%8.8X",
lastCRC, thisCRC));
lastCRC = thisCRC;
timesThrough++;
}
#endif // DEBUG_CRC
//-------------------------------------------------------------------------------------------------
/// The GameEngine singleton instance
GameEngine *TheGameEngine = NULL;
//-------------------------------------------------------------------------------------------------
SubsystemInterfaceList* TheSubsystemList = NULL;
//-------------------------------------------------------------------------------------------------
template<class SUBSYSTEM>
void initSubsystem(SUBSYSTEM*& sysref, AsciiString name, SUBSYSTEM* sys, Xfer *pXfer, const char* path1 = NULL,
const char* path2 = NULL, const char* dirpath = NULL)
{
sysref = sys;
TheSubsystemList->initSubsystem(sys, path1, path2, dirpath, pXfer, name);
}
//-------------------------------------------------------------------------------------------------
extern HINSTANCE ApplicationHInstance; ///< our application instance
extern CComModule _Module;
//-------------------------------------------------------------------------------------------------
static void updateTGAtoDDS();
Int GameEngine::getFramesPerSecondLimit( void )
{
return m_maxFPS;
}
//-------------------------------------------------------------------------------------------------
GameEngine::GameEngine( void )
{
// Set the time slice size to 1 ms.
timeBeginPeriod(1);
// initialize to non garbage values
m_maxFPS = 0;
m_quitting = FALSE;
m_isActive = FALSE;
_Module.Init(NULL, ApplicationHInstance);
}
//-------------------------------------------------------------------------------------------------
GameEngine::~GameEngine()
{
//extern std::vector<std::string> preloadTextureNamesGlobalHack;
//preloadTextureNamesGlobalHack.clear();
delete TheMapCache;
TheMapCache = NULL;
// delete TheShell;
// TheShell = NULL;
TheGameResultsQueue->endThreads();
TheSubsystemList->shutdownAll();
delete TheSubsystemList;
TheSubsystemList = NULL;
delete TheNetwork;
TheNetwork = NULL;
delete TheCommandList;
TheCommandList = NULL;
delete TheNameKeyGenerator;
TheNameKeyGenerator = NULL;
delete TheFileSystem;
TheFileSystem = NULL;
if (TheGameLODManager)
delete TheGameLODManager;
Drawable::killStaticImages();
_Module.Term();
#ifdef PERF_TIMERS
PerfGather::termPerfDump();
#endif
// Restore the previous time slice for Windows.
timeEndPeriod(1);
}
void GameEngine::setFramesPerSecondLimit( Int fps )
{
DEBUG_LOG(("GameEngine::setFramesPerSecondLimit() - setting max fps to %d (TheGlobalData->m_useFpsLimit == %d)\n", fps, TheGlobalData->m_useFpsLimit));
m_maxFPS = fps;
}
/** -----------------------------------------------------------------------------------------------
* Initialize the game engine by initializing the GameLogic and GameClient.
*/
void GameEngine::init( void ) {} /// @todo: I changed this to take argc & argv so we can parse those after the GDF is loaded. We need to rethink this immediately as it is a nasty hack
void GameEngine::init( int argc, char *argv[] )
{
try {
//create an INI object to use for loading stuff
INI ini;
#ifdef DEBUG_LOGGING
if (TheVersion)
{
DEBUG_LOG(("================================================================================\n"));
#if defined _DEBUG
const char *buildType = "Debug";
#elif defined _INTERNAL
const char *buildType = "Internal";
#else
const char *buildType = "Release";
#endif
DEBUG_LOG(("Generals version %s (%s)\n", TheVersion->getAsciiVersion().str(), buildType));
DEBUG_LOG(("Build date: %s\n", TheVersion->getAsciiBuildTime().str()));
DEBUG_LOG(("Build location: %s\n", TheVersion->getAsciiBuildLocation().str()));
DEBUG_LOG(("Built by: %s\n", TheVersion->getAsciiBuildUser().str()));
DEBUG_LOG(("================================================================================\n"));
}
#endif
#if defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
DEBUG_LOG(("Calculating CPU frequency for performance timers.\n"));
InitPrecisionTimer();
#endif
#ifdef PERF_TIMERS
PerfGather::initPerfDump("AAAPerfStats", PerfGather::PERF_NETTIME);
#endif
#ifdef DUMP_PERF_STATS////////////////////////////////////////////////////////////
__int64 startTime64;//////////////////////////////////////////////////////////////
__int64 endTime64,freq64;///////////////////////////////////////////////////////////
GetPrecisionTimerTicksPerSec(&freq64);///////////////////////////////////////////////
GetPrecisionTimer(&startTime64);////////////////////////////////////////////////////
char Buf[256];//////////////////////////////////////////////////////////////////////
#endif//////////////////////////////////////////////////////////////////////////////
m_maxFPS = DEFAULT_MAX_FPS;
TheSubsystemList = MSGNEW("GameEngineSubsystem") SubsystemInterfaceList;
TheSubsystemList->addSubsystem(this);
// initialize the random number system
InitRandom();
// Create the low-level file system interface
TheFileSystem = createFileSystem();
//Kris: Patch 1.01 - November 17, 2003
//I was unable to resolve the RTPatch method of deleting a shipped file. English, Chinese, and Korean
//SKU's shipped with two INIZH.big files. One properly in the Run directory and the other in Run\INI\Data.
//We need to toast the latter in order for the game to patch properly.
DeleteFile( "Data\\INI\\INIZH.big" );
// not part of the subsystem list, because it should normally never be reset!
TheNameKeyGenerator = MSGNEW("GameEngineSubsystem") NameKeyGenerator;
TheNameKeyGenerator->init();
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheNameKeyGenerator = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
// not part of the subsystem list, because it should normally never be reset!
TheCommandList = MSGNEW("GameEngineSubsystem") CommandList;
TheCommandList->init();
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheCommandList = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
XferCRC xferCRC;
xferCRC.open("lightCRC");
initSubsystem(TheLocalFileSystem, "TheLocalFileSystem", createLocalFileSystem(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheLocalFileSystem = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheArchiveFileSystem, "TheArchiveFileSystem", createArchiveFileSystem(), NULL); // this MUST come after TheLocalFileSystem creation
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheArchiveFileSystem = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheWritableGlobalData, "TheWritableGlobalData", MSGNEW("GameEngineSubsystem") GlobalData(), &xferCRC, "Data\\INI\\Default\\GameData.ini", "Data\\INI\\GameData.ini");
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheWritableGlobalData = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
#if defined(_DEBUG) || defined(_INTERNAL)
// If we're in Debug or Internal, load the Debug info as well.
ini.load( AsciiString( "Data\\INI\\GameDataDebug.ini" ), INI_LOAD_OVERWRITE, NULL );
#endif
// special-case: parse command-line parameters after loading global data
parseCommandLine(argc, argv);
// doesn't require resets so just create a single instance here.
TheGameLODManager = MSGNEW("GameEngineSubsystem") GameLODManager;
TheGameLODManager->init();
// after parsing the command line, we may want to perform dds stuff. Do that here.
if (TheGlobalData->m_shouldUpdateTGAToDDS) {
// update any out of date targas here.
updateTGAtoDDS();
}
// read the water settings from INI (must do prior to initing GameClient, apparently)
ini.load( AsciiString( "Data\\INI\\Default\\Water.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
ini.load( AsciiString( "Data\\INI\\Water.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
ini.load( AsciiString( "Data\\INI\\Default\\Weather.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
ini.load( AsciiString( "Data\\INI\\Weather.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After water INI's = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_CRC
initSubsystem(TheDeepCRCSanityCheck, "TheDeepCRCSanityCheck", MSGNEW("GameEngineSubystem") DeepCRCSanityCheck, NULL, NULL, NULL, NULL);
#endif // DEBUG_CRC
initSubsystem(TheGameText, "TheGameText", CreateGameTextInterface(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheGameText = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheScienceStore,"TheScienceStore", MSGNEW("GameEngineSubsystem") ScienceStore(), &xferCRC, "Data\\INI\\Default\\Science.ini", "Data\\INI\\Science.ini");
initSubsystem(TheMultiplayerSettings,"TheMultiplayerSettings", MSGNEW("GameEngineSubsystem") MultiplayerSettings(), &xferCRC, "Data\\INI\\Default\\Multiplayer.ini", "Data\\INI\\Multiplayer.ini");
initSubsystem(TheTerrainTypes,"TheTerrainTypes", MSGNEW("GameEngineSubsystem") TerrainTypeCollection(), &xferCRC, "Data\\INI\\Default\\Terrain.ini", "Data\\INI\\Terrain.ini");
initSubsystem(TheTerrainRoads,"TheTerrainRoads", MSGNEW("GameEngineSubsystem") TerrainRoadCollection(), &xferCRC, "Data\\INI\\Default\\Roads.ini", "Data\\INI\\Roads.ini");
initSubsystem(TheGlobalLanguageData,"TheGlobalLanguageData",MSGNEW("GameEngineSubsystem") GlobalLanguage, NULL); // must be before the game text
initSubsystem(TheCDManager,"TheCDManager", CreateCDManager(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheCDManager = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL);
if (!TheAudio->isMusicAlreadyLoaded())
setQuitting(TRUE);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheAudio = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheFunctionLexicon,"TheFunctionLexicon", createFunctionLexicon(), NULL);
initSubsystem(TheModuleFactory,"TheModuleFactory", createModuleFactory(), NULL);
initSubsystem(TheMessageStream,"TheMessageStream", createMessageStream(), NULL);
initSubsystem(TheSidesList,"TheSidesList", MSGNEW("GameEngineSubsystem") SidesList(), NULL);
initSubsystem(TheCaveSystem,"TheCaveSystem", MSGNEW("GameEngineSubsystem") CaveSystem(), NULL);
initSubsystem(TheRankInfoStore,"TheRankInfoStore", MSGNEW("GameEngineSubsystem") RankInfoStore(), &xferCRC, NULL, "Data\\INI\\Rank.ini");
initSubsystem(ThePlayerTemplateStore,"ThePlayerTemplateStore", MSGNEW("GameEngineSubsystem") PlayerTemplateStore(), &xferCRC, "Data\\INI\\Default\\PlayerTemplate.ini", "Data\\INI\\PlayerTemplate.ini");
initSubsystem(TheParticleSystemManager,"TheParticleSystemManager", createParticleSystemManager(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheParticleSystemManager = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheFXListStore,"TheFXListStore", MSGNEW("GameEngineSubsystem") FXListStore(), &xferCRC, "Data\\INI\\Default\\FXList.ini", "Data\\INI\\FXList.ini");
initSubsystem(TheWeaponStore,"TheWeaponStore", MSGNEW("GameEngineSubsystem") WeaponStore(), &xferCRC, NULL, "Data\\INI\\Weapon.ini");
initSubsystem(TheObjectCreationListStore,"TheObjectCreationListStore", MSGNEW("GameEngineSubsystem") ObjectCreationListStore(), &xferCRC, "Data\\INI\\Default\\ObjectCreationList.ini", "Data\\INI\\ObjectCreationList.ini");
initSubsystem(TheLocomotorStore,"TheLocomotorStore", MSGNEW("GameEngineSubsystem") LocomotorStore(), &xferCRC, NULL, "Data\\INI\\Locomotor.ini");
initSubsystem(TheSpecialPowerStore,"TheSpecialPowerStore", MSGNEW("GameEngineSubsystem") SpecialPowerStore(), &xferCRC, "Data\\INI\\Default\\SpecialPower.ini", "Data\\INI\\SpecialPower.ini");
initSubsystem(TheDamageFXStore,"TheDamageFXStore", MSGNEW("GameEngineSubsystem") DamageFXStore(), &xferCRC, NULL, "Data\\INI\\DamageFX.ini");
initSubsystem(TheArmorStore,"TheArmorStore", MSGNEW("GameEngineSubsystem") ArmorStore(), &xferCRC, NULL, "Data\\INI\\Armor.ini");
initSubsystem(TheBuildAssistant,"TheBuildAssistant", MSGNEW("GameEngineSubsystem") BuildAssistant, NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheBuildAssistant = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheThingFactory,"TheThingFactory", createThingFactory(), &xferCRC, "Data\\INI\\Default\\Object.ini", NULL, "Data\\INI\\Object");
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheThingFactory = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheUpgradeCenter,"TheUpgradeCenter", MSGNEW("GameEngineSubsystem") UpgradeCenter, &xferCRC, "Data\\INI\\Default\\Upgrade.ini", "Data\\INI\\Upgrade.ini");
initSubsystem(TheGameClient,"TheGameClient", createGameClient(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheGameClient = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
initSubsystem(TheAI,"TheAI", MSGNEW("GameEngineSubsystem") AI(), &xferCRC, "Data\\INI\\Default\\AIData.ini", "Data\\INI\\AIData.ini");
initSubsystem(TheGameLogic,"TheGameLogic", createGameLogic(), NULL);
initSubsystem(TheTeamFactory,"TheTeamFactory", MSGNEW("GameEngineSubsystem") TeamFactory(), NULL);
initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini");
initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL);
initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL);
initSubsystem(TheRadar,"TheRadar", createRadar(), NULL);
initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheVictoryConditions = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
AsciiString fname;
fname.format("Data\\%s\\CommandMap.ini", GetRegistryLanguage().str());
initSubsystem(TheMetaMap,"TheMetaMap", MSGNEW("GameEngineSubsystem") MetaMap(), NULL, fname.str(), "Data\\INI\\CommandMap.ini");
#if defined(_DEBUG) || defined(_INTERNAL)
ini.load("Data\\INI\\CommandMapDebug.ini", INI_LOAD_MULTIFILE, NULL);
#endif
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
ini.load("Data\\INI\\CommandMapDemo.ini", INI_LOAD_MULTIFILE, NULL);
#endif
initSubsystem(TheActionManager,"TheActionManager", MSGNEW("GameEngineSubsystem") ActionManager(), NULL);
//initSubsystem((CComObject<WebBrowser> *)TheWebBrowser,"(CComObject<WebBrowser> *)TheWebBrowser", (CComObject<WebBrowser> *)createWebBrowser(), NULL);
initSubsystem(TheGameStateMap,"TheGameStateMap", MSGNEW("GameEngineSubsystem") GameStateMap, NULL, NULL, NULL );
initSubsystem(TheGameState,"TheGameState", MSGNEW("GameEngineSubsystem") GameState, NULL, NULL, NULL );
// Create the interface for sending game results
initSubsystem(TheGameResultsQueue,"TheGameResultsQueue", GameResultsInterface::createNewGameResultsInterface(), NULL, NULL, NULL, NULL);
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheGameResultsQueue = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
xferCRC.close();
TheWritableGlobalData->m_iniCRC = xferCRC.getCRC();
DEBUG_LOG(("INI CRC is 0x%8.8X\n", TheGlobalData->m_iniCRC));
TheSubsystemList->postProcessLoadAll();
setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_musicOn, AudioAffect_Music);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_soundsOn, AudioAffect_Sound);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_sounds3DOn, AudioAffect_Sound3D);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_speechOn, AudioAffect_Speech);
// We're not in a network game yet, so set the network singleton to NULL.
TheNetwork = NULL;
//Create a default ini file for options if it doesn't already exist.
//OptionPreferences prefs( TRUE );
// If we turn m_quitting to FALSE here, then we throw away any requests to quit that
// took place during loading. :-\ - jkmcd
// If this really needs to take place, please make sure that pressing cancel on the audio
// load music dialog will still cause the game to quit.
// m_quitting = FALSE;
// for fingerprinting, we need to ensure the presence of these files
#if !defined(_INTERNAL) && !defined(_DEBUG)
AsciiString dirName;
dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsbzh.sec");
if (dirName.compareNoCase("genseczh.big") != 0)
{
DEBUG_LOG(("generalsbzh.sec was not found in genseczh.big - it was in '%s'\n", dirName.str()));
m_quitting = TRUE;
}
dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsazh.sec");
const char *noPath = dirName.reverseFind('\\');
if (noPath) {
dirName = noPath + 1;
}
if (dirName.compareNoCase("musiczh.big") != 0)
{
DEBUG_LOG(("generalsazh.sec was not found in musiczh.big - it was in '%s'\n", dirName.str()));
m_quitting = TRUE;
}
#endif
// initialize the MapCache
TheMapCache = MSGNEW("GameEngineSubsystem") MapCache;
TheMapCache->updateCache();
#ifdef DUMP_PERF_STATS///////////////////////////////////////////////////////////////////////////
GetPrecisionTimer(&endTime64);//////////////////////////////////////////////////////////////////
sprintf(Buf,"----------------------------------------------------------------------------After TheMapCache->updateCache = %f seconds \n",((double)(endTime64-startTime64)/(double)(freq64)));
startTime64 = endTime64;//Reset the clock ////////////////////////////////////////////////////////
DEBUG_LOG(("%s", Buf));////////////////////////////////////////////////////////////////////////////
#endif/////////////////////////////////////////////////////////////////////////////////////////////
if (TheGlobalData->m_buildMapCache)
{
// just quit, since the map cache has already updated
//populateMapListbox(NULL, true, true);
m_quitting = TRUE;
}
// load the initial shell screen
//TheShell->push( AsciiString("Menus/MainMenu.wnd") );
// This allows us to run a map/reply from the command line
if (TheGlobalData->m_initialFile.isEmpty() == FALSE)
{
AsciiString fname = TheGlobalData->m_initialFile;
fname.toLower();
if (fname.endsWithNoCase(".map"))
{
TheWritableGlobalData->m_shellMapOn = FALSE;
TheWritableGlobalData->m_playIntro = FALSE;
TheWritableGlobalData->m_pendingFile = TheGlobalData->m_initialFile;
// shutdown the top, but do not pop it off the stack
// TheShell->hideShell();
// send a message to the logic for a new game
GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
msg->appendIntegerArgument(GAME_SINGLE_PLAYER);
msg->appendIntegerArgument(DIFFICULTY_NORMAL);
msg->appendIntegerArgument(0);
InitRandom(0);
}
else if (fname.endsWithNoCase(".rep"))
{
TheRecorder->playbackFile(fname);
}
}
//
if (TheMapCache && TheGlobalData->m_shellMapOn)
{
AsciiString lowerName = TheGlobalData->m_shellMapName;
lowerName.toLower();
MapCache::const_iterator it = TheMapCache->find(lowerName);
if (it == TheMapCache->end())
{
TheWritableGlobalData->m_shellMapOn = FALSE;
}
}
if(!TheGlobalData->m_playIntro)
TheWritableGlobalData->m_afterIntro = TRUE;
//initDisabledMasks();
}
catch (ErrorCode ec)
{
if (ec == ERROR_INVALID_D3D)
{
RELEASE_CRASHLOCALIZED("ERROR:D3DFailurePrompt", "ERROR:D3DFailureMessage");
}
}
catch (INIException e)
{
if (e.mFailureMessage)
RELEASE_CRASH((e.mFailureMessage));
else
RELEASE_CRASH(("Uncaught Exception during initialization."));
}
catch (...)
{
RELEASE_CRASH(("Uncaught Exception during initialization."));
}
if(!TheGlobalData->m_playIntro)
TheWritableGlobalData->m_afterIntro = TRUE;
initKindOfMasks();
initDisabledMasks();
initDamageTypeFlags();
TheSubsystemList->resetAll();
HideControlBar();
} // end init
/** -----------------------------------------------------------------------------------------------
* Reset all necessary parts of the game engine to be ready to accept new game data
*/
void GameEngine::reset( void )
{
WindowLayout *background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd");
DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd"));
background->hide(FALSE);
background->bringForward();
background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE);
Bool deleteNetwork = false;
if (TheGameLogic->isInMultiplayerGame())
deleteNetwork = true;
TheSubsystemList->resetAll();
if (deleteNetwork)
{
DEBUG_ASSERTCRASH(TheNetwork, ("Deleting NULL TheNetwork!"));
if (TheNetwork)
delete TheNetwork;
TheNetwork = NULL;
}
if(background)
{
background->destroyWindows();
background->deleteInstance();
background = NULL;
}
}
/// -----------------------------------------------------------------------------------------------
DECLARE_PERF_TIMER(GameEngine_update)
/** -----------------------------------------------------------------------------------------------
* Update the game engine by updating the GameClient and GameLogic singletons.
* @todo Allow the client to run as fast as possible, but limit the execution
* of TheNetwork and TheGameLogic to a fixed framerate.
*/
void GameEngine::update( void )
{
USE_PERF_TIMER(GameEngine_update)
{
{
// VERIFY CRC needs to be in this code block. Please to not pull TheGameLogic->update() inside this block.
VERIFY_CRC
TheRadar->UPDATE();
/// @todo Move audio init, update, etc, into GameClient update
TheAudio->UPDATE();
TheGameClient->UPDATE();
TheMessageStream->propagateMessages();
if (TheNetwork != NULL)
{
TheNetwork->UPDATE();
}
TheCDManager->UPDATE();
}
if ((TheNetwork == NULL && !TheGameLogic->isGamePaused()) || (TheNetwork && TheNetwork->isFrameDataReady()))
{
TheGameLogic->UPDATE();
}
} // end perfGather
}
// Horrible reference, but we really, really need to know if we are windowed.
extern bool DX8Wrapper_IsWindowed;
extern HWND ApplicationHWnd;
/** -----------------------------------------------------------------------------------------------
* The "main loop" of the game engine. It will not return until the game exits.
*/
void GameEngine::execute( void )
{
DWORD prevTime = timeGetTime();
#if defined(_DEBUG) || defined(_INTERNAL)
DWORD startTime = timeGetTime() / 1000;
#endif
// pretty basic for now
while( !m_quitting )
{
//if (TheGlobalData->m_vTune)
{
#ifdef PERF_TIMERS
PerfGather::resetAll();
#endif
}
{
#if defined(_DEBUG) || defined(_INTERNAL)
{
// enter only if in benchmark mode
if (TheGlobalData->m_benchmarkTimer > 0)
{
DWORD currentTime = timeGetTime() / 1000;
if (TheGlobalData->m_benchmarkTimer < currentTime - startTime)
{
if (TheGameLogic->isInGame())
{
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
{
TheRecorder->stopRecording();
}
TheGameLogic->clearGameData();
}
TheGameEngine->setQuitting(TRUE);
}
}
}
#endif
{
try
{
// compute a frame
update();
}
catch (INIException e)
{
// Release CRASH doesn't return, so don't worry about executing additional code.
if (e.mFailureMessage)
RELEASE_CRASH((e.mFailureMessage));
else
RELEASE_CRASH(("Uncaught Exception in GameEngine::update"));
}
catch (...)
{
// try to save info off
try
{
if (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_RECORD && TheRecorder->isMultiplayer())
TheRecorder->cleanUpReplayFile();
}
catch (...)
{
}
RELEASE_CRASH(("Uncaught Exception in GameEngine::update"));
} // catch
} // perf
{
if (TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast())
{
// I'm disabling this in internal because many people need alt-tab capability. If you happen to be
// doing performance tuning, please just change this on your local system. -MDC
#if defined(_DEBUG) || defined(_INTERNAL)
::Sleep(1); // give everyone else a tiny time slice.
#endif
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
if ( ! TheGlobalData->m_TiVOFastMode )
#else //always allow this cheatkey if we're in a replaygame.
if ( ! (TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame()))
#endif
{
// limit the framerate
DWORD now = timeGetTime();
DWORD limit = (1000.0f/m_maxFPS)-1;
while (TheGlobalData->m_useFpsLimit && (now - prevTime) < limit)
{
::Sleep(0);
now = timeGetTime();
}
//Int slept = now - prevTime;
//DEBUG_LOG(("delayed %d\n",slept));
prevTime = now;
}
}
}
} // perfgather for execute_loop
#ifdef PERF_TIMERS
if (!m_quitting && TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() && !TheGameLogic->isGamePaused())
{
PerfGather::dumpAll(TheGameLogic->getFrame());
PerfGather::displayGraph(TheGameLogic->getFrame());
PerfGather::resetAll();
}
#endif
}
}
/** -----------------------------------------------------------------------------------------------
* Factory for the message stream
*/
MessageStream *GameEngine::createMessageStream( void )
{
// if you change this update the tools that use the engine systems
// like GUIEdit, it creates a message stream to run in "test" mode
return MSGNEW("GameEngineSubsystem") MessageStream;
}
//-------------------------------------------------------------------------------------------------
FileSystem *GameEngine::createFileSystem( void )
{
return MSGNEW("GameEngineSubsystem") FileSystem;
}
//-------------------------------------------------------------------------------------------------
Bool GameEngine::isMultiplayerSession( void )
{
return TheRecorder->isMultiplayer();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
#define CONVERT_EXEC1 "..\\Build\\nvdxt -list buildDDS.txt -dxt5 -full -outdir Art\\Textures > buildDDS.out"
void updateTGAtoDDS()
{
// Here's the scoop. We're going to traverse through all of the files in the Art\Textures folder
// and determine if there are any .tga files that are newer than associated .dds files. If there
// are, then we will re-run the compression tool on them.
File *fp = TheLocalFileSystem->openFile("buildDDS.txt", File::WRITE | File::CREATE | File::TRUNCATE | File::TEXT);
if (!fp) {
return;
}
FilenameList files;
TheLocalFileSystem->getFileListInDirectory("Art\\Textures\\", "", "*.tga", files, TRUE);
FilenameList::iterator it;
for (it = files.begin(); it != files.end(); ++it) {
AsciiString filenameTGA = *it;
AsciiString filenameDDS = *it;
FileInfo infoTGA;
TheLocalFileSystem->getFileInfo(filenameTGA, &infoTGA);
// skip the water textures, since they need to be NOT compressed
filenameTGA.toLower();
if (strstr(filenameTGA.str(), "caust"))
{
continue;
}
// and the recolored stuff.
if (strstr(filenameTGA.str(), "zhca"))
{
continue;
}
// replace tga with dds
filenameDDS.removeLastChar(); // a
filenameDDS.removeLastChar(); // g
filenameDDS.removeLastChar(); // t
filenameDDS.concat("dds");
Bool needsToBeUpdated = FALSE;
FileInfo infoDDS;
if (TheFileSystem->doesFileExist(filenameDDS.str())) {
TheFileSystem->getFileInfo(filenameDDS, &infoDDS);
if (infoTGA.timestampHigh > infoDDS.timestampHigh ||
(infoTGA.timestampHigh == infoDDS.timestampHigh &&
infoTGA.timestampLow > infoDDS.timestampLow)) {
needsToBeUpdated = TRUE;
}
} else {
needsToBeUpdated = TRUE;
}
if (!needsToBeUpdated) {
continue;
}
filenameTGA.concat("\n");
fp->write(filenameTGA.str(), filenameTGA.getLength());
}
fp->close();
system(CONVERT_EXEC1);
}
//-------------------------------------------------------------------------------------------------
// System things
// If we're using the Wide character version of MessageBox, then there's no additional
// processing necessary. Please note that this is a sleazy way to get this information,
// but pending a better one, this'll have to do.
extern const Bool TheSystemIsUnicode = (((void*) (::MessageBox)) == ((void*) (::MessageBoxW)));