Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

958 lines
24 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Runs the state machine for the host & server
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "host_state.h"
  8. #include "eiface.h"
  9. #include "quakedef.h"
  10. #include "server.h"
  11. #include "sv_main.h"
  12. #include "host_cmd.h"
  13. #include "host.h"
  14. #include "screen.h"
  15. #include "icliententity.h"
  16. #include "icliententitylist.h"
  17. #include "client.h"
  18. #include "host_jmp.h"
  19. #include "cdll_engine_int.h"
  20. #include "tier0/vprof.h"
  21. #include "tier0/icommandline.h"
  22. #include "filesystem_engine.h"
  23. #include "zone.h"
  24. #include "iengine.h"
  25. #include "snd_audio_source.h"
  26. #include "sv_steamauth.h"
  27. #ifndef DEDICATED
  28. #include "vgui_baseui_interface.h"
  29. #endif
  30. #include "sv_plugin.h"
  31. #include "cl_main.h"
  32. #include "sv_steamauth.h"
  33. #include "datacache/imdlcache.h"
  34. #include "sys_dll.h"
  35. #include "testscriptmgr.h"
  36. #include "cvar.h"
  37. #include "MapReslistGenerator.h"
  38. #include "filesystem/IQueuedLoader.h"
  39. #include "matchmaking/imatchframework.h"
  40. #ifdef _PS3
  41. #include "tls_ps3.h"
  42. #endif
  43. // memdbgon must be the last include file in a .cpp file!!!
  44. #include "tier0/memdbgon.h"
  45. extern bool g_bAbortServerSet;
  46. #ifndef DEDICATED
  47. extern ConVar reload_materials;
  48. #endif
  49. typedef enum
  50. {
  51. HS_NEW_GAME = 0,
  52. HS_LOAD_GAME,
  53. HS_CHANGE_LEVEL_SP,
  54. HS_CHANGE_LEVEL_MP,
  55. HS_RUN,
  56. HS_GAME_SHUTDOWN,
  57. HS_SHUTDOWN,
  58. HS_RESTART,
  59. } HOSTSTATES;
  60. // a little class that manages the state machine for the host
  61. class CHostState
  62. {
  63. public:
  64. CHostState();
  65. void Init();
  66. void FrameUpdate( float time );
  67. void SetNextState( HOSTSTATES nextState );
  68. void RunGameInit();
  69. void SetState( HOSTSTATES newState, bool clearNext );
  70. void GameShutdown();
  71. void State_NewGame();
  72. void State_LoadGame();
  73. void State_ChangeLevelMP();
  74. void State_ChangeLevelSP();
  75. void State_Run( float time );
  76. void State_GameShutdown();
  77. void State_Shutdown();
  78. void State_Restart();
  79. bool IsGameShuttingDown();
  80. void RememberLocation();
  81. void OnClientConnected(); // Client fully connected
  82. void OnClientDisconnected(); // Client disconnected
  83. HOSTSTATES m_currentState;
  84. HOSTSTATES m_nextState;
  85. Vector m_vecLocation;
  86. QAngle m_angLocation;
  87. char m_levelName[256];
  88. char m_mapGroupName[256];
  89. char m_landmarkName[256];
  90. char m_saveName[256];
  91. float m_flShortFrameTime; // run a few one-tick frames to avoid large timesteps while loading assets
  92. bool m_activeGame;
  93. bool m_bRememberLocation;
  94. bool m_bBackgroundLevel;
  95. bool m_bWaitingForConnection;
  96. bool m_bLetToolsOverrideLoadGameEnts; // During a load game, this tells Foundry to override ents that are selected in Hammer.
  97. bool m_bSplitScreenConnect;
  98. bool m_bGameHasShutDownAndFlushedMemory; // This is false once we load a map into memory, and set to true once the map is unloaded and all memory flushed
  99. bool m_bWorkshopMapDownloadPending;
  100. };
  101. static bool Host_ValidGame( void );
  102. static CHostState g_HostState;
  103. //-----------------------------------------------------------------------------
  104. // external API for manipulating the host state machine
  105. //-----------------------------------------------------------------------------
  106. void HostState_Init()
  107. {
  108. g_HostState.Init();
  109. }
  110. void HostState_Frame( float time )
  111. {
  112. g_HostState.FrameUpdate( time );
  113. }
  114. void HostState_RunGameInit()
  115. {
  116. g_HostState.RunGameInit();
  117. g_ServerGlobalVariables.bMapLoadFailed = false;
  118. }
  119. //-----------------------------------------------------------------------------
  120. // start a new game as soon as possible
  121. //-----------------------------------------------------------------------------
  122. void HostState_NewGame( char const *pMapName, bool remember_location, bool background, bool bSplitScreenConnect )
  123. {
  124. char szMapName[_MAX_PATH];
  125. Q_StripExtension( pMapName, szMapName, sizeof(szMapName) );
  126. Q_strncpy( g_HostState.m_levelName, szMapName, sizeof( g_HostState.m_levelName ) );
  127. Q_FixSlashes( g_HostState.m_levelName, '/' ); // Store with forward slashes internally to be consistent.
  128. g_HostState.m_landmarkName[0] = 0;
  129. g_HostState.m_bRememberLocation = remember_location;
  130. g_HostState.m_bWaitingForConnection = true;
  131. g_HostState.m_bBackgroundLevel = background;
  132. g_HostState.m_bSplitScreenConnect = bSplitScreenConnect;
  133. if ( remember_location )
  134. {
  135. g_HostState.RememberLocation();
  136. }
  137. g_HostState.SetNextState( HS_NEW_GAME );
  138. }
  139. //-----------------------------------------------------------------------------
  140. // load a new game as soon as possible
  141. //-----------------------------------------------------------------------------
  142. void HostState_LoadGame( char const *pSaveFileName, bool remember_location, bool bLetToolsOverrideLoadGameEnts )
  143. {
  144. #ifndef DEDICATED
  145. // Make sure the freaking save file exists....
  146. if ( !saverestore->SaveFileExists( pSaveFileName ) )
  147. {
  148. Warning("Save file %s can't be found!\n", pSaveFileName );
  149. SCR_EndLoadingPlaque();
  150. return;
  151. }
  152. Q_strncpy( g_HostState.m_saveName, pSaveFileName, sizeof( g_HostState.m_saveName ) );
  153. // Tell the game .dll we are loading another game
  154. serverGameDLL->PreSaveGameLoaded( pSaveFileName, sv.IsActive() );
  155. g_HostState.m_bRememberLocation = remember_location;
  156. g_HostState.m_bBackgroundLevel = false;
  157. g_HostState.m_bWaitingForConnection = true;
  158. g_HostState.m_bSplitScreenConnect = false;
  159. g_HostState.m_bLetToolsOverrideLoadGameEnts = bLetToolsOverrideLoadGameEnts;
  160. if ( remember_location )
  161. {
  162. g_HostState.RememberLocation();
  163. }
  164. g_HostState.SetNextState( HS_LOAD_GAME );
  165. #endif
  166. }
  167. // change level (single player style - smooth transition)
  168. void HostState_ChangeLevelSP( char const *pNewLevel, char const *pLandmarkName )
  169. {
  170. Q_strncpy( g_HostState.m_levelName, pNewLevel, sizeof( g_HostState.m_levelName ) );
  171. Q_FixSlashes( g_HostState.m_levelName, '/' ); // Store with forward slashes internally to be consistent.
  172. Q_strncpy( g_HostState.m_landmarkName, pLandmarkName, sizeof( g_HostState.m_landmarkName ) );
  173. g_HostState.SetNextState( HS_CHANGE_LEVEL_SP );
  174. }
  175. // change level (multiplayer style - respawn all connected clients)
  176. void HostState_ChangeLevelMP( char const *pNewLevel, char const *pLandmarkName )
  177. {
  178. Steam3Server().NotifyOfLevelChange();
  179. Q_strncpy( g_HostState.m_levelName, pNewLevel, sizeof( g_HostState.m_levelName ) );
  180. Q_FixSlashes( g_HostState.m_levelName, '/' ); // Store with forward slashes internally to be consistent.
  181. Q_strncpy( g_HostState.m_landmarkName, pLandmarkName, sizeof( g_HostState.m_landmarkName ) );
  182. PublishedFileId_t id = serverGameDLL->GetUGCMapFileID( pNewLevel );
  183. if ( sv.IsDedicated() && id != 0 )
  184. {
  185. // If we're hosting a workshop map, don't change level until we've made sure we're hosting the latest version.
  186. serverGameDLL->UpdateUGCMap( id );
  187. g_HostState.m_bWorkshopMapDownloadPending = true;
  188. }
  189. else
  190. {
  191. g_HostState.SetNextState( HS_CHANGE_LEVEL_MP );
  192. }
  193. }
  194. // set the mapgroup name
  195. void HostState_SetMapGroupName( char const *pMapGroupName )
  196. {
  197. if ( pMapGroupName )
  198. {
  199. V_strncpy( g_HostState.m_mapGroupName, pMapGroupName, sizeof ( g_HostState.m_mapGroupName ) );
  200. sv.SetMapGroupName( pMapGroupName );
  201. }
  202. }
  203. // shutdown the game as soon as possible
  204. void HostState_GameShutdown()
  205. {
  206. Steam3Server().NotifyOfLevelChange();
  207. // This will get called during shutdown, ignore it.
  208. if ( g_HostState.m_currentState != HS_SHUTDOWN &&
  209. g_HostState.m_currentState != HS_RESTART &&
  210. g_HostState.m_currentState != HS_GAME_SHUTDOWN
  211. )
  212. {
  213. g_HostState.SetNextState( HS_GAME_SHUTDOWN );
  214. }
  215. }
  216. // shutdown the engine/program as soon as possible
  217. void HostState_Shutdown()
  218. {
  219. g_HostState.SetNextState( HS_SHUTDOWN );
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Restart the engine
  223. //-----------------------------------------------------------------------------
  224. void HostState_Restart()
  225. {
  226. g_HostState.SetNextState( HS_RESTART );
  227. }
  228. bool HostState_IsGameShuttingDown()
  229. {
  230. return g_HostState.IsGameShuttingDown();
  231. }
  232. void HostState_OnClientConnected()
  233. {
  234. g_HostState.OnClientConnected();
  235. }
  236. void HostState_OnClientDisconnected()
  237. {
  238. g_HostState.OnClientDisconnected();
  239. }
  240. void HostState_SetSpawnPoint(Vector &position, QAngle &angle)
  241. {
  242. g_HostState.m_angLocation = angle;
  243. g_HostState.m_vecLocation = position;
  244. g_HostState.m_bRememberLocation = true;
  245. }
  246. bool HostState_IsTransitioningToLoad()
  247. {
  248. if ( g_HostState.m_nextState == HS_NEW_GAME ||
  249. g_HostState.m_nextState == HS_LOAD_GAME ||
  250. g_HostState.m_nextState == HS_CHANGE_LEVEL_SP ||
  251. g_HostState.m_nextState == HS_CHANGE_LEVEL_MP )
  252. {
  253. return true;
  254. }
  255. return false;
  256. }
  257. bool HostState_GameHasShutDownAndFlushedMemory()
  258. {
  259. // False if map-related assets are still in memory
  260. // True once all such have been flushed from memory
  261. return g_HostState.m_bGameHasShutDownAndFlushedMemory;
  262. }
  263. void HostState_Pre_LoadMapIntoMemory()
  264. {
  265. // About to load a map into memory
  266. g_HostState.m_bGameHasShutDownAndFlushedMemory = false;
  267. }
  268. void HostState_Post_FlushMapFromMemory()
  269. {
  270. // Map-related assets have been flushed from memory
  271. g_HostState.m_bGameHasShutDownAndFlushedMemory = true;
  272. }
  273. const char *HostState_GetNewLevel()
  274. {
  275. return g_HostState.m_levelName;
  276. }
  277. //-----------------------------------------------------------------------------
  278. //-----------------------------------------------------------------------------
  279. // Class implementation
  280. //-----------------------------------------------------------------------------
  281. CHostState::CHostState()
  282. {
  283. m_bGameHasShutDownAndFlushedMemory = true;
  284. SetState( HS_RUN, true );
  285. }
  286. void CHostState::Init()
  287. {
  288. // This can occur if user pressed close button during opening cinematic
  289. if ( m_nextState != HS_SHUTDOWN )
  290. {
  291. if ( IsPS3QuitRequested() && m_nextState == HS_GAME_SHUTDOWN )
  292. {
  293. // do nothing; the state is set to game shutdown, leave it there. Otherwise engine goes into infinite "RUN" frame loop
  294. }
  295. else
  296. {
  297. SetState( HS_RUN, true );
  298. }
  299. }
  300. m_bLetToolsOverrideLoadGameEnts = false;
  301. m_activeGame = false;
  302. m_levelName[0] = 0;
  303. m_mapGroupName[0] = 0;
  304. m_saveName[0] = 0;
  305. m_landmarkName[0] = 0;
  306. m_bRememberLocation = 0;
  307. m_bBackgroundLevel = false;
  308. m_bSplitScreenConnect = false;
  309. m_vecLocation.Init();
  310. m_angLocation.Init();
  311. m_bWaitingForConnection = false;
  312. m_flShortFrameTime = 1.0;
  313. m_bGameHasShutDownAndFlushedMemory = true;
  314. m_bWorkshopMapDownloadPending = false;
  315. }
  316. void CHostState::SetState( HOSTSTATES newState, bool clearNext )
  317. {
  318. m_currentState = newState;
  319. if ( clearNext )
  320. {
  321. m_nextState = newState;
  322. }
  323. }
  324. void CHostState::SetNextState( HOSTSTATES next )
  325. {
  326. Assert( m_currentState == HS_RUN );
  327. m_nextState = next;
  328. }
  329. void CHostState::RunGameInit()
  330. {
  331. materials->OnDebugEvent( "CHostState::RunGameInit" );
  332. Assert( !m_activeGame );
  333. if ( serverGameDLL )
  334. {
  335. serverGameDLL->GameInit();
  336. }
  337. m_activeGame = true;
  338. }
  339. void CHostState::GameShutdown()
  340. {
  341. if ( m_activeGame )
  342. {
  343. materials->OnDebugEvent( "HostState::GameShutdown(active)");
  344. serverGameDLL->GameShutdown();
  345. //if (! sv.IsLevelMainMenuBackground() ) can't do this here - it will overwrite variables that are part of the game setup (these vars are set _before_ the previous game is ended). hibernate is
  346. // ResetGameConVarsToDefaults(); how we deal with this on the dedicated server.
  347. m_activeGame = false;
  348. }
  349. else
  350. {
  351. materials->OnDebugEvent( "HostState::GameShutdown" );
  352. }
  353. }
  354. // These State_ functions execute that state's code right away
  355. // The external API queues up state changes to happen when the state machine is processed.
  356. void CHostState::State_NewGame()
  357. {
  358. bool bSplitScreenConnect = m_bSplitScreenConnect;
  359. m_bSplitScreenConnect = false;
  360. materials->OnDebugEvent( "CHostState::State_NewGame" );
  361. if ( Host_ValidGame() )
  362. {
  363. // Demand load game .dll if running with -nogamedll flag, etc.
  364. if ( !serverGameClients )
  365. {
  366. SV_InitGameDLL();
  367. }
  368. if ( !serverGameClients )
  369. {
  370. Warning( "Can't start game, no valid server.dll loaded\n" );
  371. }
  372. else
  373. {
  374. if ( modelloader->Map_IsValid( m_levelName ) )
  375. {
  376. if ( Host_NewGame( m_levelName, m_mapGroupName, false, m_bBackgroundLevel, bSplitScreenConnect ) )
  377. {
  378. // succesfully started the new game
  379. SetState( HS_RUN, true );
  380. return;
  381. }
  382. }
  383. }
  384. }
  385. SCR_EndLoadingPlaque();
  386. // new game failed
  387. GameShutdown();
  388. // run the server at the console
  389. SetState( HS_RUN, true );
  390. sv.ClearReservationStatus();
  391. }
  392. void CHostState::State_LoadGame()
  393. {
  394. materials->OnDebugEvent( "CHostState::State_LoadGame" );
  395. #ifndef DEDICATED
  396. HostState_RunGameInit();
  397. if ( saverestore->LoadGame( m_saveName, m_bLetToolsOverrideLoadGameEnts ) )
  398. {
  399. // succesfully started the new game
  400. GetTestScriptMgr()->CheckPoint( "load_game" );
  401. SetState( HS_RUN, true );
  402. return;
  403. }
  404. #endif
  405. SCR_EndLoadingPlaque();
  406. // load game failed
  407. GameShutdown();
  408. // run the server at the console
  409. SetState( HS_RUN, true );
  410. if ( g_pMatchFramework->GetMatchSession() )
  411. {
  412. g_pMatchFramework->CloseSession();
  413. return;
  414. }
  415. #if 0
  416. if ( IsX360() )
  417. {
  418. // On the 360 we need to return to the background map
  419. g_ServerGlobalVariables.bMapLoadFailed = true;
  420. Cbuf_Clear( Cbuf_GetCurrentPlayer() );
  421. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "startupmenu force" );
  422. Cbuf_Execute();
  423. }
  424. #endif
  425. }
  426. void CHostState::State_ChangeLevelMP()
  427. {
  428. materials->OnDebugEvent( "CHostState::State_ChangeLevelMP" );
  429. if ( Host_ValidGame() )
  430. {
  431. Steam3Server().NotifyOfLevelChange();
  432. g_pServerPluginHandler->LevelShutdown();
  433. #if !defined(DEDICATED)
  434. audiosourcecache->LevelShutdown();
  435. #endif
  436. if ( modelloader->Map_IsValid( m_levelName ) )
  437. {
  438. #ifndef DEDICATED
  439. // start progress bar immediately for multiplayer level transitions
  440. EngineVGui()->EnabledProgressBarForNextLoad();
  441. #endif
  442. Host_Changelevel( false, m_levelName, m_mapGroupName, m_landmarkName );
  443. SetState( HS_RUN, true );
  444. return;
  445. }
  446. }
  447. // fail
  448. ConMsg( "Unable to change level!\n" );
  449. SetState( HS_RUN, true );
  450. }
  451. void CHostState::State_ChangeLevelSP()
  452. {
  453. materials->OnDebugEvent( "CHostState::State_ChangeLevelSP" );
  454. if ( Host_ValidGame() )
  455. {
  456. if ( modelloader->Map_IsValid( m_levelName ) )
  457. {
  458. Host_Changelevel( true, m_levelName, m_mapGroupName, m_landmarkName );
  459. SetState( HS_RUN, true );
  460. return;
  461. }
  462. }
  463. // fail
  464. ConMsg( "Unable to change level!\n" );
  465. SetState( HS_RUN, true );
  466. }
  467. static bool IsClientActive()
  468. {
  469. if ( !sv.IsActive() )
  470. {
  471. #ifdef DEDICATED
  472. return false;
  473. #else
  474. return GetBaseLocalClient().IsActive();
  475. #endif
  476. }
  477. for ( int i = 0; i < sv.GetClientCount(); i++ )
  478. {
  479. CGameClient *pClient = sv.Client( i );
  480. if ( pClient->IsActive() )
  481. {
  482. return true;
  483. }
  484. }
  485. return false;
  486. }
  487. static bool IsClientConnected()
  488. {
  489. if ( !sv.IsActive() )
  490. {
  491. #ifndef DEDICATED
  492. return GetBaseLocalClient().IsConnected();
  493. #else
  494. return false;
  495. #endif
  496. }
  497. for ( int i = 0; i < sv.GetClientCount(); i++ )
  498. {
  499. CGameClient *pClient = sv.Client( i );
  500. if ( pClient->IsConnected() )
  501. return true;
  502. }
  503. return false;
  504. }
  505. static bool s_bFirstRunFrame = true;
  506. void CHostState::State_Run( float frameTime )
  507. {
  508. //materials->OnDebugEvent( "CHostState::State_Run" );
  509. if ( m_flShortFrameTime > 0 )
  510. {
  511. if ( IsClientActive() )
  512. {
  513. m_flShortFrameTime = (m_flShortFrameTime > frameTime) ? (m_flShortFrameTime-frameTime) : 0;
  514. }
  515. // Only clamp time if client is in process of connecting or is already connected.
  516. if ( IsClientConnected() )
  517. {
  518. frameTime = MIN( frameTime, host_state.interval_per_tick );
  519. }
  520. }
  521. int nTimerWait = 15;
  522. if ( s_bFirstRunFrame ) // the first frame can take a while especially during fork startup
  523. {
  524. s_bFirstRunFrame = false;
  525. nTimerWait *= 2;
  526. }
  527. if ( sv.IsDedicated() )
  528. BeginWatchdogTimer( nTimerWait );
  529. Host_RunFrame( frameTime ); // 5 seconds allowed unless map load
  530. if ( sv.IsDedicated() )
  531. EndWatchdogTimer();
  532. // Continue loading process once we've tried to update the new map
  533. if ( sv.IsDedicated() && g_HostState.m_bWorkshopMapDownloadPending && !serverGameDLL->HasPendingMapDownloads() )
  534. {
  535. g_HostState.SetNextState( HS_CHANGE_LEVEL_MP );
  536. g_HostState.m_bWorkshopMapDownloadPending = false;
  537. }
  538. switch( m_nextState )
  539. {
  540. case HS_RUN:
  541. break;
  542. case HS_LOAD_GAME:
  543. case HS_NEW_GAME:
  544. #if !defined( DEDICATED )
  545. SCR_BeginLoadingPlaque( m_levelName );
  546. #endif
  547. // FALL THROUGH INTENTIONALLY TO SHUTDOWN
  548. case HS_SHUTDOWN:
  549. case HS_RESTART:
  550. // NOTE: The game must be shutdown before a new game can start,
  551. // before a game can load, and before the system can be shutdown.
  552. // This is done here instead of pathfinding through a state transition graph.
  553. // That would be equivalent as the only way to get from HS_RUN to HS_LOAD_GAME is through HS_GAME_SHUTDOWN.
  554. case HS_GAME_SHUTDOWN:
  555. SetState( HS_GAME_SHUTDOWN, false );
  556. break;
  557. case HS_CHANGE_LEVEL_MP:
  558. case HS_CHANGE_LEVEL_SP:
  559. SetState( m_nextState, true );
  560. break;
  561. default:
  562. SetState( HS_RUN, true );
  563. break;
  564. }
  565. }
  566. void CHostState::State_GameShutdown()
  567. {
  568. materials->OnDebugEvent( "CHostState::State_GameShutdown" );
  569. if ( serverGameDLL )
  570. {
  571. Steam3Server().NotifyOfLevelChange();
  572. g_pServerPluginHandler->LevelShutdown();
  573. #if !defined(DEDICATED)
  574. audiosourcecache->LevelShutdown();
  575. #endif
  576. }
  577. GameShutdown();
  578. #ifndef DEDICATED
  579. saverestore->ClearSaveDir();
  580. #endif
  581. Host_ShutdownServer();
  582. MapReslistGenerator().OnLevelShutdown();
  583. if ( IsGameConsole() )
  584. {
  585. if ( m_nextState == HS_GAME_SHUTDOWN )
  586. {
  587. // game consoles needs some memory to do main menu (movie, installer, etc)
  588. // this is an attempt to purge map related data
  589. g_pQueuedLoader->PurgeAll();
  590. g_pDataCache->Flush();
  591. wavedatacache->Flush();
  592. g_pMDLCache->ReleaseAnimBlockAllocator();
  593. materials->OnLevelShutdown();
  594. materials->UncacheUnusedMaterials();
  595. // Record the fact that this memory flush completed
  596. HostState_Post_FlushMapFromMemory();
  597. // the above leaves resources in a bad state for Queued Loader
  598. // this ensures there a full purge necessary before any map gets loaded
  599. SV_FlushMemoryOnNextServer();
  600. // When ejecting BD right after starting loading a savegame, the game sometimes never calls State_shutdown, never sets engine state to DLL_CLOSE and goes into infinite loop
  601. // calling shutdown here to prevent that.
  602. if( IsPS3QuitRequested() )
  603. {
  604. State_Shutdown();
  605. }
  606. }
  607. }
  608. if( IsPS3QuitRequested() && m_nextState == HS_GAME_SHUTDOWN )
  609. {
  610. SetState( HS_GAME_SHUTDOWN, true );
  611. }
  612. else
  613. {
  614. switch( m_nextState )
  615. {
  616. case HS_LOAD_GAME:
  617. case HS_NEW_GAME:
  618. case HS_SHUTDOWN:
  619. case HS_RESTART:
  620. SetState( m_nextState, true );
  621. break;
  622. default:
  623. SetState( HS_RUN, true );
  624. break;
  625. }
  626. }
  627. }
  628. // Tell the launcher we're done.
  629. void CHostState::State_Shutdown()
  630. {
  631. #if !defined(DEDICATED)
  632. CL_EndMovie();
  633. #endif
  634. eng->SetNextState( IEngine::DLL_CLOSE );
  635. }
  636. //-----------------------------------------------------------------------------
  637. // Purpose:
  638. //-----------------------------------------------------------------------------
  639. void CHostState::State_Restart( void )
  640. {
  641. // Just like a regular shutdown
  642. State_Shutdown();
  643. // But signal launcher/front end to restart engine
  644. eng->SetNextState( IEngine::DLL_RESTART );
  645. }
  646. //-----------------------------------------------------------------------------
  647. // this is the state machine's main processing loop
  648. //-----------------------------------------------------------------------------
  649. char const *g_szHostStateDelayedMessage = NULL;
  650. void CHostState::FrameUpdate( float time )
  651. {
  652. #if _DEBUG
  653. int loopCount = 0;
  654. #endif
  655. if ( setjmp (host_abortserver) )
  656. {
  657. Init();
  658. return;
  659. }
  660. g_bAbortServerSet = true;
  661. while ( true )
  662. {
  663. if ( g_szHostStateDelayedMessage )
  664. {
  665. struct tm newtime;
  666. char tString[ 128 ] = {};
  667. Plat_GetLocalTime( &newtime );
  668. Plat_GetTimeString( &newtime, tString, sizeof( tString ) );
  669. Warning( "Host state %d at %s -- %s\n", m_currentState, tString, g_szHostStateDelayedMessage );
  670. g_szHostStateDelayedMessage = NULL;
  671. }
  672. int oldState = m_currentState;
  673. // execute the current state (and transition to the next state if not in HS_RUN)
  674. switch( m_currentState )
  675. {
  676. case HS_NEW_GAME:
  677. g_pMDLCache->BeginMapLoad();
  678. State_NewGame();
  679. break;
  680. case HS_LOAD_GAME:
  681. g_pMDLCache->BeginMapLoad();
  682. State_LoadGame();
  683. break;
  684. case HS_CHANGE_LEVEL_MP:
  685. g_pMDLCache->BeginMapLoad();
  686. m_flShortFrameTime = 0.5f;
  687. State_ChangeLevelMP();
  688. break;
  689. case HS_CHANGE_LEVEL_SP:
  690. g_pMDLCache->BeginMapLoad();
  691. m_flShortFrameTime = 1.5f; // 1.5s of slower frames
  692. State_ChangeLevelSP();
  693. break;
  694. case HS_RUN:
  695. State_Run( time );
  696. break;
  697. case HS_GAME_SHUTDOWN:
  698. State_GameShutdown();
  699. break;
  700. case HS_SHUTDOWN:
  701. State_Shutdown();
  702. break;
  703. case HS_RESTART:
  704. g_pMDLCache->BeginMapLoad();
  705. State_Restart();
  706. break;
  707. }
  708. // only do a single pass at HS_RUN per frame. All other states loop until they reach HS_RUN
  709. if ( oldState == HS_RUN )
  710. break;
  711. // shutting down
  712. if ( oldState == HS_SHUTDOWN ||
  713. oldState == HS_RESTART )
  714. break;
  715. // we may be required to quit when in GAME_SHUTDOWN state on PS3, which state doesn't change to SHUTDOWN at this point
  716. // so in case we have user request to quit (or a disk ejected), to avoid infinite loop, we need to break out of this loop now.
  717. if( IsPS3QuitRequested() && oldState == HS_GAME_SHUTDOWN )
  718. break;
  719. // Only HS_RUN is allowed to persist across loops!!!
  720. // Also, detect circular state graphs (more than 8 cycling changes is probably a loop)
  721. // NOTE: In the current graph, there are at most 2.
  722. #if _DEBUG
  723. if ( (m_currentState == oldState) || (++loopCount > 8) )
  724. {
  725. Host_Error( "state crash!\n" );
  726. }
  727. #endif
  728. }
  729. }
  730. bool CHostState::IsGameShuttingDown( void )
  731. {
  732. return ( ( m_currentState == HS_GAME_SHUTDOWN ) || ( m_nextState == HS_GAME_SHUTDOWN ) );
  733. }
  734. void CHostState::RememberLocation()
  735. {
  736. #ifndef DEDICATED
  737. Assert( m_bRememberLocation );
  738. m_vecLocation = MainViewOrigin();
  739. VectorAngles( MainViewForward(), m_angLocation );
  740. IClientEntity *localPlayer = entitylist ? entitylist->GetClientEntity( GetLocalClient().m_nPlayerSlot + 1 ) : NULL;
  741. if ( localPlayer )
  742. {
  743. m_vecLocation = localPlayer->GetAbsOrigin();
  744. }
  745. m_vecLocation.z -= 64.0f; // subtract out a bit of Z to position the player lower
  746. #endif
  747. }
  748. void CHostState::OnClientConnected()
  749. {
  750. materials->OnDebugEvent( "CHostState::OnClientConnected" );
  751. if ( !m_bWaitingForConnection )
  752. return;
  753. m_bWaitingForConnection = false;
  754. if ( m_bRememberLocation )
  755. {
  756. m_bRememberLocation = false;
  757. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "setpos_exact %f %f %f\n", m_vecLocation.x, m_vecLocation.y, m_vecLocation.z ) );
  758. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "setang_exact %f %f %f\n", m_angLocation.x, m_angLocation.y, m_angLocation.z ) );
  759. }
  760. #if !defined( DEDICATED )
  761. if ( reload_materials.GetBool() )
  762. {
  763. // building cubemaps requires the materials to reload after map
  764. reload_materials.SetValue( 0 );
  765. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "mat_reloadallmaterials\n" );
  766. }
  767. // Spew global texture memory usage if asked to
  768. if( CommandLine()->CheckParm( "-dumpvidmemstats" ) )
  769. {
  770. FileHandle_t fp;
  771. fp = g_pFileSystem->Open( "vidmemstats.txt", "a" );
  772. g_pFileSystem->FPrintf( fp, "%s:\n", g_HostState.m_levelName );
  773. #ifdef VPROF_ENABLED
  774. CVProfile *pProf = &g_VProfCurrentProfile;
  775. int prefixLen = V_strlen( "TexGroup_Global_" );
  776. float total = 0.0f;
  777. for ( int i=0; i < pProf->GetNumCounters(); i++ )
  778. {
  779. if ( pProf->GetCounterGroup( i ) == COUNTER_GROUP_TEXTURE_GLOBAL )
  780. {
  781. // The counters are in bytes and the panel is all in kilobytes.
  782. float value = pProf->GetCounterValue( i ) * ( 1.0f / ( 1024.0f * 1024.0f ) );
  783. total += value;
  784. const char *pName = pProf->GetCounterName( i );
  785. if ( StringHasPrefix( pName, "TexGroup_Global_" ) )
  786. {
  787. pName += prefixLen;
  788. }
  789. g_pFileSystem->FPrintf( fp, "%s: %0.3fMB\n", pName, value );
  790. }
  791. }
  792. g_pFileSystem->FPrintf( fp, "vidmem total: %0.3fMB\n", total );
  793. #endif
  794. #if 0
  795. g_pFileSystem->FPrintf( fp, "hunk total: %0.3fMB\n", Cache_TotalUsed() * ( 1.0f / ( 1024.0f * 1024.0f ) ) );
  796. g_pFileSystem->FPrintf( fp, "hunk sound: %0.3fMB\n", Cache_TotalUsed_Sound() * ( 1.0f / ( 1024.0f * 1024.0f ) ) );
  797. g_pFileSystem->FPrintf( fp, "hunk models: %0.3fMB\n", Cache_TotalUsed_Models() * ( 1.0f / ( 1024.0f * 1024.0f ) ) );
  798. #endif
  799. g_pFileSystem->FPrintf( fp, "---------------------------------\n" );
  800. g_pFileSystem->Close( fp );
  801. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" );
  802. }
  803. #endif
  804. #ifndef DEDICATED
  805. if ( !sv.IsDedicated() )
  806. {
  807. ClientDLL_GameInit();
  808. }
  809. #endif
  810. }
  811. void CHostState::OnClientDisconnected()
  812. {
  813. materials->OnDebugEvent( "CHostState::OnClientDisconnected" );
  814. #ifndef DEDICATED
  815. if ( !sv.IsDedicated() )
  816. {
  817. ClientDLL_GameShutdown();
  818. }
  819. #endif
  820. }
  821. // Determine if this is a valid game
  822. static bool Host_ValidGame( void )
  823. {
  824. return true;
  825. }