/* =============== idCommonLocal::LoadGame =============== */ bool idCommonLocal::LoadGame( const char * saveName ) { if ( IsMultiplayer() ) { common->Printf( "Can't load during net play.\n" ); if ( wipeForced ) { ClearWipe(); } return false; } if ( GetCurrentGame() != DOOM3_BFG ) { return false; } if ( session->GetSignInManager().GetMasterLocalUser() == NULL ) { return false; } if (mapSpawnData.savegameFile != NULL ) { return false; } bool found = false; const saveGameDetailsList_t & sgdl = session->GetSaveGameManager().GetEnumeratedSavegames(); for ( int i = 0; i < sgdl.Num(); i++ ) { if ( sgdl[i].slotName == saveName ) { if ( sgdl[i].GetLanguage() != sys_lang.GetString() ) { idStaticList< idSWFScriptFunction *, 4 > callbacks; idStaticList< idStrId, 4 > optionText; optionText.Append( idStrId( "#str_swf_continue" ) ); idStrStatic<256> langName = "#str_lang_" + sgdl[i].GetLanguage(); idStrStatic<256> msg; msg.Format( idLocalization::GetString( "#str_dlg_wrong_language" ), idLocalization::GetString( langName ) ); Dialog().AddDynamicDialog( GDM_SAVEGAME_WRONG_LANGUAGE, callbacks, optionText, true, msg, false, true ); if ( wipeForced ) { ClearWipe(); } return false; } found = true; break; } } if ( !found ) { common->Printf( "Could not find save '%s'\n", saveName ); if ( wipeForced ) { ClearWipe(); } return false; } mapSpawnData.savegameFile = &saveFile; mapSpawnData.stringTableFile = &stringsFile; saveFileEntryList_t files; files.Append( mapSpawnData.stringTableFile ); files.Append( mapSpawnData.savegameFile ); idStr slotName = saveName; ScrubSaveGameFileName( slotName ); saveFile.Clear( false ); stringsFile.Clear( false ); saveGameHandle_t loadGameHandle = session->LoadGameSync( slotName, files ); if ( loadGameHandle != 0 ) { return true; } mapSpawnData.savegameFile = NULL; if ( wipeForced ) { ClearWipe(); } return false; }
/* ================== idCommonLocal::idCommonLocal ================== */ idCommonLocal::idCommonLocal() : readSnapshotIndex( 0 ), writeSnapshotIndex( 0 ), optimalTimeBuffered( 0.0f ), optimalTimeBufferedWindow( 0.0f ), optimalPCTBuffer( 0.5f ), lastPacifierSessionTime( 0 ), lastPacifierGuiTime( 0 ), lastPacifierDialogState( false ), showShellRequested( false ), currentGame( DOOM3_BFG ), idealCurrentGame( DOOM3_BFG ), doomClassicMaterial( NULL ) { snapCurrent.localTime = -1; snapPrevious.localTime = -1; snapCurrent.serverTime = -1; snapPrevious.serverTime = -1; snapTimeBuffered = 0.0f; effectiveSnapRate = 0.0f; totalBufferedTime = 0; totalRecvTime = 0; com_fullyInitialized = false; com_refreshOnPrint = false; com_errorEntered = ERP_NONE; com_shuttingDown = false; com_isJapaneseSKU = false; logFile = NULL; strcpy( errorMessage, "" ); rd_buffer = NULL; rd_buffersize = 0; rd_flush = NULL; gameDLL = 0; loadGUI = NULL; nextLoadTip = 0; isHellMap = false; wipeForced = false; defaultLoadscreen = false; menuSoundWorld = NULL; insideUpdateScreen = false; insideExecuteMapChange = false; mapSpawnData.savegameFile = NULL; currentMapName.Clear(); aviDemoShortName.Clear(); renderWorld = NULL; soundWorld = NULL; menuSoundWorld = NULL; readDemo = NULL; writeDemo = NULL; gameFrame = 0; gameTimeResidual = 0; syncNextGameFrame = true; mapSpawned = false; aviCaptureMode = false; timeDemo = TD_NO; nextSnapshotSendTime = 0; nextUsercmdSendTime = 0; clientPrediction = 0; saveFile = NULL; stringsFile = NULL; ClearWipe(); }
/* =============== idCommonLocal::ExecuteMapChange Performs the initialization of a game based on session match parameters, used for both single player and multiplayer, but not for renderDemos, which don't create a game at all. Exits with mapSpawned = true =============== */ void idCommonLocal::ExecuteMapChange() { if ( session->GetState() != idSession::LOADING ) { idLib::Warning( "Session state is not LOADING in ExecuteMapChange" ); return; } // Clear all dialogs before beginning the load common->Dialog().ClearDialogs( true ); // Remember the current load ID. // This is so we can tell if we had a new loadmap request from within an existing loadmap call const int cachedLoadingID = session->GetLoadingID(); const idMatchParameters & matchParameters = session->GetActingGameStateLobbyBase().GetMatchParms(); if ( matchParameters.numSlots <= 0 ) { idLib::Warning( "numSlots <= 0 in ExecuteMapChange" ); return; } insideExecuteMapChange = true; common->Printf( "--------- Execute Map Change ---------\n" ); common->Printf( "Map: %s\n", matchParameters.mapName.c_str() ); // ensure that r_znear is reset to the default value // this fixes issues with the projection matrix getting messed up when switching maps or loading a saved game // while an in-game cinematic is playing. cvarSystem->SetCVarFloat( "r_znear", 1.0f ); // reset all cheat cvars for a multiplayer game if ( IsMultiplayer() ) { cvarSystem->ResetFlaggedVariables( CVAR_CHEAT ); } int start = Sys_Milliseconds(); for ( int i = 0; i < MAX_INPUT_DEVICES; i++ ) { Sys_SetRumble( i, 0, 0 ); } // close console and remove any prints from the notify lines console->Close(); // clear all menu sounds soundWorld->Pause(); menuSoundWorld->ClearAllSoundEmitters(); soundSystem->SetPlayingSoundWorld( menuSoundWorld ); soundSystem->Render(); // extract the map name from serverinfo currentMapName = matchParameters.mapName; currentMapName.StripFileExtension(); idStrStatic< MAX_OSPATH > fullMapName = "maps/"; fullMapName += currentMapName; fullMapName.SetFileExtension( "map" ); if ( mapSpawnData.savegameFile ) { fileSystem->BeginLevelLoad( currentMapName, NULL, 0 ); } else { fileSystem->BeginLevelLoad( currentMapName, saveFile.GetDataPtr(), saveFile.GetAllocated() ); } // capture the current screen and start a wipe // immediately complete the wipe to fade out the level transition // run the wipe to completion StartWipe( "wipeMaterial", true ); CompleteWipe(); int sm = Sys_Milliseconds(); // shut down the existing game if it is running UnloadMap(); int ms = Sys_Milliseconds() - sm; common->Printf( "%6d msec to unload map\n", ms ); // Free media from previous level and // note which media we are going to need to load sm = Sys_Milliseconds(); renderSystem->BeginLevelLoad(); soundSystem->BeginLevelLoad(); declManager->BeginLevelLoad(); uiManager->BeginLevelLoad(); ms = Sys_Milliseconds() - sm; common->Printf( "%6d msec to free assets\n", ms ); //Sys_DumpMemory( true ); // load / program a gui to stay up on the screen while loading // set the loading gui that we will wipe to bool hellMap = false; LoadLoadingGui( currentMapName, hellMap ); // Stop rendering the wipe ClearWipe(); if ( fileSystem->UsingResourceFiles() ) { idStrStatic< MAX_OSPATH > manifestName = currentMapName; manifestName.Replace( "game/", "maps/" ); manifestName.Replace( "/mp/", "/" ); manifestName += ".preload"; idPreloadManifest manifest; manifest.LoadManifest( manifestName ); renderSystem->Preload( manifest, currentMapName ); soundSystem->Preload( manifest ); game->Preload( manifest ); } if ( common->IsMultiplayer() ) { // In multiplayer, make sure the player is either 60Hz or 120Hz // to avoid potential issues. const float mpEngineHz = ( com_engineHz.GetFloat() < 90.0f ) ? 60.0f : 120.0f; com_engineHz_denominator = 100LL * mpEngineHz; com_engineHz_latched = mpEngineHz; } else { // allow com_engineHz to be changed between map loads com_engineHz_denominator = 100LL * com_engineHz.GetFloat(); com_engineHz_latched = com_engineHz.GetFloat(); } // note any warning prints that happen during the load process common->ClearWarnings( currentMapName ); // release the mouse cursor // before we do this potentially long operation Sys_GrabMouseCursor( false ); // let the renderSystem load all the geometry if ( !renderWorld->InitFromMap( fullMapName ) ) { common->Error( "couldn't load %s", fullMapName.c_str() ); } // for the synchronous networking we needed to roll the angles over from // level to level, but now we can just clear everything usercmdGen->InitForNewMap(); // load and spawn all other entities ( from a savegame possibly ) if ( mapSpawnData.savegameFile ) { if ( !game->InitFromSaveGame( fullMapName, renderWorld, soundWorld, mapSpawnData.savegameFile, mapSpawnData.stringTableFile, mapSpawnData.savegameVersion ) ) { // If the loadgame failed, end the session, which will force us to go back to the main menu session->QuitMatchToTitle(); } } else { if ( !IsMultiplayer() ) { assert( game->GetLocalClientNum() == 0 ); assert( matchParameters.gameMode == GAME_MODE_SINGLEPLAYER ); assert( matchParameters.gameMap == GAME_MAP_SINGLEPLAYER ); game->SetPersistentPlayerInfo( 0, mapSpawnData.persistentPlayerInfo ); } game->SetServerInfo( matchParameters.serverInfo ); game->InitFromNewMap( fullMapName, renderWorld, soundWorld, matchParameters.gameMode, Sys_Milliseconds() ); } game->Shell_CreateMenu( true ); // Reset some values important to multiplayer ResetNetworkingState(); // If the session state is not loading here, something went wrong. if ( session->GetState() == idSession::LOADING && session->GetLoadingID() == cachedLoadingID ) { // Notify session we are done loading session->LoadingFinished(); while ( session->GetState() == idSession::LOADING ) { Sys_GenerateEvents(); session->UpdateSignInManager(); session->Pump(); Sys_Sleep( 10 ); } } if ( !mapSpawnData.savegameFile ) { // run a single frame to catch any resources that are referenced by events posted in spawn idUserCmdMgr emptyCommandManager; gameReturn_t emptyGameReturn; for ( int playerIndex = 0; playerIndex < MAX_PLAYERS; ++playerIndex ) { emptyCommandManager.PutUserCmdForPlayer( playerIndex, usercmd_t() ); } if ( IsClient() ) { game->ClientRunFrame( emptyCommandManager, false, emptyGameReturn ); } else { game->RunFrame( emptyCommandManager, emptyGameReturn ); } } renderSystem->EndLevelLoad(); soundSystem->EndLevelLoad(); declManager->EndLevelLoad(); uiManager->EndLevelLoad( currentMapName ); fileSystem->EndLevelLoad(); if ( !mapSpawnData.savegameFile && !IsMultiplayer() ) { common->Printf( "----- Running initial game frames -----\n" ); // In single player, run a bunch of frames to make sure ragdolls are settled idUserCmdMgr emptyCommandManager; gameReturn_t emptyGameReturn; for ( int i = 0; i < 100; i++ ) { for ( int playerIndex = 0; playerIndex < MAX_PLAYERS; ++playerIndex ) { emptyCommandManager.PutUserCmdForPlayer( playerIndex, usercmd_t() ); } game->RunFrame( emptyCommandManager, emptyGameReturn ); } // kick off an auto-save of the game (so we can always continue in this map if we die before hitting an autosave) common->Printf( "----- Saving Game -----\n" ); SaveGame( "autosave" ); } common->Printf( "----- Generating Interactions -----\n" ); // let the renderSystem generate interactions now that everything is spawned renderWorld->GenerateAllInteractions(); { int vertexMemUsedKB = vertexCache.staticData.vertexMemUsed.GetValue() / 1024; int indexMemUsedKB = vertexCache.staticData.indexMemUsed.GetValue() / 1024; idLib::Printf( "Used %dkb of static vertex memory (%d%%)\n", vertexMemUsedKB, vertexMemUsedKB * 100 / ( STATIC_VERTEX_MEMORY / 1024 ) ); idLib::Printf( "Used %dkb of static index memory (%d%%)\n", indexMemUsedKB, indexMemUsedKB * 100 / ( STATIC_INDEX_MEMORY / 1024 ) ); } if ( common->JapaneseCensorship() ) { if ( currentMapName.Icmp( "game/mp/d3xpdm3" ) == 0 ) { const idMaterial * gizpool2 = declManager->FindMaterial( "textures/hell/gizpool2" ); idMaterial * chiglass1bluex = const_cast<idMaterial *>( declManager->FindMaterial( "textures/sfx/chiglass1bluex" ) ); idTempArray<char> text( gizpool2->GetTextLength() ); gizpool2->GetText( text.Ptr() ); chiglass1bluex->Parse( text.Ptr(), text.Num(), false ); } } common->PrintWarnings(); session->Pump(); if ( session->GetState() != idSession::INGAME ) { // Something went wrong, don't process stale reliables that have been queued up. reliableQueue.Clear(); } usercmdGen->Clear(); // remove any prints from the notify lines console->ClearNotifyLines(); Sys_SetPhysicalWorkMemory( -1, -1 ); // at this point we should be done with the loading gui so we kill it delete loadGUI; loadGUI = NULL; // capture the current screen and start a wipe StartWipe( "wipe2Material" ); // we are valid for game draws now insideExecuteMapChange = false; mapSpawned = true; Sys_ClearEvents(); int msec = Sys_Milliseconds() - start; common->Printf( "%6d msec to load %s\n", msec, currentMapName.c_str() ); //Sys_DumpMemory( false ); // Issue a render at the very end of the load process to update soundTime before the first frame soundSystem->Render(); }