Team Fortress 2 Source Code as on 22/4/2020
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.

3140 lines
85 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "client_pch.h"
  10. #include "sound.h"
  11. #include <inetchannel.h>
  12. #include "checksum_engine.h"
  13. #include "con_nprint.h"
  14. #include "r_local.h"
  15. #include "gl_lightmap.h"
  16. #include "console.h"
  17. #include "traceinit.h"
  18. #include "cl_demo.h"
  19. #include "cdll_engine_int.h"
  20. #include "debugoverlay.h"
  21. #include "filesystem_engine.h"
  22. #include "icliententity.h"
  23. #include "dt_recv_eng.h"
  24. #include "vgui_baseui_interface.h"
  25. #include "testscriptmgr.h"
  26. #include <tier0/vprof.h>
  27. #include <proto_oob.h>
  28. #include "materialsystem/imaterialsystemhardwareconfig.h"
  29. #include "gl_matsysiface.h"
  30. #include "staticpropmgr.h"
  31. #include "ispatialpartitioninternal.h"
  32. #include "cbenchmark.h"
  33. #include "vox.h"
  34. #include "LocalNetworkBackdoor.h"
  35. #include <tier0/icommandline.h>
  36. #include "GameEventManager.h"
  37. #include "host_saverestore.h"
  38. #include "ivideomode.h"
  39. #include "host_phonehome.h"
  40. #include "decal.h"
  41. #include "sv_rcon.h"
  42. #include "cl_rcon.h"
  43. #include "vgui_baseui_interface.h"
  44. #include "snd_audio_source.h"
  45. #include "iregistry.h"
  46. #include "sys.h"
  47. #include <vstdlib/random.h>
  48. #include "tier0/etwprof.h"
  49. #include "tier0/vcrmode.h"
  50. #include "sys_dll.h"
  51. #include "video/ivideoservices.h"
  52. #include "cl_steamauth.h"
  53. #include "filesystem/IQueuedLoader.h"
  54. #include "tier2/tier2.h"
  55. #include "host_state.h"
  56. #include "enginethreads.h"
  57. #include "vgui/ISystem.h"
  58. #include "pure_server.h"
  59. #include "SoundEmitterSystem/isoundemittersystembase.h"
  60. #include "LoadScreenUpdate.h"
  61. #include "tier0/systeminformation.h"
  62. #include "steam/steam_api.h"
  63. #include "SourceAppInfo.h"
  64. #include "cl_steamauth.h"
  65. #include "sv_steamauth.h"
  66. #include "engine/ivmodelinfo.h"
  67. #ifdef _X360
  68. #include "xbox/xbox_launch.h"
  69. #endif
  70. #if defined( REPLAY_ENABLED )
  71. #include "replay_internal.h"
  72. #endif
  73. #include "igame.h"
  74. // memdbgon must be the last include file in a .cpp file!!!
  75. #include "tier0/memdbgon.h"
  76. extern IVEngineClient *engineClient;
  77. void R_UnloadSkys( void );
  78. void CL_ResetEntityBits( void );
  79. void EngineTool_UpdateScreenshot();
  80. void WriteConfig_f( ConVar *var, const char *pOldString );
  81. // If we get more than 250 messages in the incoming buffer queue, dump any above this #
  82. #define MAX_INCOMING_MESSAGES 250
  83. // Size of command send buffer
  84. #define MAX_CMD_BUFFER 4000
  85. CGlobalVarsBase g_ClientGlobalVariables( true );
  86. IVideoRecorder *g_pVideoRecorder = NULL;
  87. extern ConVar rcon_password;
  88. extern ConVar host_framerate;
  89. extern ConVar cl_clanid;
  90. ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Highest unlocked game chapter." );
  91. static ConVar tv_nochat ( "tv_nochat", "0", FCVAR_ARCHIVE | FCVAR_USERINFO, "Don't receive chat messages from other SourceTV spectators" );
  92. static ConVar cl_LocalNetworkBackdoor( "cl_localnetworkbackdoor", "1", 0, "Enable network optimizations for single player games." );
  93. static ConVar cl_ignorepackets( "cl_ignorepackets", "0", FCVAR_CHEAT, "Force client to ignore packets (for debugging)." );
  94. static ConVar cl_playback_screenshots( "cl_playback_screenshots", "0", 0, "Allows the client to playback screenshot and jpeg commands in demos." );
  95. #if defined( STAGING_ONLY ) || defined( _DEBUG )
  96. static ConVar cl_block_usercommand( "cl_block_usercommand", "0", FCVAR_CHEAT, "Force client to not send usercommand (for debugging)." );
  97. #endif // STAGING_ONLY || _DEBUG
  98. ConVar dev_loadtime_map_start( "dev_loadtime_map_start", "0.0", FCVAR_HIDDEN);
  99. ConVar dev_loadtime_map_elapsed( "dev_loadtime_map_elapsed", "0.0", FCVAR_HIDDEN );
  100. MovieInfo_t cl_movieinfo;
  101. // FIXME: put these on hunk?
  102. dlight_t cl_dlights[MAX_DLIGHTS];
  103. dlight_t cl_elights[MAX_ELIGHTS];
  104. CFastPointLeafNum g_DLightLeafAccessors[MAX_DLIGHTS];
  105. CFastPointLeafNum g_ELightLeafAccessors[MAX_ELIGHTS];
  106. bool cl_takesnapshot = false;
  107. static bool cl_takejpeg = false;
  108. static bool cl_takesnapshot_internal = false;
  109. static int cl_jpegquality = DEFAULT_JPEG_QUALITY;
  110. static ConVar jpeg_quality( "jpeg_quality", "90", 0, "jpeg screenshot quality." );
  111. static int cl_snapshotnum = 0;
  112. static char cl_snapshotname[MAX_OSPATH];
  113. static char cl_snapshot_subdirname[MAX_OSPATH];
  114. // Must match game .dll definition
  115. // HACK HACK FOR E3 -- Remove this after E3
  116. #define HIDEHUD_ALL ( 1<<2 )
  117. void PhonemeMP3Shutdown( void );
  118. struct ResourceLocker
  119. {
  120. ResourceLocker()
  121. {
  122. g_pFileSystem->AsyncFinishAll();
  123. g_pFileSystem->AsyncSuspend();
  124. // Need to temporarily disable queued material system, then lock it
  125. m_QMS = Host_AllowQueuedMaterialSystem( false );
  126. m_MatLock = g_pMaterialSystem->Lock();
  127. }
  128. ~ResourceLocker()
  129. {
  130. // Restore QMS
  131. materials->Unlock( m_MatLock );
  132. Host_AllowQueuedMaterialSystem( m_QMS );
  133. g_pFileSystem->AsyncResume();
  134. // ??? What? Why?
  135. //// Need to purge cached materials due to a sv_pure change.
  136. //g_pMaterialSystem->UncacheAllMaterials();
  137. }
  138. bool m_QMS;
  139. MaterialLock_t m_MatLock;
  140. };
  141. // Reloads a list of files if they are still loaded
  142. void CL_ReloadFilesInList( IFileList *pFilesToReload )
  143. {
  144. if ( !pFilesToReload )
  145. {
  146. return;
  147. }
  148. ResourceLocker crashPreventer;
  149. // Handle materials..
  150. materials->ReloadFilesInList( pFilesToReload );
  151. // Handle models.. NOTE: this MUST come after materials->ReloadFilesInList because the
  152. // models need to know which materials got flushed.
  153. modelloader->ReloadFilesInList( pFilesToReload );
  154. S_ReloadFilesInList( pFilesToReload );
  155. // Let the client at it (for particles)
  156. if ( g_ClientDLL )
  157. {
  158. g_ClientDLL->ReloadFilesInList( pFilesToReload );
  159. }
  160. }
  161. void CL_HandlePureServerWhitelist( CPureServerWhitelist *pWhitelist, /* out */ IFileList *&pFilesToReload )
  162. {
  163. // Free the old whitelist and get the new one.
  164. if ( cl.m_pPureServerWhitelist )
  165. cl.m_pPureServerWhitelist->Release();
  166. cl.m_pPureServerWhitelist = pWhitelist;
  167. if ( cl.m_pPureServerWhitelist )
  168. cl.m_pPureServerWhitelist->AddRef();
  169. g_pFileSystem->RegisterFileWhitelist( pWhitelist, &pFilesToReload );
  170. // Now that we've flushed any files that shouldn't have been on disk, we should have a CRC
  171. // set that we can check with the server.
  172. cl.m_bCheckCRCsWithServer = true;
  173. }
  174. void PrintSvPureWhitelistClassification( const CPureServerWhitelist *pWhiteList )
  175. {
  176. if ( pWhiteList == NULL )
  177. {
  178. Msg( "The server is using sv_pure -1 (no file checking).\n" );
  179. return;
  180. }
  181. // Load up the default whitelist
  182. CPureServerWhitelist *pStandardList = CPureServerWhitelist::Create( g_pFullFileSystem );
  183. pStandardList->Load( 0 );
  184. if ( *pStandardList == *pWhiteList )
  185. {
  186. Msg( "The server is using sv_pure 0. (Enforcing consistency for select files only)\n" );
  187. }
  188. else
  189. {
  190. pStandardList->Load( 2 );
  191. if ( *pStandardList == *pWhiteList )
  192. {
  193. Msg( "The server is using sv_pure 2. (Fully pure)\n" );
  194. }
  195. else
  196. {
  197. Msg( "The server is using sv_pure 1. (Custom pure server rules.)\n" );
  198. }
  199. }
  200. pStandardList->Release();
  201. }
  202. void CL_PrintWhitelistInfo()
  203. {
  204. PrintSvPureWhitelistClassification( cl.m_pPureServerWhitelist );
  205. if ( cl.m_pPureServerWhitelist )
  206. {
  207. cl.m_pPureServerWhitelist->PrintWhitelistContents();
  208. }
  209. }
  210. // Console command to force a whitelist on the system.
  211. #ifdef _DEBUG
  212. void whitelist_f( const CCommand &args )
  213. {
  214. int pureLevel = 2;
  215. if ( args.ArgC() == 2 )
  216. {
  217. pureLevel = atoi( args[1] );
  218. }
  219. else
  220. {
  221. Warning( "Whitelist 0, 1, or 2\n" );
  222. }
  223. if ( pureLevel == 0 )
  224. {
  225. Warning( "whitelist 0: CL_HandlePureServerWhitelist( NULL )\n" );
  226. IFileList *pFilesToReload = NULL;
  227. CL_HandlePureServerWhitelist( NULL, pFilesToReload );
  228. CL_ReloadFilesInList( pFilesToReload );
  229. }
  230. else
  231. {
  232. CPureServerWhitelist *pWhitelist = CPureServerWhitelist::Create( g_pFileSystem );
  233. pWhitelist->Load( pureLevel == 1 );
  234. IFileList *pFilesToReload = NULL;
  235. CL_HandlePureServerWhitelist( pWhitelist, pFilesToReload );
  236. CL_ReloadFilesInList( pFilesToReload );
  237. pWhitelist->Release();
  238. }
  239. }
  240. ConCommand whitelist( "whitelist", whitelist_f );
  241. #endif
  242. const CPrecacheUserData* CL_GetPrecacheUserData( INetworkStringTable *table, int index )
  243. {
  244. int testLength;
  245. const CPrecacheUserData *data = ( CPrecacheUserData * )table->GetStringUserData( index, &testLength );
  246. if ( data )
  247. {
  248. ErrorIfNot(
  249. testLength == sizeof( *data ),
  250. ("CL_GetPrecacheUserData(%d,%d) - length (%d) invalid.", table->GetTableId(), index, testLength)
  251. );
  252. }
  253. return data;
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Purpose: setup the demo flag, split from CL_IsHL2Demo so CL_IsHL2Demo can be inline
  257. //-----------------------------------------------------------------------------
  258. static bool s_bIsHL2Demo = false;
  259. void CL_InitHL2DemoFlag()
  260. {
  261. #if defined(_X360)
  262. s_bIsHL2Demo = false;
  263. #else
  264. static bool initialized = false;
  265. if ( !initialized )
  266. {
  267. if ( Steam3Client().SteamApps() && !Q_stricmp( COM_GetModDirectory(), "hl2" ) )
  268. {
  269. initialized = true;
  270. // if user didn't buy HL2 yet, this must be the free demo
  271. if ( VCRGetMode() != VCR_Playback )
  272. {
  273. s_bIsHL2Demo = !Steam3Client().SteamApps()->BIsSubscribedApp( GetAppSteamAppId( k_App_HL2 ) );
  274. }
  275. #if !defined( NO_VCR )
  276. VCRGenericValue( "e", &s_bIsHL2Demo, sizeof( s_bIsHL2Demo ) );
  277. #endif
  278. }
  279. if ( !Q_stricmp( COM_GetModDirectory(), "hl2" ) && CommandLine()->CheckParm( "-demo" ) )
  280. {
  281. s_bIsHL2Demo = true;
  282. }
  283. }
  284. #endif
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Returns true if the user is playing the HL2 Demo (rather than the full game)
  288. //-----------------------------------------------------------------------------
  289. bool CL_IsHL2Demo()
  290. {
  291. CL_InitHL2DemoFlag();
  292. return s_bIsHL2Demo;
  293. }
  294. static bool s_bIsPortalDemo = false;
  295. void CL_InitPortalDemoFlag()
  296. {
  297. #if defined(_X360)
  298. s_bIsPortalDemo = false;
  299. #else
  300. static bool initialized = false;
  301. if ( !initialized )
  302. {
  303. if ( Steam3Client().SteamApps() && !Q_stricmp( COM_GetModDirectory(), "portal" ) )
  304. {
  305. initialized = true;
  306. // if user didn't buy Portal yet, this must be the free demo
  307. if ( VCRGetMode() != VCR_Playback )
  308. {
  309. s_bIsPortalDemo = !Steam3Client().SteamApps()->BIsSubscribedApp( GetAppSteamAppId( k_App_PORTAL ) );
  310. }
  311. #if !defined( NO_VCR )
  312. VCRGenericValue( "e", &s_bIsPortalDemo, sizeof( s_bIsPortalDemo ) );
  313. #endif
  314. }
  315. if ( !Q_stricmp( COM_GetModDirectory(), "portal" ) && CommandLine()->CheckParm( "-demo" ) )
  316. {
  317. s_bIsPortalDemo = true;
  318. }
  319. }
  320. #endif
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Returns true if the user is playing the Portal Demo (rather than the full game)
  324. //-----------------------------------------------------------------------------
  325. bool CL_IsPortalDemo()
  326. {
  327. CL_InitPortalDemoFlag();
  328. return s_bIsPortalDemo;
  329. }
  330. #ifdef _XBOX
  331. extern void Host_WriteConfiguration( const char *dirname, const char *filename );
  332. //-----------------------------------------------------------------------------
  333. // Convar callback to write the user configuration
  334. //-----------------------------------------------------------------------------
  335. void WriteConfig_f( ConVar *var, const char *pOldString )
  336. {
  337. Host_WriteConfiguration( "xboxuser.cfg" );
  338. }
  339. #endif
  340. //-----------------------------------------------------------------------------
  341. // Purpose: If the client is in the process of connecting and the cl.signon hits
  342. // is complete, make sure the client thinks its totally connected.
  343. //-----------------------------------------------------------------------------
  344. void CL_CheckClientState( void )
  345. {
  346. // Setup the local network backdoor (we do this each frame so it can be toggled on and off).
  347. bool useBackdoor = cl_LocalNetworkBackdoor.GetInt() &&
  348. (cl.m_NetChannel ? cl.m_NetChannel->IsLoopback() : false) &&
  349. sv.IsActive() &&
  350. !demorecorder->IsRecording() &&
  351. !demoplayer->IsPlayingBack() &&
  352. Host_IsSinglePlayerGame();
  353. CL_SetupLocalNetworkBackDoor( useBackdoor );
  354. }
  355. //-----------------------------------------------------------------------------
  356. // bool CL_CheckCRCs( const char *pszMap )
  357. //-----------------------------------------------------------------------------
  358. bool CL_CheckCRCs( const char *pszMap )
  359. {
  360. CRC32_t mapCRC; // If this is the worldmap, CRC against server's map
  361. MD5Value_t mapMD5;
  362. V_memset( mapMD5.bits, 0, MD5_DIGEST_LENGTH );
  363. // Don't verify CRC if we are running a local server (i.e., we are playing single player, or we are the server in multiplay
  364. if ( sv.IsActive() ) // Single player
  365. return true;
  366. if ( IsX360() )
  367. {
  368. return true;
  369. }
  370. bool couldHash = false;
  371. if ( g_ClientGlobalVariables.network_protocol > PROTOCOL_VERSION_17 )
  372. {
  373. couldHash = MD5_MapFile( &mapMD5, pszMap );
  374. }
  375. else
  376. {
  377. CRC32_Init(&mapCRC);
  378. couldHash = CRC_MapFile( &mapCRC, pszMap );
  379. }
  380. if (!couldHash )
  381. {
  382. // Does the file exist?
  383. FileHandle_t fp = 0;
  384. int nSize = -1;
  385. nSize = COM_OpenFile( pszMap, &fp );
  386. if ( fp )
  387. g_pFileSystem->Close( fp );
  388. if ( nSize != -1 )
  389. {
  390. COM_ExplainDisconnection( true, "Couldn't CRC map %s, disconnecting\n", pszMap);
  391. Host_Error( "Bad map" );
  392. }
  393. else
  394. {
  395. COM_ExplainDisconnection( true, "Missing map %s, disconnecting\n", pszMap);
  396. Host_Error( "Map is missing" );
  397. }
  398. return false;
  399. }
  400. bool hashValid = false;
  401. if ( g_ClientGlobalVariables.network_protocol > PROTOCOL_VERSION_17 )
  402. {
  403. hashValid = MD5_Compare( cl.serverMD5, mapMD5 );
  404. }
  405. // Hacked map
  406. if ( !hashValid && !demoplayer->IsPlayingBack())
  407. {
  408. if ( IsX360() )
  409. {
  410. Warning( "Disconnect: BSP CRC failed!\n" );
  411. }
  412. COM_ExplainDisconnection( true, "Your map [%s] differs from the server's.\n", pszMap );
  413. Host_Error( "Client's map differs from the server's" );
  414. return false;
  415. }
  416. return true;
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose:
  420. // Input : nMaxClients -
  421. //-----------------------------------------------------------------------------
  422. void CL_ReallocateDynamicData( int maxclients )
  423. {
  424. Assert( entitylist );
  425. if ( entitylist )
  426. {
  427. entitylist->SetMaxEntities( MAX_EDICTS );
  428. }
  429. }
  430. /*
  431. =================
  432. CL_ReadPackets
  433. Updates the local time and reads/handles messages on client net connection.
  434. =================
  435. */
  436. void CL_ReadPackets ( bool bFinalTick )
  437. {
  438. VPROF_BUDGET( "CL_ReadPackets", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  439. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  440. if ( !Host_ShouldRun() )
  441. return;
  442. // update client times/tick
  443. cl.oldtickcount = cl.GetServerTickCount();
  444. if ( !cl.IsPaused() )
  445. {
  446. cl.SetClientTickCount( cl.GetClientTickCount() + 1 );
  447. // While clock correction is off, we have the old behavior of matching the client and server clocks.
  448. if ( !CClockDriftMgr::IsClockCorrectionEnabled() )
  449. cl.SetServerTickCount( cl.GetClientTickCount() );
  450. g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
  451. g_ClientGlobalVariables.curtime = cl.GetTime();
  452. }
  453. // 0 or tick_rate if simulating
  454. g_ClientGlobalVariables.frametime = cl.GetFrameTime();
  455. // read packets, if any in queue
  456. if ( demoplayer->IsPlayingBack() && cl.m_NetChannel )
  457. {
  458. tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "ReadPacket" );
  459. // process data from demo file
  460. cl.m_NetChannel->ProcessPlayback();
  461. }
  462. else
  463. {
  464. if ( !cl_ignorepackets.GetInt() )
  465. {
  466. tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "ProcessSocket" );
  467. // process data from net socket
  468. NET_ProcessSocket( NS_CLIENT, &cl );
  469. }
  470. }
  471. // check timeout, but not if running _DEBUG engine
  472. #if !defined( _DEBUG )
  473. // Only check on final frame because that's when the server might send us a packet in single player. This avoids
  474. // a bug where if you sit in the game code in the debugger then you get a timeout here on resuming the engine
  475. // because the timestep is > 1 tick because of the debugging delay but the server hasn't sent the next packet yet. ywb 9/5/03
  476. if ( (cl.m_NetChannel?cl.m_NetChannel->IsTimedOut():false) &&
  477. bFinalTick &&
  478. !demoplayer->IsPlayingBack() &&
  479. cl.IsConnected() )
  480. {
  481. ConMsg ("\nServer connection timed out.\n");
  482. // Show the vgui dialog on timeout
  483. COM_ExplainDisconnection( false, "Lost connection to server.");
  484. if ( IsPC() )
  485. {
  486. EngineVGui()->ShowErrorMessage();
  487. }
  488. Host_Disconnect( true, "Lost connection" );
  489. return;
  490. }
  491. #endif
  492. }
  493. //-----------------------------------------------------------------------------
  494. // Purpose:
  495. //-----------------------------------------------------------------------------
  496. void CL_ClearState ( void )
  497. {
  498. // clear out the current whitelist
  499. IFileList *pFilesToReload = NULL;
  500. CL_HandlePureServerWhitelist( NULL, pFilesToReload );
  501. CL_ReloadFilesInList( pFilesToReload );
  502. CL_ResetEntityBits();
  503. R_UnloadSkys();
  504. // clear decal index directories
  505. Decal_Init();
  506. StaticPropMgr()->LevelShutdownClient();
  507. // shutdown this level in the client DLL
  508. if ( g_ClientDLL )
  509. {
  510. if ( host_state.worldmodel )
  511. {
  512. char mapname[256];
  513. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  514. phonehome->Message( IPhoneHome::PHONE_MSG_MAPEND, mapname );
  515. }
  516. audiosourcecache->LevelShutdown();
  517. g_ClientDLL->LevelShutdown();
  518. }
  519. R_LevelShutdown();
  520. if ( IsX360() )
  521. {
  522. // Reset material system temporary memory (frees up memory for map loading)
  523. bool bOnLevelShutdown = true;
  524. materials->ResetTempHWMemory( bOnLevelShutdown );
  525. }
  526. if ( g_pLocalNetworkBackdoor )
  527. g_pLocalNetworkBackdoor->ClearState();
  528. // clear other arrays
  529. memset (cl_dlights, 0, sizeof(cl_dlights));
  530. memset (cl_elights, 0, sizeof(cl_elights));
  531. // Wipe the hunk ( unless the server is active )
  532. Host_FreeStateAndWorld( false );
  533. Host_FreeToLowMark( false );
  534. PhonemeMP3Shutdown();
  535. // Wipe the remainder of the structure.
  536. cl.Clear();
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Purpose: Used for sorting sounds
  540. // Input : &sound1 -
  541. // &sound2 -
  542. // Output : static bool
  543. //-----------------------------------------------------------------------------
  544. static bool CL_SoundMessageLessFunc( SoundInfo_t const &sound1, SoundInfo_t const &sound2 )
  545. {
  546. return sound1.nSequenceNumber < sound2.nSequenceNumber;
  547. }
  548. static CUtlRBTree< SoundInfo_t, int > g_SoundMessages( 0, 0, CL_SoundMessageLessFunc );
  549. extern ConVar snd_show;
  550. //-----------------------------------------------------------------------------
  551. // Purpose: Add sound to queue
  552. // Input : sound -
  553. //-----------------------------------------------------------------------------
  554. void CL_AddSound( const SoundInfo_t &sound )
  555. {
  556. g_SoundMessages.Insert( sound );
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Purpose: Play sound packet
  560. // Input : sound -
  561. //-----------------------------------------------------------------------------
  562. void CL_DispatchSound( const SoundInfo_t &sound )
  563. {
  564. int nSoundNum = sound.nSoundNum;
  565. CSfxTable *pSfx;
  566. char name[ MAX_QPATH ];
  567. name[ 0 ] = 0;
  568. if ( sound.bIsSentence )
  569. {
  570. // make dummy sfx for sentences
  571. const char *pSentenceName = VOX_SentenceNameFromIndex( sound.nSoundNum );
  572. if ( !pSentenceName )
  573. {
  574. pSentenceName = "";
  575. }
  576. V_snprintf( name, sizeof( name ), "%c%s", CHAR_SENTENCE, pSentenceName );
  577. pSfx = S_DummySfx( name );
  578. }
  579. else
  580. {
  581. V_strncpy( name, cl.GetSoundName( sound.nSoundNum ), sizeof( name ) );
  582. const char *pchTranslatedName = g_ClientDLL->TranslateEffectForVisionFilter( "sounds", name );
  583. if ( V_strcmp( pchTranslatedName, name ) != 0 )
  584. {
  585. V_strncpy( name, pchTranslatedName, sizeof( name ) );
  586. nSoundNum = cl.LookupSoundIndex( name );
  587. }
  588. pSfx = cl.GetSound( nSoundNum );
  589. }
  590. if ( snd_show.GetInt() >= 2 )
  591. {
  592. DevMsg( "%i (seq %i) %s : src %d : ch %d : %d dB : vol %.2f : time %.3f (%.4f delay) @%.1f %.1f %.1f\n",
  593. host_framecount,
  594. sound.nSequenceNumber,
  595. name,
  596. sound.nEntityIndex,
  597. sound.nChannel,
  598. sound.Soundlevel,
  599. sound.fVolume,
  600. cl.GetTime(),
  601. sound.fDelay,
  602. sound.vOrigin.x,
  603. sound.vOrigin.y,
  604. sound.vOrigin.z );
  605. }
  606. StartSoundParams_t params;
  607. params.staticsound = (sound.nChannel == CHAN_STATIC) ? true : false;
  608. params.soundsource = sound.nEntityIndex;
  609. params.entchannel = params.staticsound ? CHAN_STATIC : sound.nChannel;
  610. params.pSfx = pSfx;
  611. params.origin = sound.vOrigin;
  612. params.fvol = sound.fVolume;
  613. params.soundlevel = sound.Soundlevel;
  614. params.flags = sound.nFlags;
  615. params.pitch = sound.nPitch;
  616. params.specialdsp = sound.nSpecialDSP;
  617. params.fromserver = true;
  618. params.delay = sound.fDelay;
  619. // we always want to do this when this flag is set - even if the delay is zero we need to precisely
  620. // schedule this sound
  621. if ( sound.nFlags & SND_DELAY )
  622. {
  623. // anything adjusted less than 100ms forward was probably scheduled this frame
  624. if ( sound.fDelay > -0.100f )
  625. {
  626. float soundtime = cl.m_flLastServerTickTime + sound.fDelay;
  627. // this adjusts for host_thread_mode or any other cases where we're running more than one
  628. // tick at a time, but we get network updates on the first tick
  629. soundtime -= ((g_ClientGlobalVariables.simTicksThisFrame-1) * host_state.interval_per_tick);
  630. // this sound was networked over from the server, use server clock
  631. params.delay = S_ComputeDelayForSoundtime( soundtime, CLOCK_SYNC_SERVER );
  632. #if 0
  633. static float lastSoundTime = 0;
  634. Msg("[%.3f] Play %s at %.3f %.1fsms delay\n", soundtime - lastSoundTime, name, soundtime, params.delay * 1000.0f );
  635. lastSoundTime = soundtime;
  636. #endif
  637. if ( params.delay <= 0 )
  638. {
  639. // leave a little delay to flag the channel in the low-level sound system
  640. params.delay = 1e-6f;
  641. }
  642. }
  643. else
  644. {
  645. params.delay = sound.fDelay;
  646. }
  647. }
  648. params.speakerentity = sound.nSpeakerEntity;
  649. // Give the client DLL a chance to run arbitrary code to affect the sound parameters before we
  650. // play.
  651. g_ClientDLL->ClientAdjustStartSoundParams( params );
  652. if ( params.staticsound )
  653. {
  654. S_StartSound( params );
  655. }
  656. else
  657. {
  658. // Don't actually play non-static sounds if playing a demo and skipping ahead
  659. // but always stop sounds
  660. if ( demoplayer->IsSkipping() && !(sound.nFlags&SND_STOP) )
  661. {
  662. return;
  663. }
  664. S_StartSound( params );
  665. }
  666. }
  667. //-----------------------------------------------------------------------------
  668. // Purpose: Called after reading network messages to play sounds encoded in the network packet
  669. //-----------------------------------------------------------------------------
  670. void CL_DispatchSounds( void )
  671. {
  672. int i;
  673. // Walk list in sequence order
  674. i = g_SoundMessages.FirstInorder();
  675. while ( i != g_SoundMessages.InvalidIndex() )
  676. {
  677. SoundInfo_t const *msg = &g_SoundMessages[ i ];
  678. Assert( msg );
  679. if ( msg )
  680. {
  681. // Play the sound
  682. CL_DispatchSound( *msg );
  683. }
  684. i = g_SoundMessages.NextInorder( i );
  685. }
  686. // Reset the queue each time we empty it!!!
  687. g_SoundMessages.RemoveAll();
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Retry last connection (e.g., after we enter a password)
  691. //-----------------------------------------------------------------------------
  692. void CL_Retry()
  693. {
  694. if ( !cl.m_szRetryAddress[ 0 ] )
  695. {
  696. ConMsg( "Can't retry, no previous connection\n" );
  697. return;
  698. }
  699. // Check that we can add the two execution markers
  700. bool bCanAddExecutionMarkers = Cbuf_HasRoomForExecutionMarkers( 2 );
  701. ConMsg( "Commencing connection retry to %s\n", cl.m_szRetryAddress );
  702. // We need to temporarily disable this execution marker so the connect command succeeds if it was executed by the server.
  703. // We would still need this even if we called CL_Connect directly because the connect process may execute commands which we want to succeed.
  704. const char *pszCommand = va( "connect %s %s\n", cl.m_szRetryAddress, cl.m_sRetrySourceTag.String() );
  705. if ( cl.m_bRestrictServerCommands && bCanAddExecutionMarkers )
  706. Cbuf_AddTextWithMarkers( eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE, pszCommand, eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE );
  707. else
  708. Cbuf_AddText( pszCommand );
  709. }
  710. CON_COMMAND_F( retry, "Retry connection to last server.", FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENTCMD_CAN_EXECUTE )
  711. {
  712. CL_Retry();
  713. }
  714. /*
  715. =====================
  716. CL_Connect_f
  717. User command to connect to server
  718. =====================
  719. */
  720. void CL_Connect( const char *address, const char *pszSourceTag )
  721. {
  722. // If it's not a single player connection to "localhost", initialize networking & stop listenserver
  723. if ( Q_strncmp( address, "localhost", 9 ) )
  724. {
  725. Host_Disconnect(false);
  726. // allow remote
  727. NET_SetMutiplayer( true );
  728. // start progress bar immediately for remote connection
  729. EngineVGui()->EnabledProgressBarForNextLoad();
  730. SCR_BeginLoadingPlaque();
  731. EngineVGui()->UpdateProgressBar(PROGRESS_BEGINCONNECT);
  732. }
  733. else
  734. {
  735. // we are connecting/reconnecting to local game
  736. // so don't stop listenserver
  737. cl.Disconnect( "Connecting to local host", false );
  738. }
  739. // This happens as part of the load process anyway, but on slower systems it causes the server to timeout the
  740. // connection. Use the opportunity to flush anything before starting a new connection.
  741. UpdateMaterialSystemConfig();
  742. cl.Connect( address, pszSourceTag );
  743. // Reset error conditions
  744. gfExtendedError = false;
  745. }
  746. CON_COMMAND_F( connect, "Connect to specified server.", FCVAR_DONTRECORD )
  747. {
  748. // Default command processing considers ':' a command separator,
  749. // and we donly want spaces to count. So we'll need to re-split the arg string
  750. CUtlVector<char*> vecArgs;
  751. V_SplitString( args.ArgS(), " ", vecArgs );
  752. // How many arguments?
  753. if ( vecArgs.Count() == 1 )
  754. {
  755. CL_Connect( vecArgs[0], "" );
  756. }
  757. else if ( vecArgs.Count() == 2 )
  758. {
  759. CL_Connect( vecArgs[0], vecArgs[1] );
  760. }
  761. else
  762. {
  763. ConMsg( "Usage: connect <server>\n" );
  764. }
  765. vecArgs.PurgeAndDeleteElements();
  766. }
  767. CON_COMMAND_F( redirect, "Redirect client to specified server.", FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE )
  768. {
  769. if ( !CBaseClientState::ConnectMethodAllowsRedirects() )
  770. {
  771. ConMsg( "redirect: Current connection method does not allow silent redirects.\n");
  772. return;
  773. }
  774. // Default command processing considers ':' a command separator,
  775. // and we donly want spaces to count. So we'll need to re-split the arg string
  776. CUtlVector<char*> vecArgs;
  777. V_SplitString( args.ArgS(), " ", vecArgs );
  778. if ( vecArgs.Count() == 1 )
  779. {
  780. CL_Connect( vecArgs[0], "redirect" );
  781. }
  782. else
  783. {
  784. ConMsg( "Usage: redirect <server>\n" );
  785. }
  786. vecArgs.PurgeAndDeleteElements();
  787. }
  788. //-----------------------------------------------------------------------------
  789. // Takes the map name, strips path and extension
  790. //-----------------------------------------------------------------------------
  791. void CL_SetupMapName( const char* pName, char* pFixedName, int maxlen )
  792. {
  793. const char* pSlash = strrchr( pName, '\\' );
  794. const char* pSlash2 = strrchr( pName, '/' );
  795. if (pSlash2 > pSlash)
  796. pSlash = pSlash2;
  797. if (pSlash)
  798. ++pSlash;
  799. else
  800. pSlash = pName;
  801. Q_strncpy( pFixedName, pSlash, maxlen );
  802. char* pExt = strchr( pFixedName, '.' );
  803. if (pExt)
  804. *pExt = 0;
  805. }
  806. CPureServerWhitelist* CL_LoadWhitelist( INetworkStringTable *pTable, const char *pName )
  807. {
  808. // If there is no entry for the pure server whitelist, then sv_pure is off and the client can do whatever it wants.
  809. int iString = pTable->FindStringIndex( pName );
  810. if ( iString == INVALID_STRING_INDEX )
  811. return NULL;
  812. int dataLen;
  813. const void *pData = pTable->GetStringUserData( iString, &dataLen );
  814. if ( pData )
  815. {
  816. CUtlBuffer buf( pData, dataLen, CUtlBuffer::READ_ONLY );
  817. CPureServerWhitelist *pWhitelist = CPureServerWhitelist::Create( g_pFullFileSystem );
  818. pWhitelist->Decode( buf );
  819. return pWhitelist;
  820. }
  821. else
  822. {
  823. return NULL;
  824. }
  825. }
  826. void CL_CheckForPureServerWhitelist( /* out */ IFileList *&pFilesToReload )
  827. {
  828. #ifdef DISABLE_PURE_SERVER_STUFF
  829. return;
  830. #endif
  831. // Don't do sv_pure stuff in SP games or HLTV/replay
  832. if ( cl.m_nMaxClients <= 1 || cl.ishltv || demoplayer->IsPlayingBack()
  833. #ifdef REPLAY_ENABLED
  834. || cl.isreplay
  835. #endif // ifdef REPLAY_ENABLED
  836. )
  837. return;
  838. CPureServerWhitelist *pWhitelist = NULL;
  839. if ( cl.m_pServerStartupTable )
  840. pWhitelist = CL_LoadWhitelist( cl.m_pServerStartupTable, "PureServerWhitelist" );
  841. PrintSvPureWhitelistClassification( pWhitelist );
  842. CL_HandlePureServerWhitelist( pWhitelist, pFilesToReload );
  843. if ( pWhitelist )
  844. {
  845. pWhitelist->Release();
  846. }
  847. }
  848. int CL_GetServerQueryPort()
  849. {
  850. // Yes, this is ugly getting this data out of a string table. Would be better to have it in our network protocol,
  851. // but we don't have a way to change the protocol without breaking things for people.
  852. if ( !cl.m_pServerStartupTable )
  853. return 0;
  854. int iString = cl.m_pServerStartupTable->FindStringIndex( "QueryPort" );
  855. if ( iString == INVALID_STRING_INDEX )
  856. return 0;
  857. int dataLen;
  858. const void *pData = cl.m_pServerStartupTable->GetStringUserData( iString, &dataLen );
  859. if ( pData && dataLen == sizeof( int ) )
  860. return *((const int*)pData);
  861. else
  862. return 0;
  863. }
  864. /*
  865. ==================
  866. CL_RegisterResources
  867. Clean up and move to next part of sequence.
  868. ==================
  869. */
  870. void CL_RegisterResources( void )
  871. {
  872. // All done precaching.
  873. host_state.SetWorldModel( cl.GetModel( 1 ) );
  874. if ( !host_state.worldmodel )
  875. {
  876. Host_Error( "CL_RegisterResources: host_state.worldmodel/cl.GetModel( 1 )==NULL\n" );
  877. }
  878. // Force main window to repaint... (only does something if running shaderapi
  879. videomode->InvalidateWindow();
  880. }
  881. void CL_FullyConnected( void )
  882. {
  883. CETWScope timer( "CL_FullyConnected" );
  884. EngineVGui()->UpdateProgressBar( PROGRESS_FULLYCONNECTED );
  885. // This has to happen here, in phase 3, because it is in this phase
  886. // that raycasts against the world is supported (owing to the fact
  887. // that the world entity has been created by this point)
  888. StaticPropMgr()->LevelInitClient();
  889. if ( IsX360() )
  890. {
  891. // Notify the loader the end of the loading context, preloads are about to be purged
  892. g_pQueuedLoader->EndMapLoading( false );
  893. }
  894. // flush client-side dynamic models that have no refcount
  895. modelloader->FlushDynamicModels();
  896. // loading completed
  897. // can NOW safely purge unused models and their data hierarchy (materials, shaders, etc)
  898. modelloader->PurgeUnusedModels();
  899. // Purge the preload stores, oreder is critical
  900. g_pMDLCache->ShutdownPreloadData();
  901. // NOTE: purposely disabling for singleplayer, memory spike causing issues, preload's stay in
  902. // UNDONE: discard preload for TF to save memory
  903. // g_pFileSystem->DiscardPreloadData();
  904. // We initialize this list before the load, but don't perform reloads until after flushes have happened to avoid
  905. // unnecessary reloads of items that wont be used on this map.
  906. if ( cl.m_pPendingPureFileReloads )
  907. {
  908. CL_ReloadFilesInList( cl.m_pPendingPureFileReloads );
  909. cl.m_pPendingPureFileReloads->Release();
  910. cl.m_pPendingPureFileReloads = NULL;
  911. }
  912. // ***************************************************************
  913. // NO MORE PRELOAD DATA AVAILABLE PAST THIS POINT!!!
  914. // ***************************************************************
  915. g_ClientDLL->LevelInitPostEntity();
  916. // communicate to tracker that we're in a game
  917. int ip = cl.m_NetChannel->GetRemoteAddress().GetIPNetworkByteOrder();
  918. short port = cl.m_NetChannel->GetRemoteAddress().GetPort();
  919. if (!port)
  920. {
  921. ip = net_local_adr.GetIPNetworkByteOrder();
  922. port = net_local_adr.GetPort();
  923. }
  924. int iQueryPort = CL_GetServerQueryPort();
  925. EngineVGui()->NotifyOfServerConnect(com_gamedir, ip, port, iQueryPort);
  926. GetTestScriptMgr()->CheckPoint( "FinishedMapLoad" );
  927. EngineVGui()->UpdateProgressBar( PROGRESS_READYTOPLAY );
  928. if ( !IsX360() || cl.m_nMaxClients == 1 )
  929. {
  930. // Need this to persist for multiplayer respawns, 360 can't reload
  931. CM_DiscardEntityString();
  932. }
  933. g_pMDLCache->EndMapLoad();
  934. #if defined( _MEMTEST )
  935. Cbuf_AddText( "mem_dump\n" );
  936. #endif
  937. if ( developer.GetInt() > 0 )
  938. {
  939. ConDMsg( "Signon traffic \"%s\": incoming %s, outgoing %s\n",
  940. cl.m_NetChannel->GetName(),
  941. Q_pretifymem( cl.m_NetChannel->GetTotalData( FLOW_INCOMING ), 3 ),
  942. Q_pretifymem( cl.m_NetChannel->GetTotalData( FLOW_OUTGOING ), 3 ) );
  943. }
  944. if ( IsX360() )
  945. {
  946. // Reset material system temporary memory (once loading is complete), ready for in-map use
  947. bool bOnLevelShutdown = false;
  948. materials->ResetTempHWMemory( bOnLevelShutdown );
  949. }
  950. // allow normal screen updates
  951. SCR_EndLoadingPlaque();
  952. EndLoadingUpdates();
  953. // FIXME: Please oh please move this out of this spot...
  954. // It so does not belong here. Instead, we want some phase of the
  955. // client DLL where it knows its read in all entities
  956. if ( IsPC() )
  957. {
  958. int i;
  959. if( (i = CommandLine()->FindParm( "-buildcubemaps" )) != 0 )
  960. {
  961. int numIterations = 1;
  962. if( CommandLine()->ParmCount() > i + 1 )
  963. {
  964. numIterations = atoi( CommandLine()->GetParm(i+1) );
  965. }
  966. if( numIterations == 0 )
  967. {
  968. numIterations = 1;
  969. }
  970. char cmd[1024] = { 0 };
  971. V_snprintf( cmd, sizeof( cmd ), "buildcubemaps %u\nquit\n", numIterations );
  972. Cbuf_AddText( cmd );
  973. }
  974. else if( CommandLine()->FindParm( "-navanalyze" ) )
  975. {
  976. Cbuf_AddText( "nav_edit 1;nav_analyze_scripted\n" );
  977. }
  978. else if( CommandLine()->FindParm( "-navforceanalyze" ) )
  979. {
  980. Cbuf_AddText( "nav_edit 1;nav_analyze_scripted force\n" );
  981. }
  982. else if ( CommandLine()->FindParm("-exit") )
  983. {
  984. Cbuf_AddText( "quit\n" );
  985. }
  986. }
  987. // background maps are for main menu UI, QMS not needed or used, easier context
  988. if ( !engineClient->IsLevelMainMenuBackground() )
  989. {
  990. // map load complete, safe to allow QMS
  991. Host_AllowQueuedMaterialSystem( true );
  992. }
  993. // This is a Hack, but we need to suppress rendering for a bit in single player to let values settle on the client
  994. if ( (cl.m_nMaxClients == 1) && !demoplayer->IsPlayingBack() )
  995. {
  996. scr_nextdrawtick = host_tickcount + TIME_TO_TICKS( 0.25f );
  997. }
  998. #ifdef _X360
  999. // At this point, check for a valid controller connection. If it's been lost, then we need to pop our game UI up
  1000. XINPUT_CAPABILITIES caps;
  1001. if ( XInputGetCapabilities( XBX_GetPrimaryUserId(), XINPUT_FLAG_GAMEPAD, &caps ) == ERROR_DEVICE_NOT_CONNECTED )
  1002. {
  1003. EngineVGui()->ActivateGameUI();
  1004. }
  1005. #endif // _X360
  1006. // Now that we're connected, toggle the clan tag so it gets sent to the server
  1007. int id = cl_clanid.GetInt();
  1008. cl_clanid.SetValue( 0 );
  1009. cl_clanid.SetValue( id );
  1010. MemAlloc_CompactHeap();
  1011. extern double g_flAccumulatedModelLoadTime;
  1012. extern double g_flAccumulatedSoundLoadTime;
  1013. extern double g_flAccumulatedModelLoadTimeStudio;
  1014. extern double g_flAccumulatedModelLoadTimeVCollideSync;
  1015. extern double g_flAccumulatedModelLoadTimeVCollideAsync;
  1016. extern double g_flAccumulatedModelLoadTimeVirtualModel;
  1017. extern double g_flAccumulatedModelLoadTimeStaticMesh;
  1018. extern double g_flAccumulatedModelLoadTimeBrush;
  1019. extern double g_flAccumulatedModelLoadTimeSprite;
  1020. extern double g_flAccumulatedModelLoadTimeMaterialNamesOnly;
  1021. // extern double g_flLoadStudioHdr;
  1022. COM_TimestampedLog( "Sound Loading time %.4f", g_flAccumulatedSoundLoadTime );
  1023. COM_TimestampedLog( "Model Loading time %.4f", g_flAccumulatedModelLoadTime );
  1024. COM_TimestampedLog( " Model Loading time studio %.4f", g_flAccumulatedModelLoadTimeStudio );
  1025. COM_TimestampedLog( " Model Loading time GetVCollide %.4f -sync", g_flAccumulatedModelLoadTimeVCollideSync );
  1026. COM_TimestampedLog( " Model Loading time GetVCollide %.4f -async", g_flAccumulatedModelLoadTimeVCollideAsync );
  1027. COM_TimestampedLog( " Model Loading time GetVirtualModel %.4f", g_flAccumulatedModelLoadTimeVirtualModel );
  1028. COM_TimestampedLog( " Model loading time Mod_GetModelMaterials only %.4f", g_flAccumulatedModelLoadTimeMaterialNamesOnly );
  1029. COM_TimestampedLog( " Model Loading time world %.4f", g_flAccumulatedModelLoadTimeBrush );
  1030. COM_TimestampedLog( " Model Loading time sprites %.4f", g_flAccumulatedModelLoadTimeSprite );
  1031. COM_TimestampedLog( " Model Loading time meshes %.4f", g_flAccumulatedModelLoadTimeStaticMesh );
  1032. // COM_TimestampedLog( " Model Loading time meshes studiohdr load %.4f", g_flLoadStudioHdr );
  1033. COM_TimestampedLog( "*** Map Load Complete" );
  1034. float map_loadtime_start = dev_loadtime_map_start.GetFloat();
  1035. if (map_loadtime_start > 0.0)
  1036. {
  1037. float elapsed = Plat_FloatTime() - map_loadtime_start;
  1038. dev_loadtime_map_elapsed.SetValue( elapsed );
  1039. // Clear this for next time so we know we did.
  1040. dev_loadtime_map_start.SetValue( 0.0f );
  1041. }
  1042. }
  1043. /*
  1044. =====================
  1045. CL_NextDemo
  1046. Called to play the next demo in the demo loop
  1047. =====================
  1048. */
  1049. void CL_NextDemo (void)
  1050. {
  1051. char str[1024];
  1052. if (cl.demonum == -1)
  1053. return; // don't play demos
  1054. SCR_BeginLoadingPlaque ();
  1055. if ( cl.demos[cl.demonum].IsEmpty() || cl.demonum == MAX_DEMOS )
  1056. {
  1057. cl.demonum = 0;
  1058. if ( cl.demos[cl.demonum].IsEmpty() )
  1059. {
  1060. scr_disabled_for_loading = false;
  1061. ConMsg ("No demos listed with startdemos\n");
  1062. cl.demonum = -1;
  1063. return;
  1064. }
  1065. else if ( !demoplayer->ShouldLoopDemos() )
  1066. {
  1067. cl.demonum = -1;
  1068. scr_disabled_for_loading = false;
  1069. Host_Disconnect( true );
  1070. demoplayer->OnLastDemoInLoopPlayed();
  1071. return;
  1072. }
  1073. }
  1074. Q_snprintf (str,sizeof( str ), "%s %s", CommandLine()->FindParm("-timedemoloop") ? "timedemo" : "playdemo", cl.demos[cl.demonum].Get());
  1075. Cbuf_AddText (str);
  1076. cl.demonum++;
  1077. }
  1078. ConVar cl_screenshotname( "cl_screenshotname", "", 0, "Custom Screenshot name" );
  1079. // We'll take a snapshot at the next available opportunity
  1080. void CL_TakeScreenshot(const char *name)
  1081. {
  1082. cl_takesnapshot = true;
  1083. cl_takejpeg = false;
  1084. cl_takesnapshot_internal = false;
  1085. if ( name != NULL )
  1086. {
  1087. Q_strncpy( cl_snapshotname, name, sizeof( cl_snapshotname ) );
  1088. }
  1089. else
  1090. {
  1091. cl_snapshotname[0] = 0;
  1092. if ( Q_strlen( cl_screenshotname.GetString() ) > 0 )
  1093. {
  1094. Q_snprintf( cl_snapshotname, sizeof( cl_snapshotname ), "%s", cl_screenshotname.GetString() );
  1095. }
  1096. }
  1097. cl_snapshot_subdirname[0] = 0;
  1098. }
  1099. CON_COMMAND_F( screenshot, "Take a screenshot.", FCVAR_CLIENTCMD_CAN_EXECUTE )
  1100. {
  1101. GetTestScriptMgr()->SetWaitCheckPoint( "screenshot" );
  1102. // Don't playback screenshots unless specifically requested.
  1103. if ( demoplayer->IsPlayingBack() && !cl_playback_screenshots.GetBool() )
  1104. return;
  1105. if( args.ArgC() == 2 )
  1106. {
  1107. CL_TakeScreenshot( args[ 1 ] );
  1108. }
  1109. else
  1110. {
  1111. CL_TakeScreenshot( NULL );
  1112. }
  1113. }
  1114. CON_COMMAND_F( devshots_screenshot, "Used by the -makedevshots system to take a screenshot. For taking your own screenshots, use the 'screenshot' command instead.", FCVAR_DONTRECORD )
  1115. {
  1116. CL_TakeScreenshot( NULL );
  1117. // See if we got a subdirectory to store the devshots in
  1118. if ( args.ArgC() == 2 )
  1119. {
  1120. Q_strncpy( cl_snapshot_subdirname, args[1], sizeof( cl_snapshot_subdirname ) );
  1121. // Use the first available shot in each subdirectory
  1122. cl_snapshotnum = 0;
  1123. }
  1124. }
  1125. // We'll take a snapshot at the next available opportunity
  1126. void CL_TakeJpeg(const char *name, int quality)
  1127. {
  1128. // Don't playback screenshots unless specifically requested.
  1129. if ( demoplayer->IsPlayingBack() && !cl_playback_screenshots.GetBool() )
  1130. return;
  1131. cl_takesnapshot = true;
  1132. cl_takejpeg = true;
  1133. cl_jpegquality = clamp( quality, 1, 100 );
  1134. cl_takesnapshot_internal = false;
  1135. if ( name != NULL )
  1136. {
  1137. Q_strncpy( cl_snapshotname, name, sizeof( cl_snapshotname ) );
  1138. }
  1139. else
  1140. {
  1141. cl_snapshotname[0] = 0;
  1142. }
  1143. }
  1144. CON_COMMAND( jpeg, "Take a jpeg screenshot: jpeg <filename> <quality 1-100>." )
  1145. {
  1146. if( args.ArgC() >= 2 )
  1147. {
  1148. if ( args.ArgC() == 3 )
  1149. {
  1150. CL_TakeJpeg( args[ 1 ], Q_atoi( args[2] ) );
  1151. }
  1152. else
  1153. {
  1154. CL_TakeJpeg( args[ 1 ], jpeg_quality.GetInt() );
  1155. }
  1156. }
  1157. else
  1158. {
  1159. CL_TakeJpeg( NULL, jpeg_quality.GetInt() );
  1160. }
  1161. }
  1162. static void screenshot_internal( const CCommand &args )
  1163. {
  1164. if( args.ArgC() != 2 )
  1165. {
  1166. Assert( args.ArgC() >= 2 );
  1167. Warning( "__screenshot_internal - wrong number of arguments" );
  1168. return;
  1169. }
  1170. Q_strncpy( cl_snapshotname, args[1], ARRAYSIZE(cl_snapshotname) );
  1171. cl_takesnapshot = true;
  1172. cl_takejpeg = true;
  1173. cl_jpegquality = 70;
  1174. cl_takesnapshot_internal = true;
  1175. }
  1176. ConCommand screenshot_internal_command( "__screenshot_internal", screenshot_internal, "Internal command to take a screenshot without renumbering or notifying Steam.", FCVAR_DONTRECORD | FCVAR_HIDDEN );
  1177. void CL_TakeSnapshotAndSwap()
  1178. {
  1179. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  1180. bool bReadPixelsFromFrontBuffer = g_pMaterialSystemHardwareConfig->ReadPixelsFromFrontBuffer();
  1181. if ( bReadPixelsFromFrontBuffer )
  1182. {
  1183. Shader_SwapBuffers();
  1184. }
  1185. if (cl_takesnapshot)
  1186. {
  1187. // Disable threading for the duration of the screenshots, because we need to get pointers to the (complete)
  1188. // back buffer right now.
  1189. bool bEnabled = materials->AllowThreading( false, g_nMaterialSystemThread );
  1190. char base[MAX_OSPATH];
  1191. char filename[MAX_OSPATH];
  1192. IClientEntity *world = entitylist->GetClientEntity( 0 );
  1193. g_pFileSystem->CreateDirHierarchy( "screenshots", "DEFAULT_WRITE_PATH" );
  1194. if ( cl_takesnapshot_internal )
  1195. {
  1196. // !KLUDGE! Don't save this screenshot to steam
  1197. ConVarRef cl_savescreenshotstosteam( "cl_savescreenshotstosteam" );
  1198. bool bSaveValue = cl_savescreenshotstosteam.GetBool();
  1199. cl_savescreenshotstosteam.SetValue( false );
  1200. Q_snprintf( filename, sizeof( filename ), "screenshots/%s.jpg", cl_snapshotname );
  1201. videomode->TakeSnapshotJPEG( filename, cl_jpegquality );
  1202. cl_savescreenshotstosteam.SetValue( bSaveValue );
  1203. }
  1204. else
  1205. {
  1206. if ( world && world->GetModel() )
  1207. {
  1208. Q_FileBase( modelloader->GetName( ( model_t *)world->GetModel() ), base, sizeof( base ) );
  1209. if ( IsX360() )
  1210. {
  1211. // map name has an additional extension
  1212. V_StripExtension( base, base, sizeof( base ) );
  1213. }
  1214. }
  1215. else
  1216. {
  1217. Q_strncpy( base, "Snapshot", sizeof( base ) );
  1218. }
  1219. char extension[MAX_OSPATH];
  1220. Q_snprintf( extension, sizeof( extension ), "%s.%s", GetPlatformExt(), cl_takejpeg ? "jpg" : "tga" );
  1221. // Using a subdir? If so, create it
  1222. if ( cl_snapshot_subdirname[0] )
  1223. {
  1224. Q_snprintf( filename, sizeof( filename ), "screenshots/%s/%s", base, cl_snapshot_subdirname );
  1225. g_pFileSystem->CreateDirHierarchy( filename, "DEFAULT_WRITE_PATH" );
  1226. }
  1227. if ( cl_snapshotname[0] )
  1228. {
  1229. Q_strncpy( base, cl_snapshotname, sizeof( base ) );
  1230. Q_snprintf( filename, sizeof( filename ), "screenshots/%s%s", base, extension );
  1231. int iNumber = 0;
  1232. char renamedfile[MAX_OSPATH];
  1233. while ( 1 )
  1234. {
  1235. Q_snprintf( renamedfile, sizeof( renamedfile ), "screenshots/%s_%04d%s", base, iNumber++, extension );
  1236. if( !g_pFileSystem->GetFileTime( renamedfile ) )
  1237. break;
  1238. }
  1239. if ( iNumber > 0 && g_pFileSystem->GetFileTime( filename ) )
  1240. {
  1241. g_pFileSystem->RenameFile(filename, renamedfile);
  1242. }
  1243. cl_screenshotname.SetValue( "" );
  1244. }
  1245. else
  1246. {
  1247. while( 1 )
  1248. {
  1249. if ( cl_snapshot_subdirname[0] )
  1250. {
  1251. Q_snprintf( filename, sizeof( filename ), "screenshots/%s/%s/%s%04d%s", base, cl_snapshot_subdirname, base, cl_snapshotnum++, extension );
  1252. }
  1253. else
  1254. {
  1255. Q_snprintf( filename, sizeof( filename ), "screenshots/%s%04d%s", base, cl_snapshotnum++, extension );
  1256. }
  1257. if( !g_pFileSystem->GetFileTime( filename ) )
  1258. {
  1259. // woo hoo! The file doesn't exist already, so use it.
  1260. break;
  1261. }
  1262. }
  1263. }
  1264. if ( cl_takejpeg )
  1265. {
  1266. videomode->TakeSnapshotJPEG( filename, cl_jpegquality );
  1267. g_ServerRemoteAccess.UploadScreenshot( filename );
  1268. }
  1269. else
  1270. {
  1271. videomode->TakeSnapshotTGA( filename );
  1272. }
  1273. }
  1274. cl_takesnapshot = false;
  1275. cl_takesnapshot_internal = false;
  1276. GetTestScriptMgr()->CheckPoint( "screenshot" );
  1277. // Restore threading if it was previously enabled (if it wasn't this will do nothing).
  1278. materials->AllowThreading( bEnabled, g_nMaterialSystemThread );
  1279. }
  1280. // If recording movie and the console is totally up, then write out this frame to movie file.
  1281. if ( cl_movieinfo.IsRecording() && !Con_IsVisible() && !scr_drawloading )
  1282. {
  1283. videomode->WriteMovieFrame( cl_movieinfo );
  1284. ++cl_movieinfo.movieframe;
  1285. }
  1286. if( !bReadPixelsFromFrontBuffer )
  1287. {
  1288. Shader_SwapBuffers();
  1289. }
  1290. // take a screenshot for savegames if necessary
  1291. saverestore->UpdateSaveGameScreenshots();
  1292. // take screenshot for bx movie maker
  1293. EngineTool_UpdateScreenshot();
  1294. }
  1295. static float s_flPreviousHostFramerate = 0;
  1296. ConVar cl_simulate_no_quicktime( "cl_simulate_no_quicktime", "0", FCVAR_HIDDEN );
  1297. void CL_StartMovie( const char *filename, int flags, int nWidth, int nHeight, float flFrameRate, int nJpegQuality, VideoSystem_t videoSystem )
  1298. {
  1299. Assert( g_pVideoRecorder == NULL );
  1300. // StartMove depends on host_framerate not being 0.
  1301. s_flPreviousHostFramerate = host_framerate.GetFloat();
  1302. host_framerate.SetValue( flFrameRate );
  1303. cl_movieinfo.Reset();
  1304. Q_strncpy( cl_movieinfo.moviename, filename, sizeof( cl_movieinfo.moviename ) );
  1305. cl_movieinfo.type = flags;
  1306. cl_movieinfo.jpeg_quality = nJpegQuality;
  1307. bool bSuccess = true;
  1308. if ( cl_movieinfo.DoVideo() || cl_movieinfo.DoVideoSound() )
  1309. {
  1310. // HACK: THIS MUST MATCH snd_device.h. Should be exposed more cleanly!!!
  1311. #define SOUND_DMA_SPEED 44100 // hardware playback rate
  1312. // MGP - switched over to using valve video services from avi
  1313. if ( videoSystem == VideoSystem::NONE && g_pVideo )
  1314. {
  1315. // Find a video system based on features if they didn't specify a specific one.
  1316. VideoSystemFeature_t neededFeatures = VideoSystemFeature::NO_FEATURES;
  1317. if ( cl_movieinfo.DoVideo() )
  1318. neededFeatures |= VideoSystemFeature::ENCODE_VIDEO_TO_FILE;
  1319. if ( cl_movieinfo.DoVideoSound() )
  1320. neededFeatures |= VideoSystemFeature::ENCODE_AUDIO_TO_FILE;
  1321. videoSystem = g_pVideo->FindNextSystemWithFeature( neededFeatures );
  1322. }
  1323. if ( !cl_simulate_no_quicktime.GetBool() && g_pVideo && videoSystem != VideoSystem::NONE )
  1324. {
  1325. g_pVideoRecorder = g_pVideo->CreateVideoRecorder( videoSystem );
  1326. if ( g_pVideoRecorder != NULL )
  1327. {
  1328. VideoFrameRate_t theFps;
  1329. if ( IsIntegralValue( flFrameRate ) )
  1330. {
  1331. theFps.SetFPS( RoundFloatToInt( flFrameRate ), false );
  1332. }
  1333. else if ( IsIntegralValue( flFrameRate * 1001.0f / 1000.0f ) ) // 1001 is the ntsc divisor (30*1000/1001 = 29.97, etc)
  1334. {
  1335. theFps.SetFPS( RoundFloatToInt( flFrameRate + 0.05f ), true );
  1336. }
  1337. else
  1338. {
  1339. theFps.SetFPS( RoundFloatToInt( flFrameRate ), false );
  1340. }
  1341. const int nSize = 256;
  1342. CFmtStrN<nSize> fmtFullFilename( "%s%c%s", com_gamedir, CORRECT_PATH_SEPARATOR, filename );
  1343. char szFullFilename[nSize];
  1344. V_FixupPathName( szFullFilename, nSize, fmtFullFilename.Access() );
  1345. #ifdef USE_WEBM_FOR_REPLAY
  1346. V_DefaultExtension( szFullFilename, ".webm", sizeof( szFullFilename ) );
  1347. #else
  1348. V_DefaultExtension( szFullFilename, ".mp4", sizeof( szFullFilename ) );
  1349. #endif
  1350. g_pVideoRecorder->CreateNewMovieFile( szFullFilename, cl_movieinfo.DoVideoSound() );
  1351. #ifdef USE_WEBM_FOR_REPLAY
  1352. g_pVideoRecorder->SetMovieVideoParameters( VideoEncodeCodec::WEBM_CODEC, nJpegQuality, nWidth, nHeight, theFps );
  1353. #else
  1354. g_pVideoRecorder->SetMovieVideoParameters( VideoEncodeCodec::DEFAULT_CODEC, nJpegQuality, nWidth, nHeight, theFps );
  1355. #endif
  1356. if ( cl_movieinfo.DoVideo() )
  1357. {
  1358. g_pVideoRecorder->SetMovieSourceImageParameters( VideoEncodeSourceFormat::BGR_24BIT, nWidth, nHeight );
  1359. }
  1360. if ( cl_movieinfo.DoVideoSound() )
  1361. {
  1362. g_pVideoRecorder->SetMovieSourceAudioParameters( AudioEncodeSourceFormat::AUDIO_16BIT_PCMStereo, SOUND_DMA_SPEED, AudioEncodeOptions::LIMIT_AUDIO_TRACK_TO_VIDEO_DURATION );
  1363. }
  1364. }
  1365. else
  1366. {
  1367. bSuccess = false;
  1368. }
  1369. }
  1370. else
  1371. {
  1372. bSuccess = false;
  1373. }
  1374. }
  1375. if ( bSuccess )
  1376. {
  1377. SND_MovieStart();
  1378. }
  1379. else
  1380. {
  1381. #ifdef USE_WEBM_FOR_REPLAY
  1382. Warning( "Failed to launch startmovie!\n" );
  1383. #else
  1384. Warning( "Failed to launch startmovie! If you are trying to use h264, please make sure you have QuickTime installed.\n" );
  1385. #endif
  1386. CL_EndMovie();
  1387. }
  1388. }
  1389. void CL_EndMovie()
  1390. {
  1391. if ( !CL_IsRecordingMovie() )
  1392. return;
  1393. host_framerate.SetValue( s_flPreviousHostFramerate );
  1394. s_flPreviousHostFramerate = 0.0f;
  1395. SND_MovieEnd();
  1396. if ( g_pVideoRecorder && ( cl_movieinfo.DoVideo() || cl_movieinfo.DoVideoSound() ) )
  1397. {
  1398. g_pVideoRecorder->FinishMovie();
  1399. g_pVideo->DestroyVideoRecorder( g_pVideoRecorder );
  1400. g_pVideoRecorder = NULL;
  1401. }
  1402. cl_movieinfo.Reset();
  1403. }
  1404. bool CL_IsRecordingMovie()
  1405. {
  1406. return cl_movieinfo.IsRecording();
  1407. }
  1408. /*
  1409. ===============
  1410. CL_StartMovie_f
  1411. Sets the engine up to dump frames
  1412. ===============
  1413. */
  1414. CON_COMMAND_F( startmovie, "Start recording movie frames.", FCVAR_DONTRECORD )
  1415. {
  1416. if ( cmd_source != src_command )
  1417. return;
  1418. if( args.ArgC() < 2 )
  1419. {
  1420. ConMsg( "startmovie <filename>\n [\n" );
  1421. ConMsg( " (default = TGAs + .wav file)\n" );
  1422. #ifdef USE_WEBM_FOR_REPLAY
  1423. ConMsg( " webm = WebM encoded audio and video\n" );
  1424. #else
  1425. ConMsg( " h264 = H.264-encoded audio and video (must have QuickTime installed!)\n" );
  1426. #endif
  1427. ConMsg( " raw = TGAs + .wav file, same as default\n" );
  1428. ConMsg( " tga = TGAs\n" );
  1429. ConMsg( " jpg/jpeg = JPegs\n" );
  1430. ConMsg( " wav = Write .wav audio file\n" );
  1431. ConMsg( " jpeg_quality nnn = set jpeq quality to nnn (range 1 to 100), default %d\n", DEFAULT_JPEG_QUALITY );
  1432. ConMsg( " ]\n" );
  1433. ConMsg( "examples:\n" );
  1434. ConMsg( " startmovie testmovie jpg wav jpeg_qality 75\n" );
  1435. #ifdef USE_WEBM_FOR_REPLAY
  1436. ConMsg( " startmovie testmovie webm\n" );
  1437. #else
  1438. ConMsg( " startmovie testmovie h264 <--- requires QuickTime\n" );
  1439. ConMsg( "AVI is no longer supported.\n" );
  1440. #endif
  1441. return;
  1442. }
  1443. if ( CL_IsRecordingMovie() )
  1444. {
  1445. ConMsg( "Already recording movie!\n" );
  1446. return;
  1447. }
  1448. int flags = MovieInfo_t::FMOVIE_TGA | MovieInfo_t::FMOVIE_WAV;
  1449. VideoSystem_t videoSystem = VideoSystem::NONE;
  1450. int nJpegQuality = DEFAULT_JPEG_QUALITY;
  1451. if ( args.ArgC() > 2 )
  1452. {
  1453. flags = 0;
  1454. for ( int i = 2; i < args.ArgC(); ++i )
  1455. {
  1456. if ( !Q_stricmp( args[ i ], "avi" ) )
  1457. {
  1458. //flags |= MovieInfo_t::FMOVIE_VID | MovieInfo_t::FMOVIE_VIDSOUND;
  1459. //videoSystem = VideoSystem::AVI;
  1460. #ifdef USE_WEBM_FOR_REPLAY
  1461. Warning( "AVI is not supported on this platform! Use \"webm\".\n" );
  1462. #else
  1463. Warning( "AVI is no longer supported! Make sure QuickTime is installed and use \"h264\" - if you install QuickTime, you will need to reboot before using startmovie.\n" );
  1464. #endif
  1465. return;
  1466. }
  1467. #ifdef USE_WEBM_FOR_REPLAY
  1468. else if ( !Q_stricmp( args[ i ], "webm" ) )
  1469. {
  1470. flags |= MovieInfo_t::FMOVIE_VID | MovieInfo_t::FMOVIE_VIDSOUND;
  1471. videoSystem = VideoSystem::WEBM;
  1472. }
  1473. else if ( !Q_stricmp( args[ i ], "h264" ) )
  1474. {
  1475. Warning( "h264 is not supported on this platform! Use \"webm\".\n" );
  1476. return;
  1477. }
  1478. #else
  1479. else if ( !Q_stricmp( args[ i ], "h264" ) )
  1480. {
  1481. flags |= MovieInfo_t::FMOVIE_VID | MovieInfo_t::FMOVIE_VIDSOUND;
  1482. videoSystem = VideoSystem::QUICKTIME;
  1483. }
  1484. else if ( !Q_stricmp( args[ i ], "webm" ) )
  1485. {
  1486. Warning( "WebM is not supported on this platform! Make sure QuickTime is installed and use \"h264\" - if you install QuickTime, you will need to reboot before using startmovie.\n" );
  1487. return;
  1488. }
  1489. #endif
  1490. if ( !Q_stricmp( args[ i ], "raw" ) )
  1491. {
  1492. flags |= MovieInfo_t::FMOVIE_TGA | MovieInfo_t::FMOVIE_WAV;
  1493. }
  1494. if ( !Q_stricmp( args[ i ], "tga" ) )
  1495. {
  1496. flags |= MovieInfo_t::FMOVIE_TGA;
  1497. }
  1498. if ( !Q_stricmp( args[ i ], "jpeg" ) || !Q_stricmp( args[ i ], "jpg" ) )
  1499. {
  1500. flags &= ~MovieInfo_t::FMOVIE_TGA;
  1501. flags |= MovieInfo_t::FMOVIE_JPG;
  1502. }
  1503. if ( !Q_stricmp( args[ i ], "jpeg_quality" ) )
  1504. {
  1505. nJpegQuality = clamp( Q_atoi( args[ ++i ] ), 1, 100 );
  1506. }
  1507. if ( !Q_stricmp( args[ i ], "wav" ) )
  1508. {
  1509. flags |= MovieInfo_t::FMOVIE_WAV;
  1510. }
  1511. }
  1512. }
  1513. if ( flags == 0 )
  1514. {
  1515. #ifdef USE_WEBM_FOR_REPLAY
  1516. Warning( "Missing or unknown recording types, must specify one or both of 'webm' or 'raw'\n" );
  1517. #else
  1518. Warning( "Missing or unknown recording types, must specify one or both of 'h264' or 'raw'\n" );
  1519. #endif
  1520. return;
  1521. }
  1522. float flFrameRate = host_framerate.GetFloat();
  1523. if ( flFrameRate == 0.0f )
  1524. {
  1525. flFrameRate = 30.0f;
  1526. }
  1527. CL_StartMovie( args[ 1 ], flags, videomode->GetModeStereoWidth(), videomode->GetModeStereoHeight(), flFrameRate, nJpegQuality, videoSystem );
  1528. ConMsg( "Started recording movie, frames will record after console is cleared...\n" );
  1529. }
  1530. //-----------------------------------------------------------------------------
  1531. // Ends frame dumping
  1532. //-----------------------------------------------------------------------------
  1533. CON_COMMAND_F( endmovie, "Stop recording movie frames.", FCVAR_DONTRECORD )
  1534. {
  1535. if( !CL_IsRecordingMovie() )
  1536. {
  1537. ConMsg( "No movie started.\n" );
  1538. }
  1539. else
  1540. {
  1541. CL_EndMovie();
  1542. ConMsg( "Stopped recording movie...\n" );
  1543. }
  1544. }
  1545. /*
  1546. =====================
  1547. CL_Rcon_f
  1548. Send the rest of the command line over as
  1549. an unconnected command.
  1550. =====================
  1551. */
  1552. CON_COMMAND_F( rcon, "Issue an rcon command.", FCVAR_DONTRECORD )
  1553. {
  1554. char message[1024]; // Command message
  1555. char szParam[ 256 ];
  1556. message[0] = 0;
  1557. for (int i=1 ; i<args.ArgC() ; i++)
  1558. {
  1559. const char *pParam = args[i];
  1560. // put quotes around empty arguments so we can pass things like this: rcon sv_password ""
  1561. // otherwise the "" on the end is lost
  1562. if ( strchr( pParam, ' ' ) || ( Q_strlen( pParam ) == 0 ) )
  1563. {
  1564. Q_snprintf( szParam, sizeof( szParam ), "\"%s\"", pParam );
  1565. Q_strncat( message, szParam, sizeof( message ), COPY_ALL_CHARACTERS );
  1566. }
  1567. else
  1568. {
  1569. Q_strncat( message, pParam, sizeof( message ), COPY_ALL_CHARACTERS );
  1570. }
  1571. if ( i != ( args.ArgC() - 1 ) )
  1572. {
  1573. Q_strncat (message, " ", sizeof( message ), COPY_ALL_CHARACTERS);
  1574. }
  1575. }
  1576. RCONClient().SendCmd( message );
  1577. }
  1578. CON_COMMAND_F( box, "Draw a debug box.", FCVAR_CHEAT )
  1579. {
  1580. if( args.ArgC() != 7 )
  1581. {
  1582. ConMsg ("box x1 y1 z1 x2 y2 z2\n");
  1583. return;
  1584. }
  1585. Vector mins, maxs;
  1586. for (int i = 0; i < 3; ++i)
  1587. {
  1588. mins[i] = atof(args[i + 1]);
  1589. maxs[i] = atof(args[i + 4]);
  1590. }
  1591. CDebugOverlay::AddBoxOverlay( vec3_origin, mins, maxs, vec3_angle, 255, 0, 0, 0, 100 );
  1592. }
  1593. /*
  1594. ==============
  1595. CL_View_f
  1596. Debugging changes the view entity to the specified index
  1597. ===============
  1598. */
  1599. CON_COMMAND_F( cl_view, "Set the view entity index.", FCVAR_CHEAT )
  1600. {
  1601. int nNewView;
  1602. if( args.ArgC() != 2 )
  1603. {
  1604. ConMsg ("cl_view entity#\nCurrent %i\n", cl.m_nViewEntity );
  1605. return;
  1606. }
  1607. if ( cl.m_nMaxClients > 1 )
  1608. return;
  1609. nNewView = atoi( args[1] );
  1610. if (!nNewView)
  1611. return;
  1612. if ( nNewView > entitylist->GetHighestEntityIndex() )
  1613. return;
  1614. cl.m_nViewEntity = nNewView;
  1615. videomode->MarkClientViewRectDirty(); // Force recalculation
  1616. ConMsg("View entity set to %i\n", nNewView);
  1617. }
  1618. static int CL_AllocLightFromArray( dlight_t *pLights, int lightCount, int key )
  1619. {
  1620. int i;
  1621. // first look for an exact key match
  1622. if (key)
  1623. {
  1624. for ( i = 0; i < lightCount; i++ )
  1625. {
  1626. if (pLights[i].key == key)
  1627. return i;
  1628. }
  1629. }
  1630. // then look for anything else
  1631. for ( i = 0; i < lightCount; i++ )
  1632. {
  1633. if (pLights[i].die < cl.GetTime())
  1634. return i;
  1635. }
  1636. return 0;
  1637. }
  1638. bool g_bActiveDlights = false;
  1639. bool g_bActiveElights = false;
  1640. /*
  1641. ===============
  1642. CL_AllocDlight
  1643. ===============
  1644. */
  1645. dlight_t *CL_AllocDlight (int key)
  1646. {
  1647. int i = CL_AllocLightFromArray( cl_dlights, MAX_DLIGHTS, key );
  1648. dlight_t *dl = &cl_dlights[i];
  1649. R_MarkDLightNotVisible( i );
  1650. memset (dl, 0, sizeof(*dl));
  1651. dl->key = key;
  1652. r_dlightchanged |= (1 << i);
  1653. r_dlightactive |= (1 << i);
  1654. g_bActiveDlights = true;
  1655. return dl;
  1656. }
  1657. /*
  1658. ===============
  1659. CL_AllocElight
  1660. ===============
  1661. */
  1662. dlight_t *CL_AllocElight (int key)
  1663. {
  1664. int i = CL_AllocLightFromArray( cl_elights, MAX_ELIGHTS, key );
  1665. dlight_t *el = &cl_elights[i];
  1666. memset (el, 0, sizeof(*el));
  1667. el->key = key;
  1668. g_bActiveElights = true;
  1669. return el;
  1670. }
  1671. /*
  1672. ===============
  1673. CL_DecayLights
  1674. ===============
  1675. */
  1676. void CL_DecayLights (void)
  1677. {
  1678. int i;
  1679. dlight_t *dl;
  1680. float time;
  1681. time = cl.GetFrameTime();
  1682. if ( time <= 0.0f )
  1683. return;
  1684. g_bActiveDlights = false;
  1685. g_bActiveElights = false;
  1686. dl = cl_dlights;
  1687. r_dlightchanged = 0;
  1688. r_dlightactive = 0;
  1689. for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
  1690. {
  1691. if (!dl->IsRadiusGreaterThanZero())
  1692. {
  1693. R_MarkDLightNotVisible( i );
  1694. continue;
  1695. }
  1696. if ( dl->die < cl.GetTime() )
  1697. {
  1698. r_dlightchanged |= (1 << i);
  1699. dl->radius = 0;
  1700. }
  1701. else if (dl->decay)
  1702. {
  1703. r_dlightchanged |= (1 << i);
  1704. dl->radius -= time*dl->decay;
  1705. if (dl->radius < 0)
  1706. {
  1707. dl->radius = 0;
  1708. }
  1709. }
  1710. if (dl->IsRadiusGreaterThanZero())
  1711. {
  1712. g_bActiveDlights = true;
  1713. r_dlightactive |= (1 << i);
  1714. }
  1715. else
  1716. {
  1717. R_MarkDLightNotVisible( i );
  1718. }
  1719. }
  1720. dl = cl_elights;
  1721. for (i=0 ; i<MAX_ELIGHTS ; i++, dl++)
  1722. {
  1723. if (!dl->IsRadiusGreaterThanZero())
  1724. continue;
  1725. if (dl->die < cl.GetTime())
  1726. {
  1727. dl->radius = 0;
  1728. continue;
  1729. }
  1730. dl->radius -= time*dl->decay;
  1731. if (dl->radius < 0)
  1732. {
  1733. dl->radius = 0;
  1734. }
  1735. if ( dl->IsRadiusGreaterThanZero() )
  1736. {
  1737. g_bActiveElights = true;
  1738. }
  1739. }
  1740. }
  1741. void CL_ExtraMouseUpdate( float frametime )
  1742. {
  1743. // Not ready for commands yet.
  1744. if ( !cl.IsActive() )
  1745. return;
  1746. if ( !Host_ShouldRun() )
  1747. return;
  1748. // Don't create usercmds here during playback, they were encoded into the packet already
  1749. #if defined( REPLAY_ENABLED )
  1750. if ( demoplayer->IsPlayingBack() && !cl.ishltv && !cl.isreplay )
  1751. return;
  1752. #else
  1753. if ( demoplayer->IsPlayingBack() && !cl.ishltv )
  1754. return;
  1755. #endif
  1756. // Have client .dll create and store usercmd structure
  1757. g_ClientDLL->ExtraMouseSample( frametime, !cl.m_bPaused );
  1758. }
  1759. /*
  1760. =================
  1761. CL_SendMove
  1762. Constructs the movement command and sends it to the server if it's time.
  1763. =================
  1764. */
  1765. void CL_SendMove( void )
  1766. {
  1767. #if defined( STAGING_ONLY ) || defined( _DEBUG )
  1768. if ( cl_block_usercommand.GetBool() )
  1769. return;
  1770. #endif // STAGING_ONLY || _DEBUG
  1771. byte data[ MAX_CMD_BUFFER ];
  1772. int nextcommandnr = cl.lastoutgoingcommand + cl.chokedcommands + 1;
  1773. // send the client update packet
  1774. CLC_Move moveMsg;
  1775. moveMsg.m_DataOut.StartWriting( data, sizeof( data ) );
  1776. // Determine number of backup commands to send along
  1777. int cl_cmdbackup = 2;
  1778. moveMsg.m_nBackupCommands = clamp( cl_cmdbackup, 0, MAX_BACKUP_COMMANDS );
  1779. // How many real new commands have queued up
  1780. moveMsg.m_nNewCommands = 1 + cl.chokedcommands;
  1781. moveMsg.m_nNewCommands = clamp( moveMsg.m_nNewCommands, 0, MAX_NEW_COMMANDS );
  1782. int numcmds = moveMsg.m_nNewCommands + moveMsg.m_nBackupCommands;
  1783. int from = -1; // first command is deltaed against zeros
  1784. bool bOK = true;
  1785. for ( int to = nextcommandnr - numcmds + 1; to <= nextcommandnr; to++ )
  1786. {
  1787. bool isnewcmd = to >= (nextcommandnr - moveMsg.m_nNewCommands + 1);
  1788. // first valid command number is 1
  1789. bOK = bOK && g_ClientDLL->WriteUsercmdDeltaToBuffer( &moveMsg.m_DataOut, from, to, isnewcmd );
  1790. from = to;
  1791. }
  1792. if ( bOK )
  1793. {
  1794. // only write message if all usercmds were written correctly, otherwise parsing would fail
  1795. cl.m_NetChannel->SendNetMsg( moveMsg );
  1796. }
  1797. }
  1798. void CL_Move(float accumulated_extra_samples, bool bFinalTick )
  1799. {
  1800. if ( !cl.IsConnected() )
  1801. return;
  1802. if ( !Host_ShouldRun() )
  1803. return;
  1804. tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
  1805. // only send packets on the final tick in one engine frame
  1806. bool bSendPacket = true;
  1807. // Don't create usercmds here during playback, they were encoded into the packet already
  1808. if ( demoplayer->IsPlayingBack() )
  1809. {
  1810. #if defined( REPLAY_ENABLED )
  1811. if ( cl.ishltv || cl.isreplay )
  1812. #else
  1813. if ( cl.ishltv )
  1814. #endif
  1815. {
  1816. // still do it when playing back a HLTV/replay demo
  1817. bSendPacket = false;
  1818. }
  1819. else
  1820. {
  1821. return;
  1822. }
  1823. }
  1824. // don't send packets if update time not reached or chnnel still sending
  1825. // in loopback mode don't send only if host_limitlocal is enabled
  1826. if ( ( !cl.m_NetChannel->IsLoopback() || host_limitlocal.GetInt() ) &&
  1827. ( ( net_time < cl.m_flNextCmdTime ) || !cl.m_NetChannel->CanPacket() || !bFinalTick ) )
  1828. {
  1829. bSendPacket = false;
  1830. }
  1831. if ( cl.IsActive() )
  1832. {
  1833. VPROF( "CL_Move" );
  1834. int nextcommandnr = cl.lastoutgoingcommand + cl.chokedcommands + 1;
  1835. // Have client .dll create and store usercmd structure
  1836. g_ClientDLL->CreateMove(
  1837. nextcommandnr,
  1838. host_state.interval_per_tick - accumulated_extra_samples,
  1839. !cl.IsPaused() );
  1840. // Store new usercmd to dem file
  1841. if ( demorecorder->IsRecording() )
  1842. {
  1843. // Back up one because we've incremented outgoing_sequence each frame by 1 unit
  1844. demorecorder->RecordUserInput( nextcommandnr );
  1845. }
  1846. if ( bSendPacket )
  1847. {
  1848. CL_SendMove();
  1849. }
  1850. else
  1851. {
  1852. // netchanll will increase internal outgoing sequnce number too
  1853. cl.m_NetChannel->SetChoked();
  1854. // Mark command as held back so we'll send it next time
  1855. cl.chokedcommands++;
  1856. }
  1857. }
  1858. if ( !bSendPacket )
  1859. return;
  1860. // Request non delta compression if high packet loss, show warning message
  1861. bool hasProblem = cl.m_NetChannel->IsTimingOut() && !demoplayer->IsPlayingBack() && cl.IsActive();
  1862. // Request non delta compression if high packet loss, show warning message
  1863. if ( hasProblem )
  1864. {
  1865. con_nprint_t np;
  1866. np.time_to_live = 1.0;
  1867. np.index = 2;
  1868. np.fixed_width_font = false;
  1869. np.color[ 0 ] = 1.0;
  1870. np.color[ 1 ] = 0.2;
  1871. np.color[ 2 ] = 0.2;
  1872. float flTimeOut = cl.m_NetChannel->GetTimeoutSeconds();
  1873. Assert( flTimeOut != -1.0f );
  1874. float flRemainingTime = flTimeOut - cl.m_NetChannel->GetTimeSinceLastReceived();
  1875. Con_NXPrintf( &np, "WARNING: Connection Problem" );
  1876. np.index = 3;
  1877. Con_NXPrintf( &np, "Auto-disconnect in %.1f seconds", flRemainingTime );
  1878. cl.ForceFullUpdate(); // sets m_nDeltaTick to -1
  1879. }
  1880. if ( cl.IsActive() )
  1881. {
  1882. NET_Tick mymsg( cl.m_nDeltaTick, host_frametime_unbounded, host_frametime_stddeviation );
  1883. cl.m_NetChannel->SendNetMsg( mymsg );
  1884. }
  1885. //COM_Log( "cl.log", "Sending command number %i(%i) to server\n", cl.m_NetChan->m_nOutSequenceNr, cl.m_NetChan->m_nOutSequenceNr & CL_UPDATE_MASK );
  1886. // Remember outgoing command that we are sending
  1887. cl.lastoutgoingcommand = cl.m_NetChannel->SendDatagram( NULL );
  1888. cl.chokedcommands = 0;
  1889. // calc next packet send time
  1890. if ( cl.IsActive() )
  1891. {
  1892. // use full update rate when active
  1893. float commandInterval = 1.0f / cl_cmdrate->GetFloat();
  1894. float maxDelta = min ( host_state.interval_per_tick, commandInterval );
  1895. float delta = clamp( (float)(net_time - cl.m_flNextCmdTime), 0.0f, maxDelta );
  1896. cl.m_flNextCmdTime = net_time + commandInterval - delta;
  1897. }
  1898. else
  1899. {
  1900. // during signon process send only 5 packets/second
  1901. cl.m_flNextCmdTime = net_time + ( 1.0f / 5.0f );
  1902. }
  1903. }
  1904. #define TICK_INTERVAL (host_state.interval_per_tick)
  1905. #define ROUND_TO_TICKS( t ) ( TICK_INTERVAL * TIME_TO_TICKS( t ) )
  1906. void CL_LatchInterpolationAmount()
  1907. {
  1908. if ( !cl.IsConnected() )
  1909. return;
  1910. float dt = cl.m_NetChannel->GetTimeSinceLastReceived();
  1911. float flClientInterpolationAmount = ROUND_TO_TICKS( cl.GetClientInterpAmount() );
  1912. float flInterp = 0.0f;
  1913. if ( flClientInterpolationAmount > 0.001 )
  1914. {
  1915. flInterp = clamp( dt / flClientInterpolationAmount, 0.0f, 3.0f );
  1916. }
  1917. cl.m_NetChannel->SetInterpolationAmount( flInterp );
  1918. }
  1919. //-----------------------------------------------------------------------------
  1920. // Purpose:
  1921. // Input : *pMessage -
  1922. //-----------------------------------------------------------------------------
  1923. void CL_HudMessage( const char *pMessage )
  1924. {
  1925. if ( g_ClientDLL )
  1926. {
  1927. g_ClientDLL->HudText( pMessage );
  1928. }
  1929. }
  1930. CON_COMMAND_F( cl_showents, "Dump entity list to console.", FCVAR_CHEAT )
  1931. {
  1932. for ( int i = 0; i < entitylist->GetMaxEntities(); i++ )
  1933. {
  1934. char entStr[256], classStr[256];
  1935. IClientNetworkable *pEnt;
  1936. if((pEnt = entitylist->GetClientNetworkable(i)) != NULL)
  1937. {
  1938. entStr[0] = 0;
  1939. Q_snprintf(classStr, sizeof( classStr ), "'%s'", pEnt->GetClientClass()->m_pNetworkName);
  1940. }
  1941. else
  1942. {
  1943. Q_snprintf(entStr, sizeof( entStr ), "(missing), ");
  1944. Q_snprintf(classStr, sizeof( classStr ), "(missing)");
  1945. }
  1946. if ( pEnt )
  1947. ConMsg("Ent %3d: %s class %s\n", i, entStr, classStr);
  1948. }
  1949. }
  1950. //-----------------------------------------------------------------------------
  1951. // Purpose: returns true if the background level should be loaded on startup
  1952. //-----------------------------------------------------------------------------
  1953. bool CL_ShouldLoadBackgroundLevel( const CCommand &args )
  1954. {
  1955. if ( InEditMode() )
  1956. return false;
  1957. // If TF2 and PC we don't want to load the background map.
  1958. bool bIsTF2 = false;
  1959. if ( ( Q_stricmp( COM_GetModDirectory(), "tf" ) == 0 ) || ( Q_stricmp( COM_GetModDirectory(), "tf_beta" ) == 0 ) )
  1960. {
  1961. bIsTF2 = true;
  1962. }
  1963. if ( bIsTF2 && IsPC() )
  1964. return false;
  1965. if ( args.ArgC() == 2 )
  1966. {
  1967. // presence of args identifies an end-of-game situation
  1968. if ( IsX360() )
  1969. {
  1970. // 360 needs to get UI in the correct state to transition to the Background level
  1971. // from the credits.
  1972. EngineVGui()->OnCreditsFinished();
  1973. return true;
  1974. }
  1975. if ( !Q_stricmp( args[1], "force" ) )
  1976. {
  1977. // Adrian: Have to do this so the menu shows up if we ever call this while in a level.
  1978. Host_Disconnect( true );
  1979. // pc can't get into background maps fast enough, so just show main menu
  1980. return false;
  1981. }
  1982. if ( !Q_stricmp( args[1], "playendgamevid" ) )
  1983. {
  1984. // Bail back to the menu and play the end game video.
  1985. CommandLine()->AppendParm( "-endgamevid", NULL );
  1986. CommandLine()->RemoveParm( "-recapvid" );
  1987. game->PlayStartupVideos();
  1988. CommandLine()->RemoveParm( "-endgamevid" );
  1989. cl.Disconnect( "Finished playing end game videos", true );
  1990. return false;
  1991. }
  1992. if ( !Q_stricmp( args[1], "playrecapvid" ) )
  1993. {
  1994. // Bail back to the menu and play the recap video
  1995. CommandLine()->AppendParm( "-recapvid", NULL );
  1996. CommandLine()->RemoveParm( "-endgamevid" );
  1997. HostState_Restart();
  1998. return false;
  1999. }
  2000. }
  2001. // if force is set, then always return true
  2002. if (CommandLine()->CheckParm("-forcestartupmenu"))
  2003. return true;
  2004. // don't load the map in developer or console mode
  2005. if ( developer.GetInt() ||
  2006. CommandLine()->CheckParm("-console") ||
  2007. CommandLine()->CheckParm("-dev") )
  2008. return false;
  2009. // don't load the map if we're going straight into a level
  2010. if ( CommandLine()->CheckParm("+map") ||
  2011. CommandLine()->CheckParm("+connect") ||
  2012. CommandLine()->CheckParm("+playdemo") ||
  2013. CommandLine()->CheckParm("+timedemo") ||
  2014. CommandLine()->CheckParm("+timedemoquit") ||
  2015. CommandLine()->CheckParm("+load") ||
  2016. CommandLine()->CheckParm("-makereslists"))
  2017. return false;
  2018. #ifdef _X360
  2019. // check if we are accepting an invite
  2020. if ( XboxLaunch()->GetLaunchFlags() & LF_INVITERESTART )
  2021. return false;
  2022. #endif
  2023. // nothing else is going on, so load the startup level
  2024. return true;
  2025. }
  2026. #define DEFAULT_BACKGROUND_NAME "background01"
  2027. int g_iRandomChapterIndex = -1;
  2028. int CL_GetBackgroundLevelIndex( int nNumChapters )
  2029. {
  2030. if ( g_iRandomChapterIndex != -1 )
  2031. return g_iRandomChapterIndex;
  2032. int iChapterIndex = sv_unlockedchapters.GetInt();
  2033. if ( iChapterIndex <= 0 )
  2034. {
  2035. // expected to be [1..N]
  2036. iChapterIndex = 1;
  2037. }
  2038. if ( sv_unlockedchapters.GetInt() >= ( nNumChapters-1 ) )
  2039. {
  2040. RandomSeed( Plat_MSTime() );
  2041. g_iRandomChapterIndex = iChapterIndex = RandomInt( 1, nNumChapters );
  2042. }
  2043. return iChapterIndex;
  2044. }
  2045. //-----------------------------------------------------------------------------
  2046. // Purpose: returns the name of the background level to load
  2047. //-----------------------------------------------------------------------------
  2048. void CL_GetBackgroundLevelName( char *pszBackgroundName, int bufSize, bool bMapName )
  2049. {
  2050. Q_strncpy( pszBackgroundName, DEFAULT_BACKGROUND_NAME, bufSize );
  2051. KeyValues *pChapterFile = new KeyValues( pszBackgroundName );
  2052. if ( pChapterFile->LoadFromFile( g_pFileSystem, "scripts/ChapterBackgrounds.txt" ) )
  2053. {
  2054. KeyValues *pChapterRoot = pChapterFile;
  2055. const char *szChapterIndex;
  2056. int nNumChapters = 1;
  2057. KeyValues *pChapters = pChapterFile->GetNextKey();
  2058. if ( bMapName && pChapters )
  2059. {
  2060. const char *pszName = pChapters->GetName();
  2061. if ( pszName && pszName[0] && !Q_strncmp( "BackgroundMaps", pszName, 14 ) )
  2062. {
  2063. pChapterRoot = pChapters;
  2064. pChapters = pChapters->GetFirstSubKey();
  2065. }
  2066. else
  2067. {
  2068. pChapters = NULL;
  2069. }
  2070. }
  2071. else
  2072. {
  2073. pChapters = NULL;
  2074. }
  2075. if ( !pChapters )
  2076. {
  2077. pChapters = pChapterFile->GetFirstSubKey();
  2078. }
  2079. // Find the highest indexed chapter
  2080. while ( pChapters )
  2081. {
  2082. szChapterIndex = pChapters->GetName();
  2083. if ( szChapterIndex )
  2084. {
  2085. int nChapter = atoi(szChapterIndex);
  2086. if( nChapter > nNumChapters )
  2087. nNumChapters = nChapter;
  2088. }
  2089. pChapters = pChapters->GetNextKey();
  2090. }
  2091. int nChapterToLoad = CL_GetBackgroundLevelIndex( nNumChapters );
  2092. // Find the chapter background with this index
  2093. char buf[4];
  2094. Q_snprintf( buf, sizeof(buf), "%d", nChapterToLoad );
  2095. KeyValues *pLoadChapter = pChapterRoot->FindKey(buf);
  2096. // Copy the background name
  2097. if ( pLoadChapter )
  2098. {
  2099. Q_strncpy( pszBackgroundName, pLoadChapter->GetString(), bufSize );
  2100. }
  2101. }
  2102. pChapterFile->deleteThis();
  2103. }
  2104. //-----------------------------------------------------------------------------
  2105. // Purpose: Callback to open the game menus
  2106. //-----------------------------------------------------------------------------
  2107. void CL_CheckToDisplayStartupMenus( const CCommand &args )
  2108. {
  2109. if ( CL_ShouldLoadBackgroundLevel( args ) )
  2110. {
  2111. char szBackgroundName[_MAX_PATH];
  2112. CL_GetBackgroundLevelName( szBackgroundName, sizeof(szBackgroundName), true );
  2113. char cmd[_MAX_PATH];
  2114. Q_snprintf( cmd, sizeof(cmd), "map_background %s\n", szBackgroundName );
  2115. Cbuf_AddText( cmd );
  2116. }
  2117. }
  2118. static float s_fDemoRevealGameUITime = -1;
  2119. float s_fDemoPlayMusicTime = -1;
  2120. static bool s_bIsRavenHolmn = false;
  2121. //-----------------------------------------------------------------------------
  2122. // Purpose: run the special demo logic when transitioning from the trainstation levels
  2123. //----------------------------------------------------------------------------
  2124. void CL_DemoTransitionFromTrainstation()
  2125. {
  2126. // kick them out to GameUI instead and bring up the chapter page with raveholm unlocked
  2127. sv_unlockedchapters.SetValue(6); // unlock ravenholm
  2128. Cbuf_AddText( "sv_cheats 1; fadeout 1.5; sv_cheats 0;");
  2129. Cbuf_Execute();
  2130. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.5;
  2131. s_bIsRavenHolmn = false;
  2132. }
  2133. void CL_DemoTransitionFromRavenholm()
  2134. {
  2135. Cbuf_AddText( "sv_cheats 1; fadeout 2; sv_cheats 0;");
  2136. Cbuf_Execute();
  2137. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.9;
  2138. s_bIsRavenHolmn = true;
  2139. }
  2140. void CL_DemoTransitionFromTestChmb()
  2141. {
  2142. Cbuf_AddText( "sv_cheats 1; fadeout 2; sv_cheats 0;");
  2143. Cbuf_Execute();
  2144. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.9;
  2145. }
  2146. //-----------------------------------------------------------------------------
  2147. // Purpose: make the gameui appear after a certain interval
  2148. //----------------------------------------------------------------------------
  2149. void V_RenderVGuiOnly();
  2150. bool V_CheckGamma();
  2151. void CL_DemoCheckGameUIRevealTime( )
  2152. {
  2153. if ( s_fDemoRevealGameUITime > 0 )
  2154. {
  2155. if ( s_fDemoRevealGameUITime < Sys_FloatTime() )
  2156. {
  2157. s_fDemoRevealGameUITime = -1;
  2158. SCR_BeginLoadingPlaque();
  2159. Cbuf_AddText( "disconnect;");
  2160. CCommand args;
  2161. CL_CheckToDisplayStartupMenus( args );
  2162. s_fDemoPlayMusicTime = Sys_FloatTime() + 1.0;
  2163. }
  2164. }
  2165. if ( s_fDemoPlayMusicTime > 0 )
  2166. {
  2167. V_CheckGamma();
  2168. V_RenderVGuiOnly();
  2169. if ( s_fDemoPlayMusicTime < Sys_FloatTime() )
  2170. {
  2171. s_fDemoPlayMusicTime = -1;
  2172. EngineVGui()->ActivateGameUI();
  2173. if ( CL_IsHL2Demo() )
  2174. {
  2175. if ( s_bIsRavenHolmn )
  2176. {
  2177. Cbuf_AddText( "play music/ravenholm_1.mp3;" );
  2178. }
  2179. else
  2180. {
  2181. EngineVGui()->ShowNewGameDialog(6);// bring up the new game dialog in game UI
  2182. }
  2183. }
  2184. }
  2185. }
  2186. }
  2187. //-----------------------------------------------------------------------------
  2188. // Purpose: setup a debug string that is uploaded on crash
  2189. //----------------------------------------------------------------------------
  2190. extern bool g_bV3SteamInterface;
  2191. char g_minidumpinfo[ 4096 ] = {0};
  2192. PAGED_POOL_INFO_t g_pagedpoolinfo = { 0 };
  2193. void DisplaySystemVersion( char *osversion, int maxlen );
  2194. void CL_SetPagedPoolInfo()
  2195. {
  2196. if ( IsX360() )
  2197. return;
  2198. #if !defined( _X360 ) && !defined(NO_STEAM) && !defined(SWDS)
  2199. Plat_GetPagedPoolInfo( &g_pagedpoolinfo );
  2200. #endif
  2201. }
  2202. void CL_SetSteamCrashComment()
  2203. {
  2204. if ( IsX360() )
  2205. return;
  2206. char map[ 80 ];
  2207. char videoinfo[ 2048 ];
  2208. char misc[ 256 ];
  2209. char driverinfo[ 2048 ];
  2210. char osversion[ 256 ];
  2211. map[ 0 ] = 0;
  2212. driverinfo[ 0 ] = 0;
  2213. videoinfo[ 0 ] = 0;
  2214. misc[ 0 ] = 0;
  2215. osversion[ 0 ] = 0;
  2216. if ( host_state.worldmodel )
  2217. {
  2218. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), map, sizeof( map ) );
  2219. }
  2220. DisplaySystemVersion( osversion, sizeof( osversion ) );
  2221. MaterialAdapterInfo_t info;
  2222. materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), info );
  2223. const char *dxlevel = "Unk";
  2224. int nDxLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
  2225. if ( g_pMaterialSystemHardwareConfig )
  2226. {
  2227. dxlevel = COM_DXLevelToString( nDxLevel ) ;
  2228. }
  2229. // Make a string out of the high part and low parts of driver version
  2230. char szDXDriverVersion[ 64 ];
  2231. Q_snprintf( szDXDriverVersion, sizeof( szDXDriverVersion ), "%ld.%ld.%ld.%ld",
  2232. ( long )( info.m_nDriverVersionHigh>>16 ),
  2233. ( long )( info.m_nDriverVersionHigh & 0xffff ),
  2234. ( long )( info.m_nDriverVersionLow>>16 ),
  2235. ( long )( info.m_nDriverVersionLow & 0xffff ) );
  2236. Q_snprintf( driverinfo, sizeof(driverinfo), "Driver Name: %s\nDriver Version: %s\nVendorId / DeviceId: 0x%x / 0x%x\nSubSystem / Rev: 0x%x / 0x%x\nDXLevel: %s [%d]\nVid: %i x %i",
  2237. info.m_pDriverName,
  2238. szDXDriverVersion,
  2239. info.m_VendorID,
  2240. info.m_DeviceID,
  2241. info.m_SubSysID,
  2242. info.m_Revision,
  2243. dxlevel ? dxlevel : "Unk", nDxLevel,
  2244. videomode->GetModeWidth(), videomode->GetModeHeight() );
  2245. ConVarRef mat_picmip( "mat_picmip" );
  2246. ConVarRef mat_forceaniso( "mat_forceaniso" );
  2247. ConVarRef mat_trilinear( "mat_trilinear" );
  2248. ConVarRef mat_antialias( "mat_antialias" );
  2249. ConVarRef mat_aaquality( "mat_aaquality" );
  2250. ConVarRef r_shadowrendertotexture( "r_shadowrendertotexture" );
  2251. ConVarRef r_flashlightdepthtexture( "r_flashlightdepthtexture" );
  2252. #ifndef _X360
  2253. ConVarRef r_waterforceexpensive( "r_waterforceexpensive" );
  2254. #endif
  2255. ConVarRef r_waterforcereflectentities( "r_waterforcereflectentities" );
  2256. ConVarRef mat_vsync( "mat_vsync" );
  2257. ConVarRef r_rootlod( "r_rootlod" );
  2258. ConVarRef mat_reducefillrate( "mat_reducefillrate" );
  2259. ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" );
  2260. ConVarRef mat_queue_mode( "mat_queue_mode" );
  2261. #ifdef _X360
  2262. Q_snprintf( videoinfo, sizeof(videoinfo), "picmip: %i forceansio: %i trilinear: %i antialias: %i vsync: %i rootlod: %i reducefillrate: %i\n"\
  2263. "shadowrendertotexture: %i r_flashlightdepthtexture %i waterforcereflectentities: %i mat_motion_blur_enabled: %i",
  2264. mat_picmip.GetInt(), mat_forceaniso.GetInt(), mat_trilinear.GetInt(), mat_antialias.GetInt(), mat_aaquality.GetInt(),
  2265. mat_vsync.GetInt(), r_rootlod.GetInt(), mat_reducefillrate.GetInt(),
  2266. r_shadowrendertotexture.GetInt(), r_flashlightdepthtexture.GetInt(),
  2267. r_waterforcereflectentities.GetInt(),
  2268. mat_motion_blur_enabled.GetInt() );
  2269. #else
  2270. Q_snprintf( videoinfo, sizeof(videoinfo), "picmip: %i forceansio: %i trilinear: %i antialias: %i vsync: %i rootlod: %i reducefillrate: %i\n"\
  2271. "shadowrendertotexture: %i r_flashlightdepthtexture %i waterforceexpensive: %i waterforcereflectentities: %i mat_motion_blur_enabled: %i mat_queue_mode %i",
  2272. mat_picmip.GetInt(), mat_forceaniso.GetInt(), mat_trilinear.GetInt(), mat_antialias.GetInt(),
  2273. mat_vsync.GetInt(), r_rootlod.GetInt(), mat_reducefillrate.GetInt(),
  2274. r_shadowrendertotexture.GetInt(), r_flashlightdepthtexture.GetInt(),
  2275. r_waterforceexpensive.GetInt(), r_waterforcereflectentities.GetInt(),
  2276. mat_motion_blur_enabled.GetInt(), mat_queue_mode.GetInt() );
  2277. #endif
  2278. int latency = 0;
  2279. if ( cl.m_NetChannel )
  2280. {
  2281. latency = (int)( 1000.0f * cl.m_NetChannel->GetAvgLatency( FLOW_OUTGOING ) );
  2282. }
  2283. Q_snprintf( misc, sizeof( misc ), "skill:%i rate %i update %i cmd %i latency %i msec",
  2284. skill.GetInt(),
  2285. cl_rate->GetInt(),
  2286. (int)cl_updaterate->GetFloat(),
  2287. (int)cl_cmdrate->GetFloat(),
  2288. latency
  2289. );
  2290. const char *pNetChannel = "Not Connected";
  2291. if ( cl.m_NetChannel )
  2292. {
  2293. pNetChannel = cl.m_NetChannel->GetRemoteAddress().ToString();
  2294. }
  2295. CL_SetPagedPoolInfo();
  2296. Q_snprintf( g_minidumpinfo, sizeof(g_minidumpinfo),
  2297. "Map: %s\n"\
  2298. "Game: %s\n"\
  2299. "Build: %i\n"\
  2300. "Misc: %s\n"\
  2301. "Net: %s\n"\
  2302. "cmdline:%s\n"\
  2303. "driver: %s\n"\
  2304. "video: %s\n"\
  2305. "OS: %s\n",
  2306. map, com_gamedir, build_number(), misc, pNetChannel, CommandLine()->GetCmdLine(), driverinfo, videoinfo, osversion );
  2307. char full[ 4096 ];
  2308. Q_snprintf( full, sizeof( full ), "%sPP PAGES: used: %d, free %d\n", g_minidumpinfo, (int)g_pagedpoolinfo.numPagesUsed, (int)g_pagedpoolinfo.numPagesFree );
  2309. #ifndef NO_STEAM
  2310. SteamAPI_SetMiniDumpComment( full );
  2311. #endif
  2312. }
  2313. //
  2314. // register commands
  2315. //
  2316. static ConCommand startupmenu( "startupmenu", &CL_CheckToDisplayStartupMenus, "Opens initial menu screen and loads the background bsp, but only if no other level is being loaded, and we're not in developer mode." );
  2317. ConVar cl_language( "cl_language", "english", FCVAR_USERINFO, "Language (from HKCU\\Software\\Valve\\Steam\\Language)" );
  2318. void CL_InitLanguageCvar()
  2319. {
  2320. if ( Steam3Client().SteamApps() )
  2321. {
  2322. cl_language.SetValue( Steam3Client().SteamApps()->GetCurrentGameLanguage() );
  2323. }
  2324. else
  2325. {
  2326. cl_language.SetValue( "english" );
  2327. }
  2328. }
  2329. void CL_ChangeCloudSettingsCvar( IConVar *var, const char *pOldValue, float flOldValue );
  2330. ConVar cl_cloud_settings( "cl_cloud_settings", "1", FCVAR_HIDDEN, "Cloud enabled from (from HKCU\\Software\\Valve\\Steam\\Apps\\appid\\Cloud)", CL_ChangeCloudSettingsCvar );
  2331. void CL_ChangeCloudSettingsCvar( IConVar *var, const char *pOldValue, float flOldValue )
  2332. {
  2333. // !! bug do i need to do something linux-wise here.
  2334. if ( IsPC() && Steam3Client().SteamRemoteStorage() )
  2335. {
  2336. ConVarRef ref( var->GetName() );
  2337. Steam3Client().SteamRemoteStorage()->SetCloudEnabledForApp( ref.GetBool() );
  2338. if ( cl_cloud_settings.GetInt() == STEAMREMOTESTORAGE_CLOUD_ON && flOldValue == STEAMREMOTESTORAGE_CLOUD_OFF )
  2339. {
  2340. // If we were just turned on, get our configuration from remote storage.
  2341. engineClient->ReadConfiguration( false );
  2342. engineClient->ClientCmd_Unrestricted( "refresh_options_dialog" );
  2343. }
  2344. }
  2345. }
  2346. void CL_InitCloudSettingsCvar()
  2347. {
  2348. if ( IsPC() && Steam3Client().SteamRemoteStorage() )
  2349. {
  2350. int iCloudSettings = STEAMREMOTESTORAGE_CLOUD_OFF;
  2351. if ( Steam3Client().SteamRemoteStorage()->IsCloudEnabledForApp() )
  2352. iCloudSettings = STEAMREMOTESTORAGE_CLOUD_ON;
  2353. cl_cloud_settings.SetValue( iCloudSettings );
  2354. }
  2355. else
  2356. {
  2357. // If not on PC or steam not available, set to 0 to make sure no replication occurs or is attempted
  2358. cl_cloud_settings.SetValue( STEAMREMOTESTORAGE_CLOUD_OFF );
  2359. }
  2360. }
  2361. /*
  2362. =================
  2363. CL_Init
  2364. =================
  2365. */
  2366. void CL_Init (void)
  2367. {
  2368. cl.Clear();
  2369. CL_InitLanguageCvar();
  2370. CL_InitCloudSettingsCvar();
  2371. }
  2372. //-----------------------------------------------------------------------------
  2373. // Purpose:
  2374. //-----------------------------------------------------------------------------
  2375. void CL_Shutdown( void )
  2376. {
  2377. }
  2378. CON_COMMAND_F( cl_fullupdate, "Forces the server to send a full update packet", FCVAR_CHEAT )
  2379. {
  2380. cl.ForceFullUpdate();
  2381. }
  2382. #ifdef STAGING_ONLY
  2383. CON_COMMAND( cl_download, "Downloads a file from server." )
  2384. {
  2385. if ( args.ArgC() != 2 )
  2386. return;
  2387. if ( !cl.m_NetChannel )
  2388. return;
  2389. cl.m_NetChannel->RequestFile( args[ 1 ] ); // just for testing stuff
  2390. }
  2391. #endif // STAGING_ONLY
  2392. CON_COMMAND_F( setinfo, "Adds a new user info value", FCVAR_CLIENTCMD_CAN_EXECUTE )
  2393. {
  2394. if ( args.ArgC() != 3 )
  2395. {
  2396. Msg("Syntax: setinfo <key> <value>\n");
  2397. return;
  2398. }
  2399. const char *name = args[ 1 ];
  2400. const char *value = args[ 2 ];
  2401. // Prevent players manually changing their name (their Steam account provides it now)
  2402. if ( Q_stricmp( name, "name" ) == 0 )
  2403. return;
  2404. // Discard any convar change request if contains funky characters
  2405. bool bFunky = false;
  2406. for (const char *s = name ; *s != '\0' ; ++s )
  2407. {
  2408. if ( !V_isalnum(*s) && *s != '_' )
  2409. {
  2410. bFunky = true;
  2411. break;
  2412. }
  2413. }
  2414. if ( bFunky )
  2415. {
  2416. Msg( "Ignoring convar change request for variable '%s', which contains invalid character(s)\n", name );
  2417. return;
  2418. }
  2419. ConCommandBase *pCommand = g_pCVar->FindCommandBase( name );
  2420. ConVarRef sv_cheats( "sv_cheats" );
  2421. if ( pCommand )
  2422. {
  2423. if ( pCommand->IsCommand() )
  2424. {
  2425. Msg("Name %s is already registered as console command\n", name );
  2426. return;
  2427. }
  2428. if ( !pCommand->IsFlagSet(FCVAR_USERINFO) )
  2429. {
  2430. Msg("Convar %s is already registered but not as user info value\n", name );
  2431. return;
  2432. }
  2433. if ( pCommand->IsFlagSet( FCVAR_NOT_CONNECTED ) )
  2434. {
  2435. #ifndef DEDICATED
  2436. // Connected to server?
  2437. if ( cl.IsConnected() )
  2438. {
  2439. extern IBaseClientDLL *g_ClientDLL;
  2440. if ( pCommand->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( NULL ) )
  2441. {
  2442. // Client.dll is allowing the convar change
  2443. }
  2444. else
  2445. {
  2446. ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", pCommand->GetName() );
  2447. return;
  2448. }
  2449. }
  2450. #endif
  2451. }
  2452. if ( IsPC() )
  2453. {
  2454. #if !defined(NO_STEAM)
  2455. EUniverse eUniverse = GetSteamUniverse();
  2456. if ( (( eUniverse != k_EUniverseBeta ) && ( eUniverse != k_EUniverseDev )) && pCommand->IsFlagSet( FCVAR_DEVELOPMENTONLY ) )
  2457. return;
  2458. #endif
  2459. }
  2460. if ( pCommand->IsFlagSet( FCVAR_CHEAT ) && sv_cheats.GetBool() == 0 )
  2461. {
  2462. Msg("Convar %s is marked as cheat and cheats are off\n", name );
  2463. return;
  2464. }
  2465. }
  2466. else
  2467. {
  2468. // cvar not found, create it now
  2469. char *pszString = V_strdup( name );
  2470. pCommand = new ConVar( pszString, "", FCVAR_USERINFO, "Custom user info value" );
  2471. }
  2472. ConVar *pConVar = (ConVar*)pCommand;
  2473. pConVar->SetValue( value );
  2474. if ( cl.IsConnected() )
  2475. {
  2476. // send changed cvar to server
  2477. NET_SetConVar convar( name, value );
  2478. cl.m_NetChannel->SendNetMsg( convar );
  2479. }
  2480. }
  2481. CON_COMMAND( cl_precacheinfo, "Show precache info (client)." )
  2482. {
  2483. if ( args.ArgC() == 2 )
  2484. {
  2485. cl.DumpPrecacheStats( args[ 1 ] );
  2486. return;
  2487. }
  2488. // Show all data
  2489. cl.DumpPrecacheStats( MODEL_PRECACHE_TABLENAME );
  2490. cl.DumpPrecacheStats( DECAL_PRECACHE_TABLENAME );
  2491. cl.DumpPrecacheStats( SOUND_PRECACHE_TABLENAME );
  2492. cl.DumpPrecacheStats( GENERIC_PRECACHE_TABLENAME );
  2493. }
  2494. //-----------------------------------------------------------------------------
  2495. // Purpose:
  2496. // Input : *object -
  2497. // stringTable -
  2498. // stringNumber -
  2499. // *newString -
  2500. // *newData -
  2501. //-----------------------------------------------------------------------------
  2502. void Callback_ModelChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2503. {
  2504. if ( stringTable == cl.m_pModelPrecacheTable )
  2505. {
  2506. // Index 0 is always NULL, just ignore it
  2507. // Index 1 == the world, don't
  2508. if ( stringNumber > 1 )
  2509. {
  2510. // DevMsg( "Preloading model %s\n", newString );
  2511. cl.SetModel( stringNumber );
  2512. }
  2513. }
  2514. else
  2515. {
  2516. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  2517. }
  2518. }
  2519. //-----------------------------------------------------------------------------
  2520. // Purpose:
  2521. // Input : *object -
  2522. // stringTable -
  2523. // stringNumber -
  2524. // *newString -
  2525. // *newData -
  2526. //-----------------------------------------------------------------------------
  2527. void Callback_GenericChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2528. {
  2529. if ( stringTable == cl.m_pGenericPrecacheTable )
  2530. {
  2531. // Index 0 is always NULL, just ignore it
  2532. if ( stringNumber >= 1 )
  2533. {
  2534. cl.SetGeneric( stringNumber );
  2535. }
  2536. }
  2537. else
  2538. {
  2539. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  2540. }
  2541. }
  2542. //-----------------------------------------------------------------------------
  2543. // Purpose:
  2544. // Input : *object -
  2545. // stringTable -
  2546. // stringNumber -
  2547. // *newString -
  2548. // *newData -
  2549. //-----------------------------------------------------------------------------
  2550. void Callback_SoundChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2551. {
  2552. if ( stringTable == cl.m_pSoundPrecacheTable )
  2553. {
  2554. // Index 0 is always NULL, just ignore it
  2555. if ( stringNumber >= 1 )
  2556. {
  2557. cl.SetSound( stringNumber );
  2558. }
  2559. }
  2560. else
  2561. {
  2562. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  2563. }
  2564. }
  2565. void Callback_DecalChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2566. {
  2567. if ( stringTable == cl.m_pDecalPrecacheTable )
  2568. {
  2569. cl.SetDecal( stringNumber );
  2570. }
  2571. else
  2572. {
  2573. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  2574. }
  2575. }
  2576. void Callback_InstanceBaselineChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2577. {
  2578. Assert( stringTable == cl.m_pInstanceBaselineTable );
  2579. // cl.UpdateInstanceBaseline( stringNumber );
  2580. }
  2581. void Callback_UserInfoChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2582. {
  2583. Assert( stringTable == cl.m_pUserInfoTable );
  2584. // stringnumber == player slot
  2585. player_info_t *player = (player_info_t*)newData;
  2586. if ( !player )
  2587. return; // player left the game
  2588. // request custom user files if necessary
  2589. for ( int i=0; i<MAX_CUSTOM_FILES; i++ )
  2590. {
  2591. cl.CheckOthersCustomFile( player->customFiles[i] );
  2592. }
  2593. // fire local client event game event
  2594. IGameEvent * event = g_GameEventManager.CreateEvent( "player_info" );
  2595. if ( event )
  2596. {
  2597. event->SetInt( "userid", player->userID );
  2598. event->SetInt( "friendsid", player->friendsID );
  2599. event->SetInt( "index", stringNumber );
  2600. event->SetString( "name", player->name );
  2601. event->SetString( "networkid", player->guid );
  2602. event->SetBool( "bot", player->fakeplayer );
  2603. g_GameEventManager.FireEventClientSide( event );
  2604. }
  2605. }
  2606. void Callback_DynamicModelsChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  2607. {
  2608. #ifndef SWDS
  2609. extern IVModelInfoClient *modelinfoclient;
  2610. if ( modelinfoclient )
  2611. {
  2612. modelinfoclient->OnDynamicModelsStringTableChange( stringNumber, newString, newData );
  2613. }
  2614. #endif
  2615. }
  2616. void CL_HookClientStringTables()
  2617. {
  2618. // install hooks
  2619. int numTables = cl.m_StringTableContainer->GetNumTables();
  2620. for ( int i =0; i<numTables; i++)
  2621. {
  2622. // iterate through server tables
  2623. CNetworkStringTable *pTable =
  2624. (CNetworkStringTable*)cl.m_StringTableContainer->GetTable( i );
  2625. if ( !pTable )
  2626. continue;
  2627. cl.HookClientStringTable( pTable->GetTableName() );
  2628. }
  2629. }
  2630. // Installs the all, and invokes cb for all existing items
  2631. void CL_InstallAndInvokeClientStringTableCallbacks()
  2632. {
  2633. // install hooks
  2634. int numTables = cl.m_StringTableContainer->GetNumTables();
  2635. for ( int i =0; i<numTables; i++)
  2636. {
  2637. // iterate through server tables
  2638. CNetworkStringTable *pTable =
  2639. (CNetworkStringTable*)cl.m_StringTableContainer->GetTable( i );
  2640. if ( !pTable )
  2641. continue;
  2642. pfnStringChanged pOldFunction = pTable->GetCallback();
  2643. cl.InstallStringTableCallback( pTable->GetTableName() );
  2644. pfnStringChanged pNewFunction = pTable->GetCallback();
  2645. if ( !pNewFunction )
  2646. continue;
  2647. // We already had it installed (e.g., from client .dll) so all of the callbacks have been called and don't need a second dose
  2648. if ( pNewFunction == pOldFunction )
  2649. continue;
  2650. for ( int j = 0; j < pTable->GetNumStrings(); ++j )
  2651. {
  2652. int userDataSize;
  2653. const void *pUserData = pTable->GetStringUserData( j, &userDataSize );
  2654. (*pNewFunction)( NULL, pTable, j, pTable->GetString( j ), pUserData );
  2655. }
  2656. }
  2657. }
  2658. // Singleton client state
  2659. CClientState cl;