| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 |
- /*
- ** Command & Conquer Generals(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 "GameLogic/Armor.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/CaveSystem.h"
- #include "GameLogic/CrateSystem.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 "GameNetwork/GameSpy/PeerDefs.h"
- #include "GameNetwork/GameSpy/PersistentStorageThread.h"
- #include "Common/Player.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;
- 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;
- if (TheVersion)
- {
- DEBUG_LOG(("================================================================================\n"));
- #ifdef DEBUG_LOGGING
- #if defined _DEBUG
- const char *buildType = "Debug";
- #elif defined _INTERNAL
- const char *buildType = "Internal";
- #else
- // const char *buildType = "Release";
- #endif
- #endif // DEBUG_LOGGING
- 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"));
- }
-
- 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();
- // not part of the subsystem list, because it should normally never be reset!
- TheNameKeyGenerator = MSGNEW("GameEngineSubsystem") NameKeyGenerator;
- TheNameKeyGenerator->init();
- // not part of the subsystem list, because it should normally never be reset!
- TheCommandList = MSGNEW("GameEngineSubsystem") CommandList;
- TheCommandList->init();
- XferCRC xferCRC;
- xferCRC.open("lightCRC");
- initSubsystem(TheLocalFileSystem, "TheLocalFileSystem", createLocalFileSystem(), NULL);
- initSubsystem(TheArchiveFileSystem, "TheArchiveFileSystem", createArchiveFileSystem(), NULL); // this MUST come after TheLocalFileSystem creation
- initSubsystem(TheWritableGlobalData, "TheWritableGlobalData", MSGNEW("GameEngineSubsystem") GlobalData(), &xferCRC, "Data\\INI\\Default\\GameData.ini", "Data\\INI\\GameData.ini");
- #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();
- }
- #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
- // 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 );
- #ifdef DEBUG_CRC
- initSubsystem(TheDeepCRCSanityCheck, "TheDeepCRCSanityCheck", MSGNEW("GameEngineSubystem") DeepCRCSanityCheck, NULL, NULL, NULL, NULL);
- #endif // DEBUG_CRC
- initSubsystem(TheGameText, "TheGameText", CreateGameTextInterface(), NULL);
- 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);
- initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL);
- if (!TheAudio->isMusicAlreadyLoaded())
- setQuitting(TRUE);
- 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);
- 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);
- initSubsystem(TheThingFactory,"TheThingFactory", createThingFactory(), &xferCRC, "Data\\INI\\Default\\Object.ini", NULL, "Data\\INI\\Object");
- initSubsystem(TheUpgradeCenter,"TheUpgradeCenter", MSGNEW("GameEngineSubsystem") UpgradeCenter, &xferCRC, "Data\\INI\\Default\\Upgrade.ini", "Data\\INI\\Upgrade.ini");
- initSubsystem(TheGameClient,"TheGameClient", createGameClient(), NULL);
- 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);
- 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
- 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);
- 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
- AsciiString dirName;
- dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsb.sec");
- if (dirName.compareNoCase("gensec.big") != 0)
- {
- DEBUG_LOG(("generalsb.sec was not found in gensec.big - it was in '%s'\n", dirName.str()));
- m_quitting = TRUE;
- }
-
- dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsa.sec");
- const char *noPath = dirName.reverseFind('\\');
- if (noPath) {
- dirName = noPath + 1;
- }
- if (dirName.compareNoCase("music.big") != 0)
- {
- DEBUG_LOG(("generalsa.sec was not found in music.big - it was in '%s'\n", dirName.str()));
- m_quitting = TRUE;
- }
- // initialize the MapCache
- TheMapCache = MSGNEW("GameEngineSubsystem") MapCache;
- TheMapCache->updateCache();
- 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") );
-
- #if !defined(_PLAYTEST)
- // 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);
- }
- }
- #endif
- //
- 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;
- initDisabledMasks();
- 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
- // 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();
- }
- /** MW - 6-10-03: I added this function in order to verify that users who quit the
- application by not using the menus (Alt-F4, etc.) are not doing it in the
- middle of an internet game. Quitting in this way is considered cheating so
- we will log the results to a file and transmit the updated stats on the next
- login to gamespy. I copied most of this code from the normal disconnection
- logging code that is usually called from another thread. I'm doing it here
- because there's no way to guarantee the other thread will execute before we
- exit the app.
- */
- void GameEngine::checkAbnormalQuitting(void)
- {
- if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame())
- { //Should not be quitting at this time, record it as a cheat.
- Int localID = TheGameSpyInfo->getLocalProfileID();
- PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(localID);
-
- Player *player=ThePlayerList->getLocalPlayer();
- Int ptIdx;
- const PlayerTemplate *myTemplate = player->getPlayerTemplate();
- DEBUG_LOG(("myTemplate = %X(%s)\n", myTemplate, myTemplate->getName().str()));
- for (ptIdx = 0; ptIdx < ThePlayerTemplateStore->getPlayerTemplateCount(); ++ptIdx)
- {
- const PlayerTemplate *nthTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(ptIdx);
- DEBUG_LOG(("nthTemplate = %X(%s)\n", nthTemplate, nthTemplate->getName().str()));
- if (nthTemplate == myTemplate)
- {
- break;
- }
- }
- PSRequest req;
- req.requestType = PSRequest::PSREQUEST_UPDATEPLAYERSTATS;
- req.email = TheGameSpyInfo->getLocalEmail().str();
- req.nick = TheGameSpyInfo->getLocalBaseName().str();
- req.password = TheGameSpyInfo->getLocalPassword().str();
- req.player = stats;
- req.addDesync = FALSE;
- req.addDiscon = TRUE;
- req.lastHouse = ptIdx;
- UserPreferences pref;
- AsciiString userPrefFilename;
- userPrefFilename.format("GeneralsOnline\\MiscPref%d.ini", stats.id);
- DEBUG_LOG(("using the file %s\n", userPrefFilename.str()));
- pref.load(userPrefFilename);
- Int addedInDesyncs2 = pref.getInt("0", 0);
- DEBUG_LOG(("addedInDesyncs2 = %d\n", addedInDesyncs2));
- if (addedInDesyncs2 < 0)
- addedInDesyncs2 = 10;
- Int addedInDesyncs3 = pref.getInt("1", 0);
- DEBUG_LOG(("addedInDesyncs3 = %d\n", addedInDesyncs3));
- if (addedInDesyncs3 < 0)
- addedInDesyncs3 = 10;
- Int addedInDesyncs4 = pref.getInt("2", 0);
- DEBUG_LOG(("addedInDesyncs4 = %d\n", addedInDesyncs4));
- if (addedInDesyncs4 < 0)
- addedInDesyncs4 = 10;
- Int addedInDiscons2 = pref.getInt("3", 0);
- DEBUG_LOG(("addedInDiscons2 = %d\n", addedInDiscons2));
- if (addedInDiscons2 < 0)
- addedInDiscons2 = 10;
- Int addedInDiscons3 = pref.getInt("4", 0);
- DEBUG_LOG(("addedInDiscons3 = %d\n", addedInDiscons3));
- if (addedInDiscons3 < 0)
- addedInDiscons3 = 10;
- Int addedInDiscons4 = pref.getInt("5", 0);
- DEBUG_LOG(("addedInDiscons4 = %d\n", addedInDiscons4));
- if (addedInDiscons4 < 0)
- addedInDiscons4 = 10;
- DEBUG_LOG(("req.addDesync=%d, req.addDiscon=%d, addedInDesync=%d,%d,%d, addedInDiscon=%d,%d,%d\n",
- req.addDesync, req.addDiscon, addedInDesyncs2, addedInDesyncs3, addedInDesyncs4,
- addedInDiscons2, addedInDiscons3, addedInDiscons4));
- if (req.addDesync || req.addDiscon)
- {
- AsciiString val;
- if (req.lastHouse == 2)
- {
- val.format("%d", addedInDesyncs2 + req.addDesync);
- pref["0"] = val;
- val.format("%d", addedInDiscons2 + req.addDiscon);
- pref["3"] = val;
- DEBUG_LOG(("house 2 req.addDesync || req.addDiscon: %d %d\n",
- addedInDesyncs2 + req.addDesync, addedInDiscons2 + req.addDiscon));
- }
- else if (req.lastHouse == 3)
- {
- val.format("%d", addedInDesyncs3 + req.addDesync);
- pref["1"] = val;
- val.format("%d", addedInDiscons3 + req.addDiscon);
- pref["4"] = val;
- DEBUG_LOG(("house 3 req.addDesync || req.addDiscon: %d %d\n",
- addedInDesyncs3 + req.addDesync, addedInDiscons3 + req.addDiscon));
- }
- else
- {
- val.format("%d", addedInDesyncs4 + req.addDesync);
- pref["2"] = val;
- val.format("%d", addedInDiscons4 + req.addDiscon);
- pref["5"] = val;
- DEBUG_LOG(("house 4 req.addDesync || req.addDiscon: %d %d\n",
- addedInDesyncs4 + req.addDesync, addedInDiscons4 + req.addDiscon));
- }
- pref.write();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- #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)));
|