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

2349 lines
65 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "client_pch.h"
  8. #include "networkstringtabledefs.h"
  9. #include <checksum_md5.h>
  10. #include <iregistry.h>
  11. #include "userid.h"
  12. #include "pure_server.h"
  13. #include "netmessages.h"
  14. #include "cl_demo.h"
  15. #include "host_state.h"
  16. #include "host.h"
  17. #include "gl_matsysiface.h"
  18. #include "vgui_baseui_interface.h"
  19. #include "tier0/icommandline.h"
  20. #include <proto_oob.h>
  21. #include "checksum_engine.h"
  22. #include "filesystem_engine.h"
  23. #include "logofile_shared.h"
  24. #include "sound.h"
  25. #include "decal.h"
  26. #include "networkstringtableclient.h"
  27. #include "dt_send_eng.h"
  28. #include "ents_shared.h"
  29. #include "cl_ents_parse.h"
  30. #include "cl_entityreport.h"
  31. #include "MapReslistGenerator.h"
  32. #include "DownloadListGenerator.h"
  33. #include "GameEventManager.h"
  34. #include "vgui_baseui_interface.h"
  35. #include "clockdriftmgr.h"
  36. #include "snd_audio_source.h"
  37. #include "vgui_controls/Controls.h"
  38. #include "vgui/ILocalize.h"
  39. #include "download.h"
  40. #include "checksum_engine.h"
  41. #include "ModelInfo.h"
  42. #include "materialsystem/imaterial.h"
  43. #include "materialsystem/materialsystem_config.h"
  44. #include "tier1/fmtstr.h"
  45. #include "cl_steamauth.h"
  46. #include "matchmaking/imatchframework.h"
  47. #include "audio/private/snd_sfx.h"
  48. #include "tier0/platform.h"
  49. #include "tier0/systeminformation.h"
  50. // memdbgon must be the last include file in a .cpp file!!!
  51. #include "tier0/memdbgon.h"
  52. ConVar cl_timeout( "cl_timeout", "30", FCVAR_ARCHIVE, "After this many seconds without receiving a packet from the server, the client will disconnect itself"
  53. #ifndef _DEBUG
  54. , true, 4, true, 30
  55. #endif
  56. );
  57. static ConVar cl_forcepreload( "cl_forcepreload", "0", FCVAR_ARCHIVE, "Whether we should force preloading.");
  58. static ConVar cl_downloadfilter( "cl_downloadfilter", "all", FCVAR_ARCHIVE, "Determines which files can be downloaded from the server (all, none, nosounds)" );
  59. static ConVar cl_download_demoplayer( "cl_download_demoplayer", "1", FCVAR_RELEASE, "Determines whether downloads of external resources are allowed during demo playback (0:no,1:workshop,2:all)" );
  60. ConVar cl_debug_ugc_downloads( "cl_debug_ugc_downloads", "0", FCVAR_RELEASE );
  61. extern ConVar sv_downloadurl;
  62. extern ConVar sv_consistency;
  63. extern ConVar cl_hideserverip;
  64. extern bool g_bServerGameDLLGreaterThanV5;
  65. //////////////////////////////////////////////////////////////////////
  66. // Construction/Destruction
  67. //////////////////////////////////////////////////////////////////////
  68. CClientState::CClientState()
  69. {
  70. m_bMarkedCRCsUnverified = false;
  71. demonum = -1;
  72. m_tickRemainder = 0;
  73. m_frameTime = 0;
  74. m_pAreaBits = NULL;
  75. m_hWaitForResourcesHandle = NULL;
  76. m_bUpdateSteamResources = false;
  77. m_bShownSteamResourceUpdateProgress = false;
  78. m_pPureServerWhitelist = NULL;
  79. m_bCheckCRCsWithServer = false;
  80. m_flLastCRCBatchTime = 0;
  81. m_nFriendsID = 0;
  82. m_FriendsName[ 0 ] = 0;
  83. m_flLastServerTickTime = -1.0f;
  84. lastoutgoingcommand = 0;
  85. chokedcommands = 0;
  86. last_command_ack = 0;
  87. last_server_tick = 0;
  88. command_ack = 0;
  89. m_nSoundSequence = 0;
  90. serverCRC = 0;
  91. serverClientSideDllCRC = 0;
  92. viewangles.Init();
  93. Q_memset( m_chAreaBits, 0, sizeof( m_chAreaBits ) );
  94. Q_memset( m_chAreaPortalBits, 0, sizeof( m_chAreaPortalBits ) );
  95. m_bAreaBitsValid = false;
  96. addangletotal = 0.0f;
  97. prevaddangletotal = 0.0f;
  98. cdtrack = 0;
  99. Q_memset( m_FriendsName, 0, sizeof( m_FriendsName ) );
  100. m_pModelPrecacheTable = NULL;
  101. m_pDynamicModelTable = NULL;
  102. m_pGenericPrecacheTable = NULL;
  103. m_pSoundPrecacheTable = NULL;
  104. m_pDecalPrecacheTable = NULL;
  105. m_pInstanceBaselineTable = NULL;
  106. m_pLightStyleTable = NULL;
  107. m_pUserInfoTable = NULL;
  108. m_pServerStartupTable = NULL;
  109. m_pDownloadableFileTable = NULL;
  110. m_bDownloadResources = false;
  111. m_bDownloadingUGCMap = false;
  112. insimulation = false;
  113. oldtickcount = 0;
  114. ishltv = false;
  115. #if defined( REPLAY_ENABLED )
  116. isreplay = false;
  117. #endif
  118. ResetHltvReplayState();
  119. }
  120. void CClientState::ResetHltvReplayState()
  121. {
  122. m_nHltvReplayDelay = 0;
  123. m_nHltvReplayStopAt = 0;
  124. m_nHltvReplayStartAt = 0;
  125. m_nHltvReplaySlowdownBeginAt = 0;
  126. m_nHltvReplaySlowdownEndAt = 0;
  127. m_flHltvReplaySlowdownRate = 1.0f;
  128. }
  129. CClientState::~CClientState()
  130. {
  131. if ( m_pPureServerWhitelist )
  132. m_pPureServerWhitelist->Release();
  133. }
  134. // HL1 CD Key
  135. #define GUID_LEN 13
  136. /*
  137. =======================
  138. CL_GetCDKeyHash()
  139. Connections will now use a hashed cd key value
  140. A LAN server will know not to allows more then xxx users with the same CD Key
  141. =======================
  142. */
  143. const char *CClientState::GetCDKeyHash( void )
  144. {
  145. if ( IsPC() )
  146. {
  147. char szKeyBuffer[256]; // Keys are about 13 chars long.
  148. static char szHashedKeyBuffer[64];
  149. int nKeyLength;
  150. bool bDedicated = false;
  151. MD5Context_t ctx;
  152. unsigned char digest[16]; // The MD5 Hash
  153. nKeyLength = Q_snprintf( szKeyBuffer, sizeof( szKeyBuffer ), "%s", registry->ReadString( "key", "" ) );
  154. if (bDedicated)
  155. {
  156. ConMsg("Key has no meaning on dedicated server...\n");
  157. return "";
  158. }
  159. if ( nKeyLength == 0 )
  160. {
  161. nKeyLength = 13;
  162. Q_strncpy( szKeyBuffer, "1234567890123", sizeof( szKeyBuffer ) );
  163. Assert( Q_strlen( szKeyBuffer ) == nKeyLength );
  164. DevMsg( "Missing CD Key from registry, inserting blank key\n" );
  165. registry->WriteString( "key", szKeyBuffer );
  166. }
  167. if (nKeyLength <= 0 ||
  168. nKeyLength >= 256 )
  169. {
  170. ConMsg("Bogus key length on CD Key...\n");
  171. return "";
  172. }
  173. // Now get the md5 hash of the key
  174. memset( &ctx, 0, sizeof( ctx ) );
  175. memset( digest, 0, sizeof( digest ) );
  176. MD5Init(&ctx);
  177. MD5Update(&ctx, (unsigned char*)szKeyBuffer, nKeyLength);
  178. MD5Final(digest, &ctx);
  179. Q_strncpy ( szHashedKeyBuffer, MD5_Print ( digest, sizeof( digest ) ), sizeof( szHashedKeyBuffer ) );
  180. return szHashedKeyBuffer;
  181. }
  182. return "12345678901234567890123456789012";
  183. }
  184. void CClientState::SendClientInfo( void )
  185. {
  186. CCLCMsg_ClientInfo_t info;
  187. info.set_send_table_crc( SendTable_GetCRC() );
  188. info.set_server_count( m_nServerCount );
  189. info.set_is_hltv( false );
  190. #if defined( REPLAY_ENABLED )
  191. info.set_is_replay( false );
  192. #endif
  193. #if !defined( NO_STEAM )
  194. info.set_friends_id( Steam3Client().SteamUser() ? Steam3Client().SteamUser()->GetSteamID().GetAccountID() : 0 );
  195. #else
  196. info.set_friends_id( 0 );
  197. #endif
  198. info.set_friends_name( m_FriendsName );
  199. CheckOwnCustomFiles(); // load & verfiy custom player files
  200. for ( int i=0; i< MAX_CUSTOM_FILES; i++ )
  201. {
  202. info.add_custom_files( m_nCustomFiles[i].crc );
  203. }
  204. m_NetChannel->SendNetMsg( info );
  205. }
  206. void CClientState::SendLoadingProgress( int nProgress )
  207. {
  208. if ( !m_NetChannel || nProgress <= m_nLastProgressPercent )
  209. {
  210. return;
  211. }
  212. CCLCMsg_LoadingProgress_t info;
  213. info.set_progress( nProgress );
  214. m_nLastProgressPercent = nProgress;
  215. m_NetChannel->SendNetMsg( info );
  216. }
  217. void CClientState::SendServerCmdKeyValues( KeyValues *pKeyValues )
  218. {
  219. if ( !pKeyValues )
  220. return;
  221. // Ensure keyvalues are deleted per contract obligations
  222. KeyValues::AutoDelete autodelete_pKeyValues( pKeyValues );
  223. if ( !m_NetChannel )
  224. return;
  225. CCLCMsg_CmdKeyValues_t msg;
  226. CmdKeyValuesHelper::CLCMsg_SetKeyValues( msg, pKeyValues );
  227. m_NetChannel->SendNetMsg( msg );
  228. }
  229. bool CClientState::SendNetMsg( INetMessage &msg, bool bForceReliable, bool bVoice )
  230. {
  231. if ( m_NetChannel )
  232. return m_NetChannel->SendNetMsg( msg, bForceReliable, bVoice );
  233. else
  234. return false;
  235. }
  236. extern IVEngineClient *engineClient;
  237. //-----------------------------------------------------------------------------
  238. // Purpose: A svc_signonnum has been received, perform a client side setup
  239. // Output : void CL_SignonReply
  240. //-----------------------------------------------------------------------------
  241. bool CClientState::SetSignonState ( int state, int count, const CNETMsg_SignonState *msg )
  242. {
  243. int nOldSignonState = m_nSignonState;
  244. ResetHltvReplayState();
  245. if ( !CBaseClientState::SetSignonState( state, count, msg ) )
  246. {
  247. CL_Retry();
  248. return false;
  249. }
  250. // ConDMsg ("Signon state: %i\n", state );
  251. COM_TimestampedLog( "CClientState::SetSignonState: start %i", state );
  252. switch ( m_nSignonState )
  253. {
  254. case SIGNONSTATE_CHALLENGE :
  255. m_bMarkedCRCsUnverified = false; // Remember that we just connected to a new server so it'll
  256. // reverify any necessary file CRCs on this server.
  257. EngineVGui()->UpdateProgressBar(PROGRESS_SIGNONCHALLENGE);
  258. break;
  259. case SIGNONSTATE_CONNECTED :
  260. {
  261. EngineVGui()->UpdateProgressBar(PROGRESS_SIGNONCONNECTED);
  262. // make sure it's turned off when connecting
  263. EngineVGui()->HideDebugSystem();
  264. SCR_BeginLoadingPlaque ();
  265. // Clear channel and stuff
  266. m_NetChannel->Clear();
  267. // allow longer timeout
  268. m_NetChannel->SetTimeout( SIGNON_TIME_OUT );
  269. m_NetChannel->SetMaxBufferSize( true, NET_MAX_PAYLOAD );
  270. // set user settings (rate etc)
  271. CNETMsg_SetConVar_t convars;
  272. Host_BuildUserInfoUpdateMessage( m_nSplitScreenSlot, convars.mutable_convars(), false );
  273. m_NetChannel->SendNetMsg( convars );
  274. }
  275. break;
  276. case SIGNONSTATE_NEW :
  277. {
  278. EngineVGui()->UpdateProgressBar(PROGRESS_SIGNONNEW);
  279. if ( cl_download_demoplayer.GetBool() || !demoplayer->IsPlayingBack() )
  280. {
  281. // When playing back a demo we need to suspend packet reading here
  282. if ( demoplayer->IsPlayingBack() )
  283. {
  284. demoplayer->SetPacketReadSuspended( true );
  285. }
  286. // start making sure we have all the specified resources
  287. StartUpdatingSteamResources();
  288. }
  289. else
  290. {
  291. // during demo playback dont try to download resource
  292. FinishSignonState_New();
  293. }
  294. // don't tell the server yet that we've entered this state
  295. COM_TimestampedLog( "CClientState::SetSignonState: end %i", state );
  296. return true;
  297. }
  298. break;
  299. case SIGNONSTATE_PRESPAWN :
  300. EngineVGui()->UpdateProgressBar(PROGRESS_SENDSIGNONDATA);
  301. m_nSoundSequence = 1; // reset sound sequence number after receiving signon sounds
  302. break;
  303. case SIGNONSTATE_SPAWN :
  304. {
  305. extern float NET_GetFakeLag();
  306. Assert( g_ClientDLL );
  307. EngineVGui()->UpdateProgressBar(PROGRESS_SIGNONSPAWN);
  308. // Tell client .dll about the transition
  309. char mapname[256];
  310. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  311. COM_TimestampedLog( "LevelInitPreEntity: start %i", state );
  312. // enable prediction if the server isn't local or the user is requesting fake lag
  313. g_ClientGlobalVariables.m_bRemoteClient = !Host_IsLocalServer() || (NET_GetFakeLag() != 0.0f);
  314. g_ClientDLL->LevelInitPreEntity(mapname);
  315. COM_TimestampedLog( "LevelInitPreEntity: end %i", state );
  316. audiosourcecache->LevelInit( mapname );
  317. // stop recording demo header
  318. demorecorder->SetSignonState( SIGNONSTATE_SPAWN );
  319. }
  320. break;
  321. case SIGNONSTATE_FULL:
  322. {
  323. CL_FullyConnected();
  324. if ( !m_NetChannel )
  325. return false; // disconnected during connection
  326. m_NetChannel->SetTimeout( cl_timeout.GetFloat() );
  327. m_NetChannel->SetMaxBufferSize( true, NET_MAX_DATAGRAM_PAYLOAD );
  328. HostState_OnClientConnected();
  329. // If we came through a level transition with splitscreen guys, then we need to force their
  330. // signon state to be SIGNONSTATE_FULL, too
  331. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  332. {
  333. CBaseClientState &cl = GetLocalClient( hh );
  334. if ( &cl == this )
  335. continue;
  336. cl.m_nSignonState = SIGNONSTATE_FULL;
  337. }
  338. }
  339. break;
  340. case SIGNONSTATE_CHANGELEVEL:
  341. m_NetChannel->SetTimeout( SIGNON_TIME_OUT ); // allow 5 minutes timeout
  342. m_nLastProgressPercent = -1;
  343. if ( m_nMaxClients > 1 )
  344. {
  345. // start progress bar immediately for multiplayer level transitions
  346. EngineVGui()->EnabledProgressBarForNextLoad();
  347. }
  348. SCR_BeginLoadingPlaque( msg->map_name().c_str() );
  349. if ( m_nMaxClients > 1 )
  350. {
  351. EngineVGui()->UpdateProgressBar(PROGRESS_CHANGELEVEL);
  352. }
  353. break;
  354. }
  355. COM_TimestampedLog( "CClientState::SetSignonState: end %i", state );
  356. KeyValues *pEvent = new KeyValues( "OnEngineClientSignonStateChange" );
  357. pEvent->SetInt( "slot", m_nSplitScreenSlot );
  358. if ( m_bServerConnectionRedirect && nOldSignonState >= SIGNONSTATE_CONNECTED && state < SIGNONSTATE_CONNECTED )
  359. nOldSignonState = state; // during server redirect attempt to keep the MMS session
  360. pEvent->SetInt( "old", nOldSignonState );
  361. pEvent->SetInt( "new", state );
  362. pEvent->SetInt( "count", count );
  363. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pEvent );
  364. if ( m_nSignonState >= SIGNONSTATE_CONNECTED )
  365. {
  366. // tell server that we entered now that state
  367. CNETMsg_SignonState_t msgSignonState( m_nSignonState, count);
  368. m_NetChannel->SendNetMsg( msgSignonState );
  369. }
  370. return true;
  371. }
  372. bool CClientState::HookClientStringTable( char const *tableName )
  373. {
  374. INetworkStringTable *table = GetStringTable( tableName );
  375. if ( !table )
  376. {
  377. // If engine takes a pass, allow client dll to hook in its callbacks
  378. if ( g_ClientDLL )
  379. {
  380. g_ClientDLL->InstallStringTableCallback( tableName );
  381. }
  382. return false;
  383. }
  384. // Hook Model Precache table
  385. if ( !Q_strcasecmp( tableName, MODEL_PRECACHE_TABLENAME ) )
  386. {
  387. m_pModelPrecacheTable = table;
  388. return true;
  389. }
  390. if ( !Q_strcasecmp( tableName, GENERIC_PRECACHE_TABLENAME ) )
  391. {
  392. m_pGenericPrecacheTable = table;
  393. return true;
  394. }
  395. if ( !Q_strcasecmp( tableName, SOUND_PRECACHE_TABLENAME ) )
  396. {
  397. m_pSoundPrecacheTable = table;
  398. return true;
  399. }
  400. if ( !Q_strcasecmp( tableName, DECAL_PRECACHE_TABLENAME ) )
  401. {
  402. // Cache the id
  403. m_pDecalPrecacheTable = table;
  404. return true;
  405. }
  406. if ( !Q_strcasecmp( tableName, INSTANCE_BASELINE_TABLENAME ) )
  407. {
  408. // Cache the id
  409. m_pInstanceBaselineTable = table;
  410. return true;
  411. }
  412. if ( !Q_strcasecmp( tableName, LIGHT_STYLES_TABLENAME ) )
  413. {
  414. // Cache the id
  415. m_pLightStyleTable = table;
  416. return true;
  417. }
  418. if ( !Q_strcasecmp( tableName, USER_INFO_TABLENAME ) )
  419. {
  420. // Cache the id
  421. m_pUserInfoTable = table;
  422. return true;
  423. }
  424. if ( !Q_strcasecmp( tableName, SERVER_STARTUP_DATA_TABLENAME ) )
  425. {
  426. // Cache the id
  427. m_pServerStartupTable = table;
  428. return true;
  429. }
  430. if ( !Q_strcasecmp( tableName, DOWNLOADABLE_FILE_TABLENAME ) )
  431. {
  432. // Cache the id
  433. m_pDownloadableFileTable = table;
  434. return true;
  435. }
  436. if ( !Q_strcasecmp( tableName, DYNAMIC_MODEL_TABLENAME ) )
  437. {
  438. m_pDynamicModelTable = table;
  439. return true;
  440. }
  441. // If engine takes a pass, allow client dll to hook in its callbacks
  442. g_ClientDLL->InstallStringTableCallback( tableName );
  443. return false;
  444. }
  445. bool CClientState::InstallEngineStringTableCallback( char const *tableName )
  446. {
  447. INetworkStringTable *table = GetStringTable( tableName );
  448. if ( !table )
  449. return false;
  450. // Hook Model Precache table
  451. if ( !Q_strcasecmp( tableName, MODEL_PRECACHE_TABLENAME ) )
  452. {
  453. table->SetStringChangedCallback( NULL, Callback_ModelChanged );
  454. return true;
  455. }
  456. if ( !Q_strcasecmp( tableName, GENERIC_PRECACHE_TABLENAME ) )
  457. {
  458. // Install the callback
  459. table->SetStringChangedCallback( NULL, Callback_GenericChanged );
  460. return true;
  461. }
  462. if ( !Q_strcasecmp( tableName, SOUND_PRECACHE_TABLENAME ) )
  463. {
  464. // Install the callback
  465. table->SetStringChangedCallback( NULL, Callback_SoundChanged );
  466. return true;
  467. }
  468. if ( !Q_strcasecmp( tableName, DECAL_PRECACHE_TABLENAME ) )
  469. {
  470. // Install the callback
  471. table->SetStringChangedCallback( NULL, Callback_DecalChanged );
  472. return true;
  473. }
  474. if ( !Q_strcasecmp( tableName, INSTANCE_BASELINE_TABLENAME ) )
  475. {
  476. // Install the callback (already done above)
  477. table->SetStringChangedCallback( NULL, Callback_InstanceBaselineChanged );
  478. return true;
  479. }
  480. if ( !Q_strcasecmp( tableName, LIGHT_STYLES_TABLENAME ) )
  481. {
  482. return true;
  483. }
  484. if ( !Q_strcasecmp( tableName, USER_INFO_TABLENAME ) )
  485. {
  486. // Install the callback
  487. table->SetStringChangedCallback( NULL, Callback_UserInfoChanged );
  488. return true;
  489. }
  490. if ( !Q_strcasecmp( tableName, SERVER_STARTUP_DATA_TABLENAME ) )
  491. {
  492. return true;
  493. }
  494. if ( !Q_strcasecmp( tableName, DOWNLOADABLE_FILE_TABLENAME ) )
  495. {
  496. return true;
  497. }
  498. if ( !Q_strcasecmp( tableName, DYNAMIC_MODEL_TABLENAME ) )
  499. {
  500. table->SetStringChangedCallback( NULL, Callback_DynamicModelChanged );
  501. m_pDynamicModelTable = table;
  502. return true;
  503. }
  504. // The the client.dll have a shot at it
  505. return false;
  506. }
  507. void CClientState::InstallStringTableCallback( char const *tableName )
  508. {
  509. // Let engine hook callbacks before we read in any data values at all
  510. if ( !InstallEngineStringTableCallback( tableName ) )
  511. {
  512. // If engine takes a pass, allow client dll to hook in its callbacks
  513. g_ClientDLL->InstallStringTableCallback( tableName );
  514. }
  515. }
  516. bool CClientState::IsPaused() const
  517. {
  518. return m_bPaused || ( g_LostVideoMemory && Host_IsSinglePlayerGame() ) ||
  519. !host_initialized ||
  520. demoplayer->IsPlaybackPaused() ||
  521. EngineVGui()->ShouldPause();
  522. }
  523. float CClientState::GetTime() const
  524. {
  525. int nTickCount = GetClientTickCount();
  526. float flTickTime = nTickCount * host_state.interval_per_tick;
  527. float flResult;
  528. // Timestamps are rounded to exact tick during simulation
  529. if ( insimulation )
  530. {
  531. return flTickTime;
  532. }
  533. #if defined(_X360) || defined( _PS3 )
  534. // This function is called enough under ComputeLightingState to make this little cache worthwhile [10/6/2010 tom]
  535. static float lastResult;
  536. static int lastTick;
  537. if ( lastTick == nTickCount )
  538. {
  539. return lastResult;
  540. }
  541. lastTick = nTickCount;
  542. #endif
  543. // Tracker 77931: If the game is paused, then lock the client clock at the previous tick boundary
  544. // (otherwise we'll keep interpolating through the "remainder" time causing the paused characters
  545. // to twitch like they have the shakes)
  546. // TODO: Since this rounds down on the frame we paused, we could see a slight backsliding. We could remember the last "remainder" before pause and re-use it and
  547. // set insimulation == false to be more exact. We'd still have to deal with the timing difference between
  548. // when pause/unpause happens on the server versus the client
  549. if ( GetBaseLocalClient().IsPaused() )
  550. {
  551. // Go just before next tick
  552. flResult = flTickTime + host_state.interval_per_tick - 0.00001f;
  553. }
  554. else
  555. {
  556. flResult = flTickTime + m_tickRemainder;
  557. }
  558. #if defined(_X360) || defined( _PS3 )
  559. lastResult = flResult;
  560. #endif
  561. return flResult;
  562. }
  563. float CClientState::GetFrameTime() const
  564. {
  565. if ( CClockDriftMgr::IsClockCorrectionEnabled() )
  566. {
  567. return IsPaused() ? 0 : m_frameTime;
  568. }
  569. else
  570. {
  571. if ( insimulation )
  572. {
  573. int nElapsedTicks = ( GetClientTickCount() - oldtickcount );
  574. return nElapsedTicks * host_state.interval_per_tick;
  575. }
  576. else
  577. {
  578. return IsPaused() ? 0 : m_frameTime;
  579. }
  580. }
  581. }
  582. float CClientState::GetClientInterpAmount()
  583. {
  584. // we need client cvar cl_interp_ratio
  585. static const ConVar *s_cl_interp_ratio = NULL;
  586. if ( !s_cl_interp_ratio )
  587. {
  588. s_cl_interp_ratio = g_pCVar->FindVar( "cl_interp_ratio" );
  589. if ( !s_cl_interp_ratio )
  590. return 0.1f;
  591. }
  592. static const ConVar *s_cl_interp = NULL;
  593. if ( !s_cl_interp )
  594. {
  595. s_cl_interp = g_pCVar->FindVar( "cl_interp" );
  596. if ( !s_cl_interp )
  597. return 0.1f;
  598. }
  599. float flInterpRatio = s_cl_interp_ratio->GetFloat();
  600. float flInterp = s_cl_interp->GetFloat();
  601. const ConVar_ServerBounded *pBounded = dynamic_cast<const ConVar_ServerBounded*>( s_cl_interp_ratio );
  602. if ( pBounded )
  603. flInterpRatio = pBounded->GetFloat();
  604. //#define FIXME_INTERP_RATIO
  605. return MAX( flInterpRatio / cl_updaterate->GetFloat(), flInterp );
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: // Clear all the variables in the CClientState.
  609. //-----------------------------------------------------------------------------
  610. void CClientState::Clear( void )
  611. {
  612. CBaseClientState::Clear();
  613. m_pModelPrecacheTable = NULL;
  614. m_pDynamicModelTable = NULL;
  615. m_pGenericPrecacheTable = NULL;
  616. m_pSoundPrecacheTable = NULL;
  617. m_pDecalPrecacheTable = NULL;
  618. m_pInstanceBaselineTable = NULL;
  619. m_pLightStyleTable = NULL;
  620. m_pUserInfoTable = NULL;
  621. m_pServerStartupTable = NULL;
  622. m_pAreaBits = NULL;
  623. // Clear all download vars.
  624. m_pDownloadableFileTable = NULL;
  625. m_hWaitForResourcesHandle = NULL;
  626. m_bUpdateSteamResources = false;
  627. m_bShownSteamResourceUpdateProgress = false;
  628. m_bDownloadResources = false;
  629. m_bDownloadingUGCMap = false;
  630. m_modelIndexLoaded = -1;
  631. m_lastModelPercent = -1;
  632. DeleteClientFrames( -1 ); // clear all
  633. viewangles.Init();
  634. m_flLastServerTickTime = 0.0f;
  635. oldtickcount = 0;
  636. insimulation = false;
  637. addangle.RemoveAll();
  638. addangletotal = 0.0f;
  639. prevaddangletotal = 0.0f;
  640. memset(model_precache, 0, sizeof(model_precache));
  641. memset(sound_precache, 0, sizeof(sound_precache));
  642. ishltv = false;
  643. #if defined( REPLAY_ENABLED )
  644. isreplay = false;
  645. #endif
  646. cdtrack = 0;
  647. serverCRC = 0;
  648. serverClientSideDllCRC = 0;
  649. last_command_ack = 0;
  650. last_server_tick = 0;
  651. command_ack = 0;
  652. m_nSoundSequence = 0;
  653. // make sure the client isn't active anymore, but stay
  654. // connected if we are.
  655. if ( m_nSignonState > SIGNONSTATE_CONNECTED )
  656. {
  657. m_nSignonState = SIGNONSTATE_CONNECTED;
  658. }
  659. }
  660. void CClientState::ClearSounds()
  661. {
  662. int c = ARRAYSIZE( sound_precache );
  663. for ( int i = 0; i < c; ++i )
  664. {
  665. sound_precache[ i ].SetSound( NULL );
  666. }
  667. }
  668. bool CClientState::ProcessConnectionlessPacket( netpacket_t *packet )
  669. {
  670. Assert( packet );
  671. return CBaseClientState::ProcessConnectionlessPacket( packet );
  672. }
  673. void CClientState::ConnectionStart( INetChannel *chan )
  674. {
  675. CBaseClientState::ConnectionStart( chan );
  676. m_SVCMsgHltvReplay.Bind< CSVCMsg_HltvReplay_t >( chan, UtlMakeDelegate( this, &CClientState::SVCMsg_HltvReplay ) );
  677. }
  678. void CClientState::ConnectionStop( )
  679. {
  680. CBaseClientState::ConnectionStop( );
  681. m_SVCMsgHltvReplay.Unbind();
  682. }
  683. float CClientState::GetHltvReplayTimeScale()const
  684. {
  685. extern ConVar spec_replay_rate_base;
  686. if ( m_nHltvReplayDelay )
  687. {
  688. int nCurrentTick = GetClientTickCount();
  689. if ( nCurrentTick >= m_nHltvReplaySlowdownBeginAt && nCurrentTick < m_nHltvReplaySlowdownEndAt )
  690. return spec_replay_rate_base.GetFloat() * m_flHltvReplaySlowdownRate;
  691. else
  692. return spec_replay_rate_base.GetFloat();
  693. }
  694. return 1.0f;
  695. }
  696. float CL_GetHltvReplayTimeScale()
  697. {
  698. return GetBaseLocalClient().GetHltvReplayTimeScale();
  699. }
  700. void CClientState::StopHltvReplay()
  701. {
  702. ForceFullUpdate( "Force StopHltvReplay on client" );
  703. m_nHltvReplayDelay = 0;
  704. m_nHltvReplayStopAt = 0;
  705. m_nHltvReplayStartAt = 0;
  706. if ( g_ClientDLL )
  707. {
  708. CSVCMsg_HltvReplay msg;
  709. g_ClientDLL->OnHltvReplay( msg );
  710. }
  711. }
  712. void CClientState::FullConnect( const ns_address &adr, int nEncryptionKey )
  713. {
  714. CBaseClientState::FullConnect( adr, nEncryptionKey );
  715. m_NetChannel->SetDemoRecorder( g_pClientDemoRecorder );
  716. m_NetChannel->SetDataRate( cl_rate->GetFloat() );
  717. // Not in the demo loop now
  718. demonum = -1;
  719. // We don't have a backed up cmd history yet
  720. lastoutgoingcommand = -1;
  721. // we didn't send commands yet
  722. chokedcommands = 0;
  723. // Report connection success.
  724. if ( !adr.IsLoopback() )
  725. {
  726. ConMsg( "Connected to %s\n", cl_hideserverip.GetInt()>0 ? "<ip hidden>" : ns_address_render( adr ).String() );
  727. }
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Purpose:
  731. // Input : index -
  732. // Output : model_t
  733. //-----------------------------------------------------------------------------
  734. model_t *CClientState::GetModel( int index )
  735. {
  736. if ( !m_pModelPrecacheTable )
  737. {
  738. return NULL;
  739. }
  740. if ( index <= 0 )
  741. {
  742. return NULL;
  743. }
  744. if ( index >= m_pModelPrecacheTable->GetNumStrings() )
  745. {
  746. Assert( 0 ); // model index for unkown model requested
  747. return NULL;
  748. }
  749. CPrecacheItem *p = &model_precache[ index ];
  750. model_t *m = p->GetModel();
  751. if ( m )
  752. {
  753. return m;
  754. }
  755. char const *name = m_pModelPrecacheTable->GetString( index );
  756. if ( host_showcachemiss.GetBool() )
  757. {
  758. ConDMsg( "client model cache miss on %s\n", name );
  759. }
  760. m = modelloader->GetModelForName( name, IModelLoader::FMODELLOADER_CLIENT );
  761. if ( !m )
  762. {
  763. const CPrecacheUserData *data = CL_GetPrecacheUserData( m_pModelPrecacheTable, index );
  764. if ( data && ( data->flags & RES_FATALIFMISSING ) )
  765. {
  766. COM_ExplainDisconnection( true, "Cannot continue without model %s, disconnecting\n", name );
  767. Host_Disconnect(true);
  768. }
  769. }
  770. p->SetModel( m );
  771. return m;
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose:
  775. // Input : *name -
  776. // Output : int -- note -1 if missing
  777. //-----------------------------------------------------------------------------
  778. int CClientState::LookupModelIndex( char const *name )
  779. {
  780. if ( !m_pModelPrecacheTable )
  781. {
  782. return -1;
  783. }
  784. int idx = m_pModelPrecacheTable->FindStringIndex( name );
  785. return ( idx == INVALID_STRING_INDEX ) ? -1 : idx;
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Purpose:
  789. // Input : index -
  790. // *name -
  791. //-----------------------------------------------------------------------------
  792. void CClientState::SetModel( int tableIndex )
  793. {
  794. if ( !m_pModelPrecacheTable )
  795. {
  796. return;
  797. }
  798. // Bogus index
  799. if ( tableIndex < 0 || tableIndex >= m_pModelPrecacheTable->GetNumStrings() )
  800. {
  801. return;
  802. }
  803. CPrecacheItem *p = &model_precache[ tableIndex ];
  804. const CPrecacheUserData *data = CL_GetPrecacheUserData( m_pModelPrecacheTable, tableIndex );
  805. bool bLoadNow = ( data && ( data->flags & RES_PRELOAD ) ) || IsGameConsole();
  806. if ( CommandLine()->FindParm( "-nopreload" ) || CommandLine()->FindParm( "-nopreloadmodels" ))
  807. {
  808. bLoadNow = false;
  809. }
  810. else if ( cl_forcepreload.GetInt() || CommandLine()->FindParm( "-preload" ) )
  811. {
  812. bLoadNow = true;
  813. }
  814. if ( bLoadNow )
  815. {
  816. char const *name = m_pModelPrecacheTable->GetString( tableIndex );
  817. int lenModelName = V_strlen( name );
  818. if ( demoplayer->IsPlayingBack() && ( lenModelName > 4 ) && !V_stricmp( name + lenModelName - 4, ".bsp" ) )
  819. name = m_szLevelName; // For demo playback we force the client bsp which may differ from precache table
  820. p->SetModel( modelloader->GetModelForName( name, IModelLoader::FMODELLOADER_CLIENT ) );
  821. }
  822. else
  823. {
  824. p->SetModel( NULL );
  825. }
  826. // log the file reference, if necessary
  827. if (MapReslistGenerator().IsEnabled())
  828. {
  829. char const *name = m_pModelPrecacheTable->GetString( tableIndex );
  830. int lenModelName = V_strlen( name );
  831. if ( demoplayer->IsPlayingBack() && ( lenModelName > 4 ) && !V_stricmp( name + lenModelName - 4, ".bsp" ) )
  832. name = m_szLevelName; // For demo playback we force the client bsp which may differ from precache table
  833. MapReslistGenerator().OnModelPrecached( name );
  834. }
  835. }
  836. //-----------------------------------------------------------------------------
  837. // Purpose:
  838. // Input : index -
  839. // Output : model_t
  840. //-----------------------------------------------------------------------------
  841. char const *CClientState::GetGeneric( int index )
  842. {
  843. if ( !m_pGenericPrecacheTable )
  844. {
  845. Warning( "Can't GetGeneric( %d ), no precache table [no level loaded?]\n", index );
  846. return "";
  847. }
  848. if ( index <= 0 )
  849. return "";
  850. if ( index >= m_pGenericPrecacheTable->GetNumStrings() )
  851. {
  852. return "";
  853. }
  854. CPrecacheItem *p = &generic_precache[ index ];
  855. char const *g = p->GetGeneric();
  856. return g;
  857. }
  858. //-----------------------------------------------------------------------------
  859. // Purpose:
  860. // Input : *name -
  861. // Output : int -- note -1 if missing
  862. //-----------------------------------------------------------------------------
  863. int CClientState::LookupGenericIndex( char const *name )
  864. {
  865. if ( !m_pGenericPrecacheTable )
  866. {
  867. Warning( "Can't LookupGenericIndex( %s ), no precache table [no level loaded?]\n", name );
  868. return -1;
  869. }
  870. int idx = m_pGenericPrecacheTable->FindStringIndex( name );
  871. return ( idx == INVALID_STRING_INDEX ) ? -1 : idx;
  872. }
  873. //-----------------------------------------------------------------------------
  874. // Purpose:
  875. // Input : index -
  876. // *name -
  877. //-----------------------------------------------------------------------------
  878. void CClientState::SetGeneric( int tableIndex )
  879. {
  880. if ( !m_pGenericPrecacheTable )
  881. {
  882. Warning( "Can't SetGeneric( %d ), no precache table [no level loaded?]\n", tableIndex );
  883. return;
  884. }
  885. // Bogus index
  886. if ( tableIndex < 0 ||
  887. tableIndex >= m_pGenericPrecacheTable->GetNumStrings() )
  888. {
  889. return;
  890. }
  891. char const *name = m_pGenericPrecacheTable->GetString( tableIndex );
  892. CPrecacheItem *p = &generic_precache[ tableIndex ];
  893. p->SetGeneric( name );
  894. }
  895. //-----------------------------------------------------------------------------
  896. // Purpose:
  897. // Input : index -
  898. // Output : char const
  899. //-----------------------------------------------------------------------------
  900. char const *CClientState::GetSoundName( int index )
  901. {
  902. if ( index <= 0 || !m_pSoundPrecacheTable )
  903. return "";
  904. if ( index >= m_pSoundPrecacheTable->GetNumStrings() )
  905. {
  906. return "";
  907. }
  908. char const *name = m_pSoundPrecacheTable->GetString( index );
  909. return name;
  910. }
  911. //-----------------------------------------------------------------------------
  912. // Purpose:
  913. // Input : index -
  914. // Output : model_t
  915. //-----------------------------------------------------------------------------
  916. CSfxTable *CClientState::GetSound( int index )
  917. {
  918. if ( index <= 0 || !m_pSoundPrecacheTable )
  919. return NULL;
  920. if ( index >= m_pSoundPrecacheTable->GetNumStrings() )
  921. {
  922. return NULL;
  923. }
  924. CPrecacheItem *p = &sound_precache[ index ];
  925. CSfxTable *s = p->GetSound();
  926. if ( s )
  927. return s;
  928. char const *name = m_pSoundPrecacheTable->GetString( index );
  929. if ( host_showcachemiss.GetBool() )
  930. {
  931. ConDMsg( "client sound cache miss on %s\n", name );
  932. }
  933. s = S_PrecacheSound( name );
  934. p->SetSound( s );
  935. return s;
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Purpose:
  939. // Input : *name -
  940. // Output : int -- note -1 if missing
  941. //-----------------------------------------------------------------------------
  942. int CClientState::LookupSoundIndex( char const *name )
  943. {
  944. if ( !m_pSoundPrecacheTable )
  945. return -1;
  946. int idx = m_pSoundPrecacheTable->FindStringIndex( name );
  947. return ( idx == INVALID_STRING_INDEX ) ? -1 : idx;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose:
  951. // Input : index -
  952. // *name -
  953. //-----------------------------------------------------------------------------
  954. void CClientState::SetSound( int tableIndex )
  955. {
  956. // Bogus index
  957. if ( !m_pSoundPrecacheTable )
  958. return;
  959. if ( tableIndex < 0 || tableIndex >= m_pSoundPrecacheTable->GetNumStrings() )
  960. {
  961. return;
  962. }
  963. CPrecacheItem *p = &sound_precache[ tableIndex ];
  964. const CPrecacheUserData *data = CL_GetPrecacheUserData( m_pSoundPrecacheTable, tableIndex );
  965. bool bLoadNow = ( data && ( data->flags & RES_PRELOAD ) ) || IsGameConsole();
  966. if ( CommandLine()->FindParm( "-nopreload" ) || CommandLine()->FindParm( "-nopreloadsounds" ))
  967. {
  968. bLoadNow = false;
  969. }
  970. else if ( cl_forcepreload.GetInt() || CommandLine()->FindParm( "-preload" ) )
  971. {
  972. bLoadNow = true;
  973. }
  974. if ( bLoadNow )
  975. {
  976. char const *name = m_pSoundPrecacheTable->GetString( tableIndex );
  977. CSfxTable *pSfxTable = S_PrecacheSound( name );
  978. if ( ( pSfxTable != NULL ) && pSfxTable->m_bIsLateLoad )
  979. {
  980. DevWarning( " CClientState::SetSound() created the late loading.\n" );
  981. }
  982. p->SetSound( pSfxTable );
  983. }
  984. else
  985. {
  986. p->SetSound( NULL );
  987. }
  988. // log the file reference, if necssary
  989. if (MapReslistGenerator().IsEnabled())
  990. {
  991. char const *name = m_pSoundPrecacheTable->GetString( tableIndex );
  992. MapReslistGenerator().OnSoundPrecached( name );
  993. }
  994. }
  995. //-----------------------------------------------------------------------------
  996. // Purpose:
  997. // Input : index -
  998. // Output : model_t
  999. //-----------------------------------------------------------------------------
  1000. char const *CClientState::GetDecalName( int index )
  1001. {
  1002. if ( index <= 0 || !m_pDecalPrecacheTable )
  1003. {
  1004. return NULL;
  1005. }
  1006. if ( index >= m_pDecalPrecacheTable->GetNumStrings() )
  1007. {
  1008. return NULL;
  1009. }
  1010. CPrecacheItem *p = &decal_precache[ index ];
  1011. char const *d = p->GetDecal();
  1012. return d;
  1013. }
  1014. //-----------------------------------------------------------------------------
  1015. // Purpose:
  1016. // Input : index -
  1017. // *name -
  1018. //-----------------------------------------------------------------------------
  1019. void CClientState::SetDecal( int tableIndex )
  1020. {
  1021. if ( !m_pDecalPrecacheTable )
  1022. return;
  1023. if ( tableIndex < 0 ||
  1024. tableIndex >= m_pDecalPrecacheTable->GetNumStrings() )
  1025. {
  1026. return;
  1027. }
  1028. char const *name = m_pDecalPrecacheTable->GetString( tableIndex );
  1029. CPrecacheItem *p = &decal_precache[ tableIndex ];
  1030. p->SetDecal( name );
  1031. Draw_DecalSetName( tableIndex, (char *)name );
  1032. }
  1033. //-----------------------------------------------------------------------------
  1034. // Purpose: sets friends info locally to be sent to other users
  1035. //-----------------------------------------------------------------------------
  1036. void CClientState::SetFriendsID( uint friendsID, const char *friendsName )
  1037. {
  1038. m_nFriendsID = friendsID;
  1039. Q_strncpy( m_FriendsName, friendsName, sizeof(m_FriendsName) );
  1040. }
  1041. void CClientState::CheckOthersCustomFile( CRC32_t crcValue )
  1042. {
  1043. if ( crcValue == 0 )
  1044. return; // not a valid custom file
  1045. extern ConVar cl_allowdownload;
  1046. if ( !cl_allowdownload.GetBool() )
  1047. return; // client doesn't want to download anything
  1048. CCustomFilename filehex( crcValue );
  1049. if ( g_pFileSystem->FileExists( filehex.m_Filename ) )
  1050. return; // we already have this file (assuming the CRC is correct)
  1051. // we don't have it, request download from server
  1052. m_NetChannel->RequestFile( filehex.m_Filename, false );
  1053. }
  1054. void CClientState::AddCustomFile( int slot, const char *resourceFile)
  1055. {
  1056. if ( Q_strlen(resourceFile) <= 0 )
  1057. return; // no resource file given
  1058. if ( !COM_IsValidPath( resourceFile ) )
  1059. {
  1060. Msg("Customization file '%s' has invalid path.\n", resourceFile );
  1061. return;
  1062. }
  1063. if ( slot < 0 || slot >= MAX_CUSTOM_FILES )
  1064. return; // wrong slot
  1065. if ( !g_pFileSystem->FileExists( resourceFile ) )
  1066. {
  1067. DevMsg("Couldn't find customization file '%s'.\n", resourceFile );
  1068. return; // resource file doesn't exits
  1069. }
  1070. if ( g_pFileSystem->Size( resourceFile ) > MAX_CUSTOM_FILE_SIZE )
  1071. {
  1072. Msg("Customization file '%s' is too big ( >%i bytes).\n", resourceFile, MAX_CUSTOM_FILE_SIZE );
  1073. return; // resource file doesn't exits
  1074. }
  1075. CRC32_t crcValue;
  1076. // Compute checksum of resource file
  1077. CRC_File( &crcValue, resourceFile );
  1078. // Copy it into materials/downloads if it's not there yet, so the server doesn't have to
  1079. // transmit the file back to us.
  1080. bool bCopy = true;
  1081. CCustomFilename filehex( crcValue );
  1082. if ( g_pFileSystem->FileExists( filehex.m_Filename ) )
  1083. {
  1084. // check if existing file already has same CRC,
  1085. // then we don't need to copy it anymore
  1086. CRC32_t test;
  1087. CRC_File( &test, filehex.m_Filename );
  1088. if ( test == crcValue )
  1089. bCopy = false;
  1090. }
  1091. if ( bCopy )
  1092. {
  1093. // Copy it over under the new name
  1094. COM_CopyFile( resourceFile, filehex.m_Filename );
  1095. if ( !g_pFileSystem->FileExists( filehex.m_Filename ) )
  1096. {
  1097. Warning( "CacheCustomFiles: can't copy '%s' to '%s'.\n", resourceFile, filehex.m_Filename );
  1098. return;
  1099. }
  1100. }
  1101. /* Finally, validate the VTF file. TODO
  1102. CUtlVector<char> fileData;
  1103. if ( LogoFile_ReadFile( crcValue, fileData ) )
  1104. {
  1105. bValid = true;
  1106. }
  1107. else
  1108. {
  1109. Warning( "CL_LogoFile_OnConnect: logo file '%s' invalid.\n", logotexture );
  1110. } */
  1111. m_nCustomFiles[slot].crc = crcValue; // first slot is logo
  1112. m_nCustomFiles[slot].reqID = 0;
  1113. }
  1114. void CClientState::CheckOwnCustomFiles()
  1115. {
  1116. // clear file CRCs
  1117. Q_memset( m_nCustomFiles, 0, sizeof(m_nCustomFiles) );
  1118. }
  1119. //-----------------------------------------------------------------------------
  1120. // Purpose:
  1121. //-----------------------------------------------------------------------------
  1122. void CClientState::DumpPrecacheStats( const char * name )
  1123. {
  1124. if ( !name || !name[0] )
  1125. {
  1126. ConMsg( "Can only dump stats when active in a level\n" );
  1127. return;
  1128. }
  1129. CPrecacheItem *items = NULL;
  1130. if ( !Q_strcmp(MODEL_PRECACHE_TABLENAME, name ) )
  1131. {
  1132. items = model_precache;
  1133. }
  1134. else if ( !Q_strcmp(GENERIC_PRECACHE_TABLENAME, name ) )
  1135. {
  1136. items = generic_precache;
  1137. }
  1138. else if ( !Q_strcmp(SOUND_PRECACHE_TABLENAME, name ) )
  1139. {
  1140. items = sound_precache;
  1141. }
  1142. else if ( !Q_strcmp(DECAL_PRECACHE_TABLENAME, name ) )
  1143. {
  1144. items = decal_precache;
  1145. }
  1146. INetworkStringTable *table = GetStringTable( name );
  1147. if ( !items || !table)
  1148. {
  1149. ConMsg( "Precache table '%s' not found.\n", name );
  1150. return;
  1151. }
  1152. int count = table->GetNumStrings();
  1153. int maxcount = table->GetMaxStrings();
  1154. ConMsg( "\n" );
  1155. ConMsg( "Precache table %s: %i of %i slots used\n", table->GetTableName(),
  1156. count, maxcount );
  1157. for ( int i = 0; i < count; i++ )
  1158. {
  1159. char const *name = table->GetString( i );
  1160. CPrecacheItem *slot = &items[ i ];
  1161. const CPrecacheUserData *p = CL_GetPrecacheUserData( table, i );
  1162. if ( !name || !slot || !p )
  1163. continue;
  1164. ConMsg( "%03i: %s (%s): ",
  1165. i,
  1166. name,
  1167. GetFlagString( p->flags ) );
  1168. if ( slot->GetReferenceCount() == 0 )
  1169. {
  1170. ConMsg( " never used\n" );
  1171. }
  1172. else
  1173. {
  1174. ConMsg( " %i refs, first %.2f mru %.2f\n",
  1175. slot->GetReferenceCount(),
  1176. slot->GetFirstReference(),
  1177. slot->GetMostRecentReference() );
  1178. }
  1179. }
  1180. ConMsg( "\n" );
  1181. }
  1182. void CClientState::ReadDeletions( CEntityReadInfo &u )
  1183. {
  1184. VPROF( "ReadDeletions" );
  1185. int nBase = -1;
  1186. int nCount = u.m_pBuf->ReadUBitVar();
  1187. for ( int i = 0; i < nCount; ++i )
  1188. {
  1189. int nDelta = u.m_pBuf->ReadUBitVar();
  1190. int nSlot = nBase + nDelta;
  1191. Assert( !u.m_pTo->transmit_entity.Get( nSlot ) );
  1192. CL_DeleteDLLEntity( nSlot, "ReadDeletions" );
  1193. nBase = nSlot;
  1194. }
  1195. }
  1196. inline static UpdateType DetermineUpdateType( CEntityReadInfo &u, int oldEntity )
  1197. {
  1198. if ( !u.m_bIsEntity || ( u.m_nNewEntity > oldEntity ) )
  1199. {
  1200. // If we're at the last entity, preserve whatever entities followed it in the old packet.
  1201. // If newnum > oldnum, then the server skipped sending entities that it wants to leave the state alone for.
  1202. if ( !u.m_pFrom || ( oldEntity > u.m_pFrom->last_entity ) )
  1203. {
  1204. return Finished;
  1205. }
  1206. // Preserve entities until we reach newnum (ie: the server didn't send certain entities because
  1207. // they haven't changed).
  1208. }
  1209. else
  1210. {
  1211. if( u.m_UpdateFlags & FHDR_ENTERPVS )
  1212. {
  1213. return EnterPVS;
  1214. }
  1215. else if( u.m_UpdateFlags & FHDR_LEAVEPVS )
  1216. {
  1217. return LeavePVS;
  1218. }
  1219. return DeltaEnt;
  1220. }
  1221. return PreserveEnt;
  1222. }
  1223. static inline void CL_ParseDeltaHeader( CEntityReadInfo &u )
  1224. {
  1225. u.m_UpdateFlags = FHDR_ZERO;
  1226. #ifdef DEBUG_NETWORKING
  1227. int startbit = u.m_pBuf->GetNumBitsRead();
  1228. #endif
  1229. u.m_nNewEntity = u.m_nHeaderBase + 1 + u.m_pBuf->ReadUBitVar();
  1230. u.m_nHeaderBase = u.m_nNewEntity;
  1231. // leave pvs flag
  1232. if ( u.m_pBuf->ReadOneBit() == 0 )
  1233. {
  1234. // enter pvs flag
  1235. if ( u.m_pBuf->ReadOneBit() != 0 )
  1236. {
  1237. u.m_UpdateFlags |= FHDR_ENTERPVS;
  1238. }
  1239. }
  1240. else
  1241. {
  1242. u.m_UpdateFlags |= FHDR_LEAVEPVS;
  1243. // Force delete flag
  1244. if ( u.m_pBuf->ReadOneBit() != 0 )
  1245. {
  1246. u.m_UpdateFlags |= FHDR_DELETE;
  1247. }
  1248. }
  1249. // Output the bitstream...
  1250. #ifdef DEBUG_NETWORKING
  1251. int lastbit = u.m_pBuf->GetNumBitsRead();
  1252. {
  1253. void SpewBitStream( unsigned char* pMem, int bit, int lastbit );
  1254. SpewBitStream( (byte *)u.m_pBuf->m_pData, startbit, lastbit );
  1255. }
  1256. #endif
  1257. }
  1258. void CClientState::ReadPacketEntities( CEntityReadInfo &u )
  1259. {
  1260. // Loop until there are no more entities to read
  1261. bool bRecord = cl_entityreport.GetBool();
  1262. int oldEntity = u.m_nOldEntity;
  1263. oldEntity = u.GetNextOldEntity(u.m_nOldEntity);
  1264. UpdateType updateType = u.m_UpdateType;
  1265. while ( updateType < Finished )
  1266. {
  1267. u.m_nHeaderCount--;
  1268. u.m_bIsEntity = ( u.m_nHeaderCount >= 0 ) ? true : false;
  1269. if ( u.m_bIsEntity )
  1270. {
  1271. CL_ParseDeltaHeader( u );
  1272. }
  1273. for ( updateType = PreserveEnt; updateType == PreserveEnt; )
  1274. {
  1275. // Figure out what kind of an update this is.
  1276. updateType = DetermineUpdateType(u, oldEntity);
  1277. switch( updateType )
  1278. {
  1279. case EnterPVS:
  1280. {
  1281. int iClass = u.m_pBuf->ReadUBitLong( m_nServerClassBits );
  1282. int iSerialNum = u.m_pBuf->ReadUBitLong( NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS );
  1283. u.m_nOldEntity = oldEntity;
  1284. CL_CopyNewEntity( u, iClass, iSerialNum );
  1285. if ( u.m_nNewEntity == oldEntity ) // that was a recreate
  1286. {
  1287. oldEntity = u.GetNextOldEntity(oldEntity);
  1288. }
  1289. }
  1290. break;
  1291. case LeavePVS:
  1292. {
  1293. if ( !u.m_bAsDelta )
  1294. {
  1295. Assert(0); // GetBaseLocalClient().validsequence = 0;
  1296. ConMsg( "WARNING: LeavePVS on full update" );
  1297. updateType = Failed; // break out
  1298. }
  1299. else
  1300. {
  1301. Assert( !u.m_pTo->transmit_entity.Get( oldEntity ) );
  1302. if ( u.m_UpdateFlags & FHDR_DELETE )
  1303. {
  1304. CL_DeleteDLLEntity( oldEntity, "ReadLeavePVS" );
  1305. }
  1306. oldEntity = u.GetNextOldEntity(oldEntity);
  1307. }
  1308. }
  1309. break;
  1310. case DeltaEnt:
  1311. {
  1312. u.m_nOldEntity = oldEntity;
  1313. CL_CopyExistingEntity( u );
  1314. oldEntity = u.GetNextOldEntity(oldEntity);
  1315. }
  1316. break;
  1317. case PreserveEnt:
  1318. {
  1319. if ( !u.m_bAsDelta ) // Should never happen on a full update.
  1320. {
  1321. updateType = Failed; // break out
  1322. }
  1323. else
  1324. {
  1325. Assert( u.m_pFrom->transmit_entity.Get(oldEntity) );
  1326. // copy one of the old entities over to the new packet unchanged
  1327. if ( u.m_nNewEntity < 0 || u.m_nNewEntity >= MAX_EDICTS )
  1328. {
  1329. Host_Error ("CL_ReadPreserveEnt: u.m_nNewEntity == MAX_EDICTS");
  1330. }
  1331. u.m_pTo->last_entity = oldEntity;
  1332. u.m_pTo->transmit_entity.Set( oldEntity );
  1333. if ( bRecord )
  1334. {
  1335. CL_RecordEntityBits( oldEntity, 0 );
  1336. }
  1337. oldEntity = u.GetNextOldEntity(oldEntity);
  1338. }
  1339. }
  1340. break;
  1341. default:
  1342. break;
  1343. }
  1344. }
  1345. }
  1346. u.m_nOldEntity = oldEntity;
  1347. u.m_UpdateType = updateType;
  1348. // Now process explicit deletes
  1349. if ( u.m_bAsDelta && u.m_UpdateType == Finished )
  1350. {
  1351. ReadDeletions( u );
  1352. }
  1353. // Something didn't parse...
  1354. if ( u.m_pBuf->IsOverflowed() )
  1355. {
  1356. Host_Error ( "CL_ParsePacketEntities: buffer read overflow\n" );
  1357. }
  1358. // If we get an uncompressed packet, then the server is waiting for us to ack the validsequence
  1359. // that we got the uncompressed packet on. So we stop reading packets here and force ourselves to
  1360. // send the clc_move on the next frame.
  1361. if ( !u.m_bAsDelta )
  1362. {
  1363. m_flNextCmdTime = 0.0; // answer ASAP to confirm full update tick
  1364. }
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose: Starts checking that all the necessary files are local
  1368. //-----------------------------------------------------------------------------
  1369. void CClientState::StartUpdatingSteamResources()
  1370. {
  1371. if ( IsX360() )
  1372. {
  1373. return;
  1374. }
  1375. // we can only do this when in SIGNONSTATE_NEW,
  1376. // since the completion of this triggers the continuation of SIGNONSTATE_NEW
  1377. Assert(m_nSignonState == SIGNONSTATE_NEW);
  1378. // make sure we have all the necessary resources locally before continuing
  1379. m_hWaitForResourcesHandle = g_pFileSystem->WaitForResources(m_szLevelNameShort);
  1380. m_bUpdateSteamResources = true;
  1381. m_bShownSteamResourceUpdateProgress = false;
  1382. m_bDownloadResources = false;
  1383. m_bDownloadingUGCMap = false;
  1384. }
  1385. // INFESTED_DLL
  1386. bool g_bASW_Waiting_For_Map_Build = false;
  1387. CON_COMMAND( asw_engine_finished_building_map, "Notify engine that we've finished building a map" )
  1388. {
  1389. g_bASW_Waiting_For_Map_Build = false;
  1390. }
  1391. //-----------------------------------------------------------------------------
  1392. // Purpose: checks to see if we're done updating files
  1393. //-----------------------------------------------------------------------------
  1394. void CClientState::CheckUpdatingSteamResources()
  1395. {
  1396. if ( IsX360() )
  1397. {
  1398. return;
  1399. }
  1400. VPROF_BUDGET( "CheckUpdatingSteamResources", VPROF_BUDGETGROUP_STEAM );
  1401. if (m_bUpdateSteamResources)
  1402. {
  1403. bool bComplete = false;
  1404. float flProgress = 0.0f;
  1405. g_pFileSystem->GetWaitForResourcesProgress(m_hWaitForResourcesHandle, &flProgress, &bComplete);
  1406. if (bComplete)
  1407. {
  1408. m_hWaitForResourcesHandle = NULL;
  1409. m_bUpdateSteamResources = false;
  1410. m_bDownloadResources = false;
  1411. m_bDownloadingUGCMap = false;
  1412. if ( m_pDownloadableFileTable )
  1413. {
  1414. bool allowDownloads = true;
  1415. bool allowSoundDownloads = true;
  1416. if ( !Q_strcasecmp( cl_downloadfilter.GetString(), "none" ) )
  1417. {
  1418. allowDownloads = allowSoundDownloads = false;
  1419. }
  1420. else if ( !Q_strcasecmp( cl_downloadfilter.GetString(), "nosounds" ) )
  1421. {
  1422. allowSoundDownloads = false;
  1423. }
  1424. if ( allowDownloads )
  1425. {
  1426. char extension[4];
  1427. for ( int i=0; i<m_pDownloadableFileTable->GetNumStrings(); ++i )
  1428. {
  1429. const char *fname = m_pDownloadableFileTable->GetString( i );
  1430. if ( !allowSoundDownloads )
  1431. {
  1432. Q_ExtractFileExtension( fname, extension, sizeof( extension ) );
  1433. if ( !Q_strcasecmp( extension, "wav" ) || !Q_strcasecmp( extension, "mp3" ) )
  1434. {
  1435. continue;
  1436. }
  1437. }
  1438. // If this is a community map we're loading download from the workshop cdn instead of server.
  1439. char bufFileName[MAX_PATH];
  1440. V_FixupPathName( bufFileName, sizeof( bufFileName ), fname );
  1441. EngineVGui()->UpdateProgressBar(PROGRESS_PROCESSSERVERINFO);
  1442. if ( m_unUGCMapFileID != 0 )
  1443. {
  1444. int lenBufFileName = V_strlen( bufFileName );
  1445. if ( !V_stricmp( bufFileName, m_szLevelName ) || // if UGC map file ID is set and the resource is the bsp then download the client level
  1446. ( ( lenBufFileName > 4 ) && !V_stricmp( bufFileName + lenBufFileName - 4, ".bsp" ) ) )
  1447. {
  1448. g_ClientDLL->DownloadCommunityMapFile( m_unUGCMapFileID );
  1449. m_bDownloadingUGCMap = true;
  1450. if ( cl_debug_ugc_downloads.GetBool() )
  1451. Msg( "CheckUpdatingSteamResources: downloading UGC map file '%s' id %llu\n", m_szLevelName, m_unUGCMapFileID );
  1452. continue;
  1453. }
  1454. if ( ( lenBufFileName > 4 ) && !V_stricmp( bufFileName + lenBufFileName - 4, ".nav" ) )
  1455. continue; // UGC maps always have nav embedded in the bsp
  1456. }
  1457. if ( demoplayer->IsPlayingBack() && ( cl_download_demoplayer.GetInt() < 2 ) )
  1458. continue; // demo playback doesn't need to download all the resources
  1459. if ( cl_debug_ugc_downloads.GetBool() )
  1460. Msg( "CheckUpdatingSteamResources: downloading file '%s'\n", bufFileName );
  1461. // INFESTED_DLL
  1462. static char gamedir[MAX_OSPATH];
  1463. Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
  1464. if ( !Q_stricmp( gamedir, "infested" ) )
  1465. {
  1466. // if we're trying to download a randomly generated map, instead request the map layout file
  1467. const char *pExt = V_GetFileExtension( fname );
  1468. if ( !Q_stricmp( pExt, "bsp" ) )
  1469. {
  1470. char mapLayoutFName[256];
  1471. Q_snprintf( mapLayoutFName, sizeof( mapLayoutFName ), "%s", fname );
  1472. int extPos = pExt - fname;
  1473. Q_snprintf( mapLayoutFName + extPos, sizeof( mapLayoutFName ) - extPos, "layout" );
  1474. if ( StringHasPrefix( fname + 5, "gridrandom" ) || StringHasPrefix( fname + 5, "output" ) )
  1475. {
  1476. CL_QueueDownload( mapLayoutFName );
  1477. }
  1478. else
  1479. {
  1480. // if we're downloading a non-random map, make sure we're not waiting for a map build
  1481. g_bASW_Waiting_For_Map_Build = false;
  1482. CL_QueueDownload( fname );
  1483. }
  1484. }
  1485. else
  1486. {
  1487. CL_QueueDownload( fname );
  1488. }
  1489. }
  1490. else
  1491. {
  1492. CL_QueueDownload( fname );
  1493. }
  1494. }
  1495. }
  1496. if ( CL_GetDownloadQueueSize() || g_bASW_Waiting_For_Map_Build || m_bDownloadingUGCMap )
  1497. {
  1498. // make sure the loading dialog is up
  1499. EngineVGui()->StartCustomProgress();
  1500. EngineVGui()->ActivateGameUI();
  1501. m_bDownloadResources = true;
  1502. }
  1503. else
  1504. {
  1505. m_bDownloadResources = false;
  1506. FinishSignonState_New();
  1507. }
  1508. }
  1509. else
  1510. {
  1511. Host_Error( "Invalid download file table." );
  1512. }
  1513. }
  1514. else if (flProgress > 0.0f)
  1515. {
  1516. if (!m_bShownSteamResourceUpdateProgress)
  1517. {
  1518. // make sure the loading dialog is up
  1519. EngineVGui()->StartCustomProgress();
  1520. EngineVGui()->ActivateGameUI();
  1521. m_bShownSteamResourceUpdateProgress = true;
  1522. }
  1523. // change it to be updating steam resources
  1524. EngineVGui()->UpdateSecondaryProgressBar( flProgress, (flProgress < 1.0f) ? g_pVGuiLocalize->FindSafe("#Valve_UpdatingSteamResources") : L"" );
  1525. }
  1526. }
  1527. if ( m_bDownloadResources || m_bDownloadingUGCMap )
  1528. {
  1529. // Check on any HTTP downloads in progress
  1530. bool stillDownloading = CL_DownloadUpdate();
  1531. if ( m_bDownloadingUGCMap )
  1532. {
  1533. float progress = g_ClientDLL->GetUGCFileDownloadProgress( m_unUGCMapFileID );
  1534. if ( progress == 1.0f || progress < 0.0f )
  1535. m_bDownloadingUGCMap = false; // stop waiting if we're done, or on error.
  1536. if ( cl_debug_ugc_downloads.GetBool() )
  1537. Msg( "CheckUpdatingSteamResources: Downloading UGC Map.... %f%%\n", 100.0f*progress );
  1538. wchar_t wszPercent[ 10 ];
  1539. V_snwprintf( wszPercent, ARRAYSIZE( wszPercent ), L"%d%%", (int)(100*progress) );
  1540. wchar_t wszWideBuff[ 128 ];
  1541. g_pVGuiLocalize->ConstructString( wszWideBuff, sizeof( wszWideBuff ), g_pVGuiLocalize->Find( "#SFUI_Loading_UGCMap_Progress" ), 1, wszPercent );
  1542. // change it to be updating steam resources
  1543. EngineVGui()->UpdateSecondaryProgressBar( progress, ( (progress > 0.0f) && (progress < 1.0f) ) ? wszWideBuff : L"" );
  1544. }
  1545. if ( !stillDownloading && !g_bASW_Waiting_For_Map_Build && !m_bDownloadingUGCMap )
  1546. {
  1547. m_bDownloadResources = false;
  1548. FinishSignonState_New();
  1549. // Setting to blank will clear it
  1550. EngineVGui()->UpdateSecondaryProgressBar( 1, L"" );
  1551. }
  1552. }
  1553. }
  1554. //-----------------------------------------------------------------------------
  1555. // Purpose: At a certain rate, this function will verify any unverified
  1556. // file CRCs with the server.
  1557. //-----------------------------------------------------------------------------
  1558. void CClientState::CheckFileCRCsWithServer()
  1559. {
  1560. VPROF_( "CheckFileCRCsWithServer", 1, VPROF_BUDGETGROUP_OTHER_NETWORKING, false, BUDGETFLAG_CLIENT );
  1561. const float flBatchInterval = 1.0f / 5.0f;
  1562. const int nBatchSize = 5;
  1563. // Don't do this yet..
  1564. if ( !m_bCheckCRCsWithServer )
  1565. return;
  1566. if ( m_nSignonState != SIGNONSTATE_FULL )
  1567. return;
  1568. // Only send a batch every so often.
  1569. float flCurTime = Plat_FloatTime();
  1570. if ( (flCurTime - m_flLastCRCBatchTime) < flBatchInterval )
  1571. return;
  1572. m_flLastCRCBatchTime = flCurTime;
  1573. CUnverifiedFileHash rgUnverifiedFiles[nBatchSize];
  1574. int count = g_pFileSystem->GetUnverifiedFileHashes( rgUnverifiedFiles, ARRAYSIZE( rgUnverifiedFiles ) );
  1575. if ( count == 0 )
  1576. return;
  1577. // Send the messages to the server.
  1578. for ( int i=0; i < count; i++ )
  1579. {
  1580. CCLCMsg_FileCRCCheck_t crcCheck;
  1581. CCLCMsg_FileCRCCheck_t::SetPath( crcCheck, rgUnverifiedFiles[i].m_PathID );
  1582. CCLCMsg_FileCRCCheck_t::SetFileName( crcCheck, rgUnverifiedFiles[i].m_Filename );
  1583. crcCheck.set_file_fraction( rgUnverifiedFiles[i].m_nFileFraction );
  1584. crcCheck.set_md5( (void*)(rgUnverifiedFiles[i].m_FileHash.m_md5contents.bits), MD5_DIGEST_LENGTH );
  1585. crcCheck.set_crc ( CRC32_ConvertToUnsignedLong( &rgUnverifiedFiles[i].m_FileHash.m_crcIOSequence ) );
  1586. crcCheck.set_file_hash_type ( rgUnverifiedFiles[i].m_FileHash.m_eFileHashType );
  1587. crcCheck.set_file_len ( rgUnverifiedFiles[i].m_FileHash.m_cbFileLen );
  1588. crcCheck.set_pack_file_number( rgUnverifiedFiles[i].m_FileHash.m_nPackFileNumber );
  1589. crcCheck.set_pack_file_id( rgUnverifiedFiles[i].m_FileHash.m_PackFileID );
  1590. m_NetChannel->SendNetMsg( crcCheck );
  1591. }
  1592. }
  1593. //-----------------------------------------------------------------------------
  1594. // Purpose: sanity-checks the variables in a VMT file to prevent the client from
  1595. // making player etc. textures that glow or show through walls etc. Anything
  1596. // other than $baseTexture and $bumpmap is hereby verboten.
  1597. //-----------------------------------------------------------------------------
  1598. bool CheckSimpleMaterial( IMaterial *pMaterial )
  1599. {
  1600. if ( !pMaterial )
  1601. return false;
  1602. const char *name = pMaterial->GetShaderName();
  1603. if ( Q_strncasecmp( name, "VertexLitGeneric", 16 ) &&
  1604. Q_strncasecmp( name, "UnlitGeneric", 12 ) &&
  1605. Q_strncasecmp( name, "Infected", 8 ) )
  1606. {
  1607. return false;
  1608. }
  1609. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_IGNOREZ ) )
  1610. return false;
  1611. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_WIREFRAME ) )
  1612. return false;
  1613. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SELFILLUM ) )
  1614. return false;
  1615. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_ADDITIVE ) )
  1616. return false;
  1617. if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_NOFOG ) )
  1618. return false;
  1619. return true;
  1620. }
  1621. //-----------------------------------------------------------------------------
  1622. // Purpose: find a filename in the string table, ignoring case and slash mismatches. Returns the index, or INVALID_STRING_INDEX if not found.
  1623. //-----------------------------------------------------------------------------
  1624. int FindFilenameInStringTable( INetworkStringTable *table, const char *searchFname )
  1625. {
  1626. char searchFilename[MAX_PATH];
  1627. char tableFilename[MAX_PATH];
  1628. Q_strncpy( searchFilename, searchFname, MAX_PATH );
  1629. Q_FixSlashes( searchFilename );
  1630. for ( int i=0; i<table->GetNumStrings(); ++i )
  1631. {
  1632. const char *tableFname = table->GetString( i );
  1633. Q_strncpy( tableFilename, tableFname, MAX_PATH );
  1634. Q_FixSlashes( tableFilename );
  1635. if ( !Q_strcasecmp( searchFilename, tableFilename ) )
  1636. {
  1637. return i;
  1638. }
  1639. }
  1640. return INVALID_STRING_INDEX;
  1641. }
  1642. //-----------------------------------------------------------------------------
  1643. // Purpose: find a filename in the string table, ignoring case and slash mismatches.
  1644. // Returns the consistency type, with CONSISTENCY_NONE being a Not Found result.
  1645. //-----------------------------------------------------------------------------
  1646. ConsistencyType GetFileConsistencyType( INetworkStringTable *table, const char *searchFname )
  1647. {
  1648. int index = FindFilenameInStringTable( table, searchFname );
  1649. if ( index == INVALID_STRING_INDEX )
  1650. {
  1651. return CONSISTENCY_NONE;
  1652. }
  1653. int length = 0;
  1654. unsigned char *userData = NULL;
  1655. userData = (unsigned char *)table->GetStringUserData( index, &length );
  1656. if ( userData && length == sizeof( ExactFileUserData ) )
  1657. {
  1658. switch ( userData[0] )
  1659. {
  1660. case CONSISTENCY_EXACT:
  1661. case CONSISTENCY_SIMPLE_MATERIAL:
  1662. return (ConsistencyType)userData[0];
  1663. default:
  1664. return CONSISTENCY_NONE;
  1665. }
  1666. }
  1667. else
  1668. {
  1669. return CONSISTENCY_NONE;
  1670. }
  1671. }
  1672. //-----------------------------------------------------------------------------
  1673. // Purpose: Does a CRC check compared to the CRC stored in the user data.
  1674. //-----------------------------------------------------------------------------
  1675. bool CheckCRCs( unsigned char *userData, int length, const char *filename )
  1676. {
  1677. if ( userData && length == sizeof( ExactFileUserData ) )
  1678. {
  1679. if ( userData[0] != CONSISTENCY_EXACT && userData[0] != CONSISTENCY_SIMPLE_MATERIAL )
  1680. {
  1681. return false;
  1682. }
  1683. ExactFileUserData *exactFileData = (ExactFileUserData *)userData;
  1684. CRC32_t crc;
  1685. if ( !CRC_File( &crc, filename ) )
  1686. {
  1687. return false;
  1688. }
  1689. return ( crc == exactFileData->crc );
  1690. }
  1691. return false;
  1692. }
  1693. //-----------------------------------------------------------------------------
  1694. // Purpose: completes the SIGNONSTATE_NEW state
  1695. //-----------------------------------------------------------------------------
  1696. void CClientState::FinishSignonState_New()
  1697. {
  1698. // make sure we're still in the right signon state
  1699. if (m_nSignonState != SIGNONSTATE_NEW)
  1700. return;
  1701. if ( demoplayer->IsPlayingBack() )
  1702. {
  1703. demoplayer->SetPacketReadSuspended( false );
  1704. }
  1705. VPROF_BUDGET( "FinishSignonState_New", VPROF_BUDGETGROUP_STEAM );
  1706. if ( !m_bMarkedCRCsUnverified )
  1707. {
  1708. // Mark all file CRCs unverified once per server. We may have verified CRCs for certain files on
  1709. // the previous server, but we need to reverify them on the new server.
  1710. m_bMarkedCRCsUnverified = true;
  1711. g_pFileSystem->MarkAllCRCsUnverified();
  1712. }
  1713. // Check for a new whitelist. It's good to do it early in the connection process here because if we wait until later,
  1714. // the client may have loaded some files w/o the proper whitelist restrictions and we'd have to reload them.
  1715. m_bCheckCRCsWithServer = false; // Don't check CRCs yet.. wait until we got a whitelist and cleaned out our files based on it to send CRCs.
  1716. CL_CheckForPureServerWhitelist();
  1717. // Verify the map and player .mdl crc's now that we've finished downloading missing resources (maps etc)
  1718. if ( !CL_CheckCRCs( m_szLevelName ) )
  1719. {
  1720. Host_Error( "Unabled to verify map %s\n", ( m_szLevelName && m_szLevelName[0] ) ? m_szLevelName : "unknown" );
  1721. return;
  1722. }
  1723. // Don't load the client if we don't own the game
  1724. if ( NET_IsMultiplayer() && Steam3Client().SteamApps() && !Steam3Client().SteamApps()->BIsSubscribed() )
  1725. {
  1726. Host_Error( "Steam ownership check failed.\n" );
  1727. return;
  1728. }
  1729. COM_TimestampedLog( "CL_InstallAndInvokeClientStringTableCallbacks" );
  1730. CL_InstallAndInvokeClientStringTableCallbacks();
  1731. #if 0
  1732. // HACK!!!! For use only on PC not yet using a whitelist!
  1733. // install hooks
  1734. if ( IsPC() && ( m_nMaxClients > 1 ) )
  1735. {
  1736. m_pModelPrecacheTable->SetStringChangedCallback( NULL, Callback_ModelChanged );
  1737. int nTableCount = m_StringTableContainer->GetNumTables();
  1738. for ( int iTable =0; iTable < nTableCount; ++iTable )
  1739. {
  1740. // iterate through server tables
  1741. CNetworkStringTable *pTable = (CNetworkStringTable*)m_StringTableContainer->GetTable( iTable );
  1742. if ( !pTable )
  1743. continue;
  1744. pfnStringChanged pCallbackFunction = pTable->GetCallback();
  1745. if ( pCallbackFunction )
  1746. for ( int iString = 0; iString < pTable->GetNumStrings(); ++iString )
  1747. {
  1748. int userDataSize;
  1749. const void *pUserData = pTable->GetStringUserData( iString, &userDataSize );
  1750. (*pCallbackFunction)( NULL, pTable, iString, pTable->GetString( iString ), pUserData );
  1751. }
  1752. }
  1753. materials->CacheUsedMaterials();
  1754. }
  1755. #endif
  1756. COM_TimestampedLog( "materials->CacheUsedMaterials" );
  1757. materials->CacheUsedMaterials();
  1758. COM_TimestampedLog( "ConsistencyCheck" );
  1759. // force a consistency check
  1760. ConsistencyCheck( true );
  1761. COM_TimestampedLog( "CL_RegisterResources" );
  1762. CL_RegisterResources();
  1763. // Done with all resources, issue prespawn command.
  1764. // Include server count in case server disconnects and changes level during d/l
  1765. // Tell rendering system we have a new set of models.
  1766. R_LevelInit();
  1767. EngineVGui()->UpdateProgressBar(PROGRESS_SENDCLIENTINFO);
  1768. if ( !m_NetChannel )
  1769. return;
  1770. SendClientInfo();
  1771. CL_SetSteamCrashComment();
  1772. // tell server that we entered now that state
  1773. CNETMsg_SignonState_t msgSignonState( m_nSignonState, m_nServerCount );
  1774. m_NetChannel->SendNetMsg( msgSignonState );
  1775. }
  1776. //-----------------------------------------------------------------------------
  1777. // Purpose: run a file consistency check if enforced by server
  1778. //-----------------------------------------------------------------------------
  1779. void CClientState::ConsistencyCheck(bool bChanged )
  1780. {
  1781. VPROF_BUDGET( "CClientState::ConsistencyCheck", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  1782. // get the default config for the current card as a starting point.
  1783. // server must have sent us this table
  1784. if ( !m_pDownloadableFileTable )
  1785. return;
  1786. // no checks during single player or demo playback
  1787. if( (m_nMaxClients == 1) || demoplayer->IsPlayingBack() )
  1788. return;
  1789. // only if we are connected
  1790. if ( !IsConnected() )
  1791. return;
  1792. // only if enforce by server
  1793. if ( !sv_consistency.GetBool() )
  1794. return;
  1795. // check if material configuration changed
  1796. static MaterialSystem_Config_t s_LastConfig;
  1797. MaterialSystem_Config_t newConfig = materials->GetCurrentConfigForVideoCard();
  1798. if ( Q_memcmp( &s_LastConfig, &newConfig, sizeof(MaterialSystem_Config_t) ) )
  1799. {
  1800. // remember last config we tested
  1801. s_LastConfig = newConfig;
  1802. bChanged = true;
  1803. }
  1804. if ( !bChanged )
  1805. return;
  1806. char errorFilenameBuf[MAX_PATH] = "";
  1807. // check CRCs and model sizes
  1808. Color red( 200, 20, 20, 255 );
  1809. Color blue( 100, 100, 200, 255 );
  1810. for ( int i=0; i<m_pDownloadableFileTable->GetNumStrings(); ++i )
  1811. {
  1812. int length = 0;
  1813. unsigned char *userData = NULL;
  1814. userData = (unsigned char *)m_pDownloadableFileTable->GetStringUserData( i, &length );
  1815. const char *filename = m_pDownloadableFileTable->GetString( i );
  1816. // [FTrepte] Ignore the CRC check for Counter-Strike 15.
  1817. // $FIXME: Is this the right thing to do or should we fix endianness and content issues
  1818. // that may be causing this not to match between the PC server and Xbox client?
  1819. //
  1820. // CRC Check
  1821. //
  1822. if ( userData && userData[0] == CONSISTENCY_EXACT && length == sizeof( ExactFileUserData ) )
  1823. {
  1824. #if !defined( CSTRIKE15 )
  1825. if ( !CheckCRCs( userData, length, filename ) )
  1826. {
  1827. ConColorMsg( red, "Bad CRC for %s\n", filename );
  1828. V_strncpy( errorFilenameBuf, filename, sizeof( errorFilenameBuf ) );
  1829. }
  1830. #endif
  1831. }
  1832. //
  1833. // Bounds Check
  1834. //
  1835. // This is simply asking for the model's mins and maxs. Also, it checks each material referenced
  1836. // by the model, to make sure it doesn't ignore Z, isn't overbright, etc.
  1837. //
  1838. // TODO: Animations and facial expressions can still pull verts out past this.
  1839. //
  1840. else if ( userData && userData[0] == CONSISTENCY_BOUNDS && length == sizeof( ModelBoundsUserData ) )
  1841. {
  1842. ModelBoundsUserData *boundsData = (ModelBoundsUserData *)userData;
  1843. model_t *pModel = modelloader->GetModelForName( filename, IModelLoader::FMODELLOADER_CLIENT );
  1844. if ( !pModel )
  1845. {
  1846. ConColorMsg( red, "Can't find model for %s\n", filename );
  1847. V_strncpy( errorFilenameBuf, filename, sizeof( errorFilenameBuf ) );
  1848. }
  1849. else
  1850. {
  1851. // [FTrepte] It seems that the boundsData is endian-swapped when connecting to the PC server in CClientState::ConsistencyCheck.
  1852. if (IsX360())
  1853. {
  1854. CByteswap swap;
  1855. swap.ActivateByteSwapping( true );
  1856. float minx = boundsData->mins.x;
  1857. swap.SwapBufferToTargetEndian<float>(&boundsData->mins.x, &minx, 1);
  1858. float miny = boundsData->mins.y;
  1859. swap.SwapBufferToTargetEndian<float>(&boundsData->mins.y, &miny, 1);
  1860. float minz = boundsData->mins.z;
  1861. swap.SwapBufferToTargetEndian<float>(&boundsData->mins.z, &minz, 1);
  1862. float maxx = boundsData->maxs.x;
  1863. swap.SwapBufferToTargetEndian<float>(&boundsData->maxs.x, &maxx, 1);
  1864. float maxy = boundsData->maxs.y;
  1865. swap.SwapBufferToTargetEndian<float>(&boundsData->maxs.y, &maxy, 1);
  1866. float maxz = boundsData->maxs.z;
  1867. swap.SwapBufferToTargetEndian<float>(&boundsData->maxs.z, &maxz, 1);
  1868. }
  1869. if ( pModel->mins.x < boundsData->mins.x ||
  1870. pModel->mins.y < boundsData->mins.y ||
  1871. pModel->mins.z < boundsData->mins.z )
  1872. {
  1873. ConColorMsg( red, "Model %s exceeds mins (%.1f %.1f %.1f vs. %.1f %.1f %.1f)\n", filename,
  1874. pModel->mins.x, pModel->mins.y, pModel->mins.z,
  1875. boundsData->mins.x, boundsData->mins.y, boundsData->mins.z);
  1876. V_strncpy( errorFilenameBuf, filename, sizeof( errorFilenameBuf ) );
  1877. }
  1878. if ( pModel->maxs.x > boundsData->maxs.x ||
  1879. pModel->maxs.y > boundsData->maxs.y ||
  1880. pModel->maxs.z > boundsData->maxs.z )
  1881. {
  1882. ConColorMsg( red, "Model %s exceeds maxs (%.1f %.1f %.1f vs. %.1f %.1f %.1f)\n", filename,
  1883. pModel->maxs.x, pModel->maxs.y, pModel->maxs.z,
  1884. boundsData->maxs.x, boundsData->maxs.y, boundsData->maxs.z);
  1885. V_strncpy( errorFilenameBuf, filename, sizeof( errorFilenameBuf ) );
  1886. }
  1887. // Check each texture
  1888. IMaterial *materials[ 128 ];
  1889. int materialCount = Mod_GetModelMaterials( pModel, ARRAYSIZE( materials ), materials );
  1890. for ( int j = 0; j<materialCount; ++j )
  1891. {
  1892. IMaterial *pMaterial = materials[j];
  1893. if ( !CheckSimpleMaterial( pMaterial ) )
  1894. {
  1895. // Try reloading the material:
  1896. pMaterial->RecomputeStateSnapshots();
  1897. if ( !CheckSimpleMaterial( pMaterial ) )
  1898. {
  1899. ConColorMsg( red, "Model %s has a bad texture %s\n", filename, pMaterial->GetName() );
  1900. V_strncpy( errorFilenameBuf, filename, sizeof( errorFilenameBuf ) );
  1901. break;
  1902. }
  1903. }
  1904. }
  1905. }
  1906. }
  1907. }
  1908. if ( *errorFilenameBuf )
  1909. {
  1910. COM_ExplainDisconnection( true, "Server is enforcing consistency for this file:\n%s\n", errorFilenameBuf );
  1911. Host_Error( "Server is enforcing file consistency for %s\n", errorFilenameBuf );
  1912. }
  1913. }
  1914. void CClientState::UpdateAreaBits_BackwardsCompatible()
  1915. {
  1916. if ( m_pAreaBits )
  1917. {
  1918. memcpy( m_chAreaBits, m_pAreaBits, sizeof( m_chAreaBits ) );
  1919. // The whole point of adding this array was that the client could react to closed portals.
  1920. // If they're using the old interface to set area portal bits, then we use the old
  1921. // behavior of assuming all portals are open on the clent.
  1922. memset( m_chAreaPortalBits, 0xFF, sizeof( m_chAreaPortalBits ) );
  1923. m_bAreaBitsValid = true;
  1924. }
  1925. }
  1926. unsigned char** CClientState::GetAreaBits_BackwardCompatibility()
  1927. {
  1928. return &m_pAreaBits;
  1929. }
  1930. void CClientState::RunFrame()
  1931. {
  1932. CBaseClientState::RunFrame();
  1933. // Since cl_rate is a virtualized cvar, make sure to pickup changes in it.
  1934. if ( m_NetChannel )
  1935. m_NetChannel->SetDataRate( cl_rate->GetFloat() );
  1936. ConsistencyCheck( false );
  1937. // Check if paged pool is low ( < 8% free )
  1938. static bool s_bLowPagedPoolMemoryWarning = false;
  1939. PAGED_POOL_INFO_t ppi;
  1940. if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
  1941. ( ( ppi.numPagesFree * 12 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
  1942. {
  1943. con_nprint_t np;
  1944. np.time_to_live = 1.0;
  1945. np.index = 1;
  1946. np.fixed_width_font = false;
  1947. np.color[ 0 ] = 1.0;
  1948. np.color[ 1 ] = 0.2;
  1949. np.color[ 2 ] = 0.0;
  1950. Con_NXPrintf( &np, "WARNING: OS Paged Pool Memory Low" );
  1951. // Also print a warning to console
  1952. static float s_flLastWarningTime = 0.0f;
  1953. if ( !s_bLowPagedPoolMemoryWarning ||
  1954. ( Plat_FloatTime() - s_flLastWarningTime > 3.0f ) ) // print a warning no faster than once every 3 sec
  1955. {
  1956. s_bLowPagedPoolMemoryWarning = true;
  1957. s_flLastWarningTime = Plat_FloatTime();
  1958. Warning( "OS Paged Pool Memory Low!\n" );
  1959. Warning( " Currently using %d pages (%d Kb) of total %d pages (%d Kb total)\n",
  1960. ppi.numPagesUsed, ppi.numPagesUsed * Plat_GetMemPageSize(),
  1961. ( ppi.numPagesFree + ppi.numPagesUsed ), ( ppi.numPagesFree + ppi.numPagesUsed ) * Plat_GetMemPageSize() );
  1962. Warning( " Please see http://support.steampowered.com for more information.\n" );
  1963. }
  1964. }
  1965. else if ( s_bLowPagedPoolMemoryWarning )
  1966. {
  1967. s_bLowPagedPoolMemoryWarning = false;
  1968. Msg( "Info: OS Paged Pool Memory restored - currently %d pages free (%d Kb) of total %d pages (%d Kb total).\n",
  1969. ppi.numPagesFree, ppi.numPagesFree * Plat_GetMemPageSize(),
  1970. ( ppi.numPagesFree + ppi.numPagesUsed ), ( ppi.numPagesFree + ppi.numPagesUsed ) * Plat_GetMemPageSize() );
  1971. }
  1972. }