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.

3921 lines
109 KiB

  1. //===== Copyright 1996-2005, 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 <time.h>
  13. #include "checksum_engine.h"
  14. #include "con_nprint.h"
  15. #include "r_local.h"
  16. #include "gl_lightmap.h"
  17. #include "console.h"
  18. #include "traceinit.h"
  19. #include "cl_demo.h"
  20. #include "cdll_engine_int.h"
  21. #include "debugoverlay.h"
  22. #include "filesystem_engine.h"
  23. #include "icliententity.h"
  24. #include "dt_recv_eng.h"
  25. #include "vgui_baseui_interface.h"
  26. #include "testscriptmgr.h"
  27. #include <tier0/vprof.h>
  28. #include <proto_oob.h>
  29. #include "materialsystem/imaterialsystemhardwareconfig.h"
  30. #include "gl_matsysiface.h"
  31. #include "staticpropmgr.h"
  32. #include "ispatialpartitioninternal.h"
  33. #include "cbenchmark.h"
  34. #include "vox.h"
  35. #include "LocalNetworkBackdoor.h"
  36. #include <tier0/icommandline.h>
  37. #include "GameEventManager.h"
  38. #include "host_saverestore.h"
  39. #include "ivideomode.h"
  40. #include "host_phonehome.h"
  41. #include "decal.h"
  42. #include "sv_rcon.h"
  43. #include "cl_rcon.h"
  44. #include "vgui_baseui_interface.h"
  45. #include "snd_audio_source.h"
  46. #include "iregistry.h"
  47. #include "sys.h"
  48. #include <vstdlib/random.h>
  49. #include "tier0/etwprof.h"
  50. #include "SteamInterface.h"
  51. #include "sys_dll.h"
  52. #include "avi/iavi.h"
  53. #include "cl_steamauth.h"
  54. #include "filesystem/IQueuedLoader.h"
  55. #include "matchmaking/imatchframework.h"
  56. #include "tier2/tier2.h"
  57. #include "host_state.h"
  58. #include "enginethreads.h"
  59. #include "vgui/ISystem.h"
  60. #include "pure_server.h"
  61. #include "SoundEmitterSystem/isoundemittersystembase.h"
  62. #include "LoadScreenUpdate.h"
  63. #include "tier0/systeminformation.h"
  64. #include "steam/steam_api.h"
  65. #include "SourceAppInfo.h"
  66. #include "cl_texturelistpanel.h"
  67. #include "enginethreads.h"
  68. #include "tier1/characterset.h"
  69. #include "const.h"
  70. #include "audio/private/snd_sfx.h"
  71. #include "MapReslistGenerator.h"
  72. #ifdef _X360
  73. #include "xbox/xbox_launch.h"
  74. #endif
  75. #if defined( REPLAY_ENABLED )
  76. #include "replayhistorymanager.h"
  77. #endif
  78. #include "ConfigManager.h"
  79. // memdbgon must be the last include file in a .cpp file!!!
  80. #include "tier0/memdbgon.h"
  81. extern IVEngineClient *engineClient;
  82. void R_UnloadSkys( void );
  83. void CL_ResetEntityBits( void );
  84. void EngineTool_UpdateScreenshot();
  85. // If we get more than 250 messages in the incoming buffer queue, dump any above this #
  86. #define MAX_INCOMING_MESSAGES 250
  87. // Size of command send buffer
  88. #define MAX_CMD_BUFFER 4000
  89. CGlobalVarsBase g_ClientGlobalVariables( true );
  90. AVIHandle_t g_hCurrentAVI = AVIHANDLE_INVALID;
  91. extern ConVar rcon_password;
  92. extern ConVar host_framerate;
  93. extern ConVar cl_clanid;
  94. ConVar sv_unlockedchapters( "sv_unlockedchapters", "1", FCVAR_ARCHIVE, "Highest unlocked game chapter." );
  95. static ConVar tv_nochat ( "tv_nochat", "0", FCVAR_ARCHIVE | FCVAR_USERINFO, "Don't receive chat messages from other GOTV spectators" );
  96. // ZOID: Disabled cl_LocalNetworkBackdoor from the cell optimization code, Dussault is going to fix this later
  97. static ConVar cl_LocalNetworkBackdoor( "cl_localnetworkbackdoor", "1", 0, "Enable network optimizations for single player games." );
  98. static ConVar cl_ignorepackets( "cl_ignorepackets", "0", FCVAR_CHEAT, "Force client to ignore packets (for debugging)." );
  99. static ConVar cl_playback_screenshots( "cl_playback_screenshots", "0", 0, "Allows the client to playback screenshot and jpeg commands in demos." );
  100. // Exposing connectivity trouble as a convar so we can display info in the client
  101. ConVar cl_connection_trouble_info( "cl_connection_trouble_info", "", FCVAR_HIDDEN, "How long until we timeout on our network connection because of connectivity loss (empty if no problem)" );
  102. MovieInfo_t cl_movieinfo;
  103. // FIXME: put these on hunk?
  104. dlight_t cl_dlights[MAX_DLIGHTS];
  105. dlight_t cl_elights[MAX_ELIGHTS];
  106. CFastPointLeafNum g_DLightLeafAccessors[MAX_DLIGHTS];
  107. CFastPointLeafNum g_ELightLeafAccessors[MAX_ELIGHTS];
  108. int g_ActiveDLightIndex[MAX_DLIGHTS];
  109. int g_ActiveELightIndex[MAX_ELIGHTS];
  110. int g_nNumActiveDLights = 0;
  111. int g_nNumActiveELights = 0;
  112. extern bool g_bClearingClientState;
  113. bool cl_takesnapshot = false;
  114. bool cl_takejpeg = false;
  115. static int cl_jpegquality = DEFAULT_JPEG_QUALITY;
  116. static ConVar jpeg_quality( "jpeg_quality", "90", 0, "jpeg screenshot quality." );
  117. static int cl_snapshotnum = 0;
  118. static char cl_snapshotname[MAX_OSPATH];
  119. static char cl_snapshot_subdirname[MAX_OSPATH];
  120. char cl_snapshot_fullpathname[MAX_OSPATH];
  121. static ConVar cl_retire_low_priority_lights( "cl_retire_low_priority_lights", "0", 0, "Low priority dlights are replaced by high priority ones" );
  122. // Must match game .dll definition
  123. // HACK HACK FOR E3 -- Remove this after E3
  124. #define HIDEHUD_ALL ( 1<<2 )
  125. //
  126. // This is called when a client receives the whitelist from a pure server (on map change).
  127. // Each pure server (and each map on the server) has a whitelist that says which files a
  128. // client is allowed to load off disk. When the client gets the whitelist, it must
  129. // flush out any files that it has loaded previously that were NOT in the Steam cache.
  130. //
  131. // -- pseudocode --
  132. // for all loaded resources (models/sounds/materials/scripts)
  133. // for each file related to this resource
  134. // if (file is not in whitelist)
  135. // if (file was loaded off disk instead of coming from the Steam cache)
  136. // flush the file
  137. //
  138. // Note: It could also check in here that the on-disk file is actually different
  139. // than the Steam one. If it happens to have the same CRC, then there's no need
  140. // to do all the flushing.
  141. //
  142. void CL_HandlePureServerWhitelist( CPureServerWhitelist *pWhitelist )
  143. {
  144. // Free the old whitelist and get the new one.
  145. if ( GetBaseLocalClient().m_pPureServerWhitelist )
  146. GetBaseLocalClient().m_pPureServerWhitelist->Release();
  147. GetBaseLocalClient().m_pPureServerWhitelist = pWhitelist;
  148. IFileList *pForceMatchList = NULL;
  149. IFileList *pAllowFromDiskList = NULL;
  150. if ( pWhitelist )
  151. {
  152. pForceMatchList = pWhitelist->GetForceMatchList();
  153. pAllowFromDiskList = pWhitelist->GetAllowFromDiskList();
  154. }
  155. if ( !IsPC() )
  156. {
  157. if ( pForceMatchList )
  158. pForceMatchList->Release();
  159. if ( pAllowFromDiskList )
  160. pAllowFromDiskList->Release();
  161. return;
  162. }
  163. // we wont reload any files.
  164. IFileList *pFilesToReload;
  165. g_pFileSystem->RegisterFileWhitelist( pForceMatchList, pAllowFromDiskList, &pFilesToReload );
  166. GetBaseLocalClient().m_bCheckCRCsWithServer = true;
  167. }
  168. void CL_PrintWhitelistInfo()
  169. {
  170. if ( GetBaseLocalClient().m_pPureServerWhitelist )
  171. {
  172. if ( GetBaseLocalClient().m_pPureServerWhitelist->IsInFullyPureMode() )
  173. {
  174. Msg( "The server is using sv_pure = 2.\n" );
  175. }
  176. else
  177. {
  178. Msg( "The server is using sv_pure = 1.\n" );
  179. GetBaseLocalClient().m_pPureServerWhitelist->PrintWhitelistContents();
  180. }
  181. }
  182. else
  183. {
  184. Msg( "The server is using sv_pure = 0 (no whitelist).\n" );
  185. }
  186. }
  187. // Console command to force a whitelist on the system.
  188. #ifdef _DEBUG
  189. void whitelist_f( const CCommand &args )
  190. {
  191. int pureLevel = 2;
  192. if ( args.ArgC() == 2 )
  193. {
  194. pureLevel = atoi( args[1] );
  195. }
  196. else
  197. {
  198. Warning( "Whitelist 0, 1, or 2\n" );
  199. }
  200. if ( pureLevel == 0 )
  201. {
  202. Warning( "whitelist 0: CL_HandlePureServerWhitelist( NULL )\n" );
  203. CL_HandlePureServerWhitelist( NULL );
  204. }
  205. else
  206. {
  207. CPureServerWhitelist *pWhitelist = CPureServerWhitelist::Create( g_pFileSystem );
  208. if( pureLevel == 2 )
  209. {
  210. Warning( "whitelist 2: pWhitelist->EnableFullyPureMode()\n" );
  211. pWhitelist->EnableFullyPureMode();
  212. }
  213. else
  214. {
  215. Warning( "whitelist 1: loading pure_server_whitelist.txt\n" );
  216. KeyValues *kv = new KeyValues( "" );
  217. bool bLoaded = kv->LoadFromFile( g_pFileSystem, "pure_server_whitelist.txt", "game" );
  218. if ( bLoaded )
  219. bLoaded = pWhitelist->LoadFromKeyValues( kv );
  220. if ( !bLoaded )
  221. Warning( "Error loading pure_server_whitelist.txt\n" );
  222. kv->deleteThis();
  223. }
  224. CL_HandlePureServerWhitelist( pWhitelist );
  225. pWhitelist->Release();
  226. }
  227. }
  228. ConCommand whitelist( "whitelist", whitelist_f );
  229. #endif
  230. const CPrecacheUserData* CL_GetPrecacheUserData( INetworkStringTable *table, int index )
  231. {
  232. int testLength;
  233. const CPrecacheUserData *data = ( CPrecacheUserData * )table->GetStringUserData( index, &testLength );
  234. if ( data )
  235. {
  236. ErrorIfNot(
  237. testLength == sizeof( *data ),
  238. ("CL_GetPrecacheUserData(%d,%d) - length (%d) invalid.", table->GetTableId(), index, testLength)
  239. );
  240. }
  241. return data;
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose: setup the demo flag, split from CL_IsHL2Demo so CL_IsHL2Demo can be inline
  245. //-----------------------------------------------------------------------------
  246. static bool s_bIsHL2Demo = false;
  247. void CL_InitHL2DemoFlag()
  248. {
  249. #if defined(_GAMECONSOLE)
  250. s_bIsHL2Demo = false;
  251. #else
  252. static bool initialized = false;
  253. if ( !initialized )
  254. {
  255. #ifndef NO_STEAM
  256. if ( Steam3Client().SteamApps() && !Q_stricmp( COM_GetModDirectory(), "hl2" ) && g_pFileSystem->IsSteam() )
  257. {
  258. initialized = true;
  259. // if user didn't buy HL2 yet, this must be the free demo
  260. s_bIsHL2Demo = !Steam3Client().SteamApps()->BIsSubscribedApp( GetAppSteamAppId( k_App_HL2 ) );
  261. }
  262. #endif
  263. if ( !Q_stricmp( COM_GetModDirectory(), "hl2" ) && CommandLine()->CheckParm( "-demo" ) )
  264. {
  265. s_bIsHL2Demo = true;
  266. }
  267. }
  268. #endif
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Purpose: Returns true if the user is playing the HL2 Demo (rather than the full game)
  272. //-----------------------------------------------------------------------------
  273. bool CL_IsHL2Demo()
  274. {
  275. CL_InitHL2DemoFlag();
  276. return s_bIsHL2Demo;
  277. }
  278. static bool s_bIsPortalDemo = false;
  279. void CL_InitPortalDemoFlag()
  280. {
  281. #if defined(_GAMECONSOLE) || defined( NO_STEAM )
  282. s_bIsPortalDemo = false;
  283. #else
  284. static bool initialized = false;
  285. if ( !initialized )
  286. {
  287. if ( Steam3Client().SteamApps() && !Q_stricmp( COM_GetModDirectory(), "portal" ) && g_pFileSystem->IsSteam() )
  288. {
  289. initialized = true;
  290. // if user didn't buy Portal yet, this must be the free demo
  291. s_bIsPortalDemo = !Steam3Client().SteamApps()->BIsSubscribedApp( GetAppSteamAppId( k_App_PORTAL ) );
  292. }
  293. if ( !Q_stricmp( COM_GetModDirectory(), "portal" ) && CommandLine()->CheckParm( "-demo" ) )
  294. {
  295. s_bIsPortalDemo = true;
  296. }
  297. }
  298. #endif
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Returns true if the user is playing the Portal Demo (rather than the full game)
  302. //-----------------------------------------------------------------------------
  303. bool CL_IsPortalDemo()
  304. {
  305. CL_InitPortalDemoFlag();
  306. return s_bIsPortalDemo;
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: If the client is in the process of connecting and the GetBaseLocalClient().signon hits
  310. // is complete, make sure the client thinks its totally connected.
  311. //-----------------------------------------------------------------------------
  312. void CL_CheckClientState( void )
  313. {
  314. // Setup the local network backdoor (we do this each frame so it can be toggled on and off).
  315. bool useBackdoor = cl_LocalNetworkBackdoor.GetInt() &&
  316. (GetBaseLocalClient().m_NetChannel ? GetBaseLocalClient().m_NetChannel->IsLoopback() : false) &&
  317. sv.IsActive() &&
  318. !demorecorder->IsRecording() &&
  319. !demoplayer->IsPlayingBack() &&
  320. Host_IsSinglePlayerGame();
  321. CL_SetupLocalNetworkBackDoor( useBackdoor );
  322. }
  323. //-----------------------------------------------------------------------------
  324. // bool CL_CheckCRCs( const char *pszMap )
  325. //-----------------------------------------------------------------------------
  326. bool CL_CheckCRCs( const char *pszMap )
  327. {
  328. if ( IsGameConsole() )
  329. {
  330. // Console does not need to CRC map/dlls (slows loading), closed data system
  331. return true;
  332. }
  333. // If we are on PC cross-playing with a console, then don't perform CRC check
  334. if ( !GetBaseLocalClient().serverCRC && !GetBaseLocalClient().serverClientSideDllCRC )
  335. return true;
  336. VPROF_BUDGET( "CL_CheckCRCs", VPROF_BUDGETGROUP_STEAM );
  337. CRC32_t mapCRC; // If this is the worldmap, CRC agains server's map
  338. // 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
  339. if ( sv.IsActive() ) // Single player
  340. return true;
  341. CRC32_Init(&mapCRC);
  342. if ( !CRC_MapFile( &mapCRC, pszMap ) )
  343. {
  344. // Does the file exist?
  345. FileHandle_t fp = 0;
  346. int nSize = -1;
  347. nSize = COM_OpenFile( pszMap, &fp );
  348. if ( fp )
  349. g_pFileSystem->Close( fp );
  350. if ( nSize != -1 )
  351. {
  352. COM_ExplainDisconnection( true, "Couldn't CRC map %s, disconnecting\n", pszMap);
  353. }
  354. else
  355. {
  356. COM_ExplainDisconnection( true, "Missing map %s, disconnecting\n", pszMap);
  357. }
  358. Host_Error( "Disconnected" );
  359. return false;
  360. }
  361. // Hacked map
  362. if ( GetBaseLocalClient().serverCRC != mapCRC )
  363. {
  364. if ( demoplayer && demoplayer->IsPlayingBack() )
  365. {
  366. Msg( "Client map CRC mismatch, %lu local != %lu demo.\n", mapCRC, GetBaseLocalClient().serverCRC );
  367. }
  368. else
  369. {
  370. COM_ExplainDisconnection( true, "Your map [%s] differs from the server's.\n", pszMap );
  371. Host_Error( "Disconnected" );
  372. return false;
  373. }
  374. }
  375. return true;
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose:
  379. // Input : nMaxClients -
  380. //-----------------------------------------------------------------------------
  381. void CL_ReallocateDynamicData( int maxclients )
  382. {
  383. Assert( entitylist );
  384. if ( entitylist )
  385. {
  386. entitylist->SetMaxEntities( MAX_EDICTS );
  387. }
  388. }
  389. /*
  390. =================
  391. CL_ReadPackets
  392. Updates the local time and reads/handles messages on client net connection.
  393. =================
  394. */
  395. ConVar net_earliertempents( "net_earliertempents", "0", FCVAR_CHEAT );
  396. void CL_ReadPackets ( bool bFinalTick )
  397. {
  398. VPROF_BUDGET( "CL_ReadPackets", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  399. // Before parsing any messages, assume we're in split player "slot 0"
  400. splitscreen->SetActiveSplitScreenPlayerSlot( 0 );
  401. splitscreen->SetLocalPlayerIsResolvable( __FILE__, __LINE__, false );
  402. if ( !Host_ShouldRun() )
  403. return;
  404. splitscreen->SetLocalPlayerIsResolvable( __FILE__, __LINE__, true );
  405. CClientState &cl = GetBaseLocalClient();
  406. // If we're fully connected, but still showing loading plaque, tick it once per frame
  407. if ( cl.IsActive() && scr_drawloading )
  408. {
  409. EngineVGui()->UpdateProgressBar( PROGRESS_DEFAULT );
  410. }
  411. // update client times/tick
  412. cl.oldtickcount = cl.GetServerTickCount();
  413. if ( !cl.IsPaused() )
  414. {
  415. cl.SetClientTickCount( cl.GetClientTickCount() + 1 );
  416. // While clock correction is off, we have the old behavior of matching the client and server clocks.
  417. if ( !CClockDriftMgr::IsClockCorrectionEnabled() )
  418. cl.SetServerTickCount( cl.GetClientTickCount() );
  419. g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
  420. g_ClientGlobalVariables.curtime = cl.GetTime();
  421. }
  422. // 0 or tick_rate if simulating
  423. g_ClientGlobalVariables.frametime = cl.GetFrameTime();
  424. // read packets, if any in queue
  425. if ( demoplayer->IsPlayingBack() && cl.m_NetChannel )
  426. {
  427. // process data from demo file
  428. cl.m_NetChannel->ProcessPlayback();
  429. }
  430. else
  431. {
  432. if ( !cl_ignorepackets.GetInt() )
  433. {
  434. // process data from net socket
  435. NET_ProcessSocket( NS_CLIENT, &cl );
  436. if( net_earliertempents.GetBool() )
  437. {
  438. CL_FireEvents();
  439. }
  440. }
  441. }
  442. // check timeout, but not if running _DEBUG engine
  443. bool bAllowTimeout = true;
  444. #if defined( _DEBUG )
  445. bAllowTimeout = false;
  446. #endif
  447. // Only check on final frame because that's when the server might send us a packet in single player. This avoids
  448. // a bug where if you sit in the game code in the debugger then you get a timeout here on resuming the engine
  449. // 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
  450. if ( bFinalTick &&
  451. !demoplayer->IsPlayingBack() &&
  452. cl.IsConnected() )
  453. {
  454. bool bDisconnected = false;
  455. if ( cl.m_NetChannel )
  456. {
  457. if ( bAllowTimeout && cl.m_NetChannel->IsTimedOut() )
  458. {
  459. bDisconnected = true;
  460. ConMsg ("\nServer connection timed out.\n");
  461. // Show the vgui dialog on timeout
  462. COM_ExplainDisconnection( false, "Connection to server timed out.");
  463. }
  464. // Check for Steam mediated socket disconnection
  465. else if ( cl.m_NetChannel->IsRemoteDisconnected() )
  466. {
  467. bDisconnected = true;
  468. ConMsg ("\nServer shutting down\n");
  469. // Show the vgui dialog on timeout
  470. COM_ExplainDisconnection( false, "Server shutting down");
  471. }
  472. }
  473. if ( bDisconnected )
  474. {
  475. if ( IsPC() )
  476. {
  477. EngineVGui()->ShowErrorMessage();
  478. }
  479. Host_Disconnect (true);
  480. return;
  481. }
  482. }
  483. splitscreen->SetLocalPlayerIsResolvable( __FILE__, __LINE__, false );
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Purpose:
  487. //-----------------------------------------------------------------------------
  488. void CL_ClearState ( void )
  489. {
  490. // clear out the current whitelist
  491. CL_HandlePureServerWhitelist( NULL );
  492. CL_TextureListPanel_ClearState();
  493. CL_ResetEntityBits();
  494. R_UnloadSkys();
  495. // clear decal index directories
  496. Decal_Init();
  497. StaticPropMgr()->LevelShutdownClient();
  498. // shutdown this level in the client DLL
  499. if ( g_ClientDLL )
  500. {
  501. if ( host_state.worldmodel )
  502. {
  503. char mapname[256];
  504. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), mapname, sizeof( mapname ) );
  505. phonehome->Message( IPhoneHome::PHONE_MSG_MAPEND, mapname );
  506. }
  507. audiosourcecache->LevelShutdown();
  508. g_ClientDLL->LevelShutdown();
  509. }
  510. R_LevelShutdown();
  511. if ( g_pLocalNetworkBackdoor )
  512. g_pLocalNetworkBackdoor->ClearState();
  513. // clear other arrays
  514. memset (cl_dlights, 0, sizeof(cl_dlights));
  515. memset (cl_elights, 0, sizeof(cl_elights));
  516. g_bActiveDlights = false;
  517. g_bActiveElights = false;
  518. r_dlightchanged = 0;
  519. r_dlightactive = 0;
  520. int i;
  521. for ( i=0; i<MAX_DLIGHTS; ++i )
  522. {
  523. g_DLightLeafAccessors[i].Reset();
  524. }
  525. for ( i=0; i<MAX_ELIGHTS; ++i )
  526. {
  527. g_ELightLeafAccessors[i].Reset();
  528. }
  529. g_bClearingClientState = true;
  530. // Wipe the hunk ( unless the server is active )
  531. // Make sure world is set if we have the models stringtable
  532. // This fixes a bug where you would fail to connect to a server due to, e.g., sv_consistency failure,
  533. // The client would Host_Error. The CM_FreeMap would have been called, but the worldmodel itself would not have
  534. // been unreferenced, which would make it possible to not load the collision data for the next connection, causing a crash.
  535. model_t *pWorldModel = GetBaseLocalClient().GetModel( 1 );
  536. if ( pWorldModel && !host_state.worldmodel )
  537. {
  538. host_state.SetWorldModel( pWorldModel );
  539. }
  540. g_bClearingClientState = false;
  541. Host_FreeStateAndWorld( false );
  542. Host_FreeToLowMark( false );
  543. // Wipe the remainder of the structure.
  544. GetBaseLocalClient().Clear();
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Purpose: Used for sorting sounds
  548. // Input : &sound1 -
  549. // &sound2 -
  550. // Output : static bool
  551. //-----------------------------------------------------------------------------
  552. static bool CL_SoundMessageLessFunc( SoundInfo_t const &sound1, SoundInfo_t const &sound2 )
  553. {
  554. return sound1.nSequenceNumber < sound2.nSequenceNumber;
  555. }
  556. static CUtlRBTree< SoundInfo_t, int > g_SoundMessages( 0, 0, CL_SoundMessageLessFunc );
  557. #ifndef LINUX
  558. extern ConVar snd_show;
  559. #endif
  560. //-----------------------------------------------------------------------------
  561. // Purpose: Add sound to queue
  562. // Input : sound -
  563. //-----------------------------------------------------------------------------
  564. void CL_AddSound( const SoundInfo_t &sound )
  565. {
  566. g_SoundMessages.Insert( sound );
  567. }
  568. void CL_SndShow( const char *pName, const SoundInfo_t &pSound )
  569. {
  570. #ifndef LINUX
  571. if ( snd_show.GetInt() >= 2 )
  572. {
  573. DevMsg( "%i (seq %i) %s : src %d : ch %d : %d dB : vol %.2f : time %.3f (%.4f delay) @%.1f %.1f %.1f\n",
  574. host_framecount,
  575. pSound.nSequenceNumber,
  576. pName,
  577. pSound.nEntityIndex,
  578. pSound.nChannel,
  579. pSound.Soundlevel,
  580. pSound.fVolume,
  581. GetBaseLocalClient().GetTime(),
  582. pSound.fDelay,
  583. pSound.vOrigin.x,
  584. pSound.vOrigin.y,
  585. pSound.vOrigin.z );
  586. }
  587. #endif
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose: Play sound packet
  591. // Input : sound -
  592. //-----------------------------------------------------------------------------
  593. void CL_DispatchSound( const SoundInfo_t &sound )
  594. {
  595. StartSoundParams_t params;
  596. // we always want to do this when this flag is set - even if the delay is zero we need to precisely
  597. // schedule this sound
  598. if ( sound.nFlags & SND_DELAY )
  599. {
  600. // anything adjusted less than 100ms forward was probably scheduled this frame
  601. if ( fabs(sound.fDelay) < 0.100f )
  602. {
  603. float soundtime = GetBaseLocalClient().m_flLastServerTickTime + sound.fDelay;
  604. // this adjusts for host_thread_mode or any other cases where we're running more than one
  605. // tick at a time, but we get network updates on the first tick
  606. soundtime -= ((g_ClientGlobalVariables.simTicksThisFrame-1) * host_state.interval_per_tick);
  607. #if 0
  608. static float lastSoundTime = 0;
  609. Msg("[%.3f] Play %s at %.3f\n", soundtime - lastSoundTime, name, soundtime );
  610. lastSoundTime = soundtime;
  611. #endif
  612. // this sound was networked over from the server, use server clock
  613. params.delay = S_ComputeDelayForSoundtime( soundtime, CLOCK_SYNC_SERVER );
  614. if ( params.delay < 0 )
  615. {
  616. params.delay = 0;
  617. }
  618. }
  619. else
  620. {
  621. params.delay = sound.fDelay;
  622. }
  623. }
  624. // copy emitter params
  625. params.staticsound = (sound.nChannel == CHAN_STATIC) ? true : false;
  626. params.soundsource = sound.nEntityIndex;
  627. params.entchannel = params.staticsound ? CHAN_STATIC : sound.nChannel;
  628. params.origin = sound.vOrigin;
  629. params.fvol = sound.fVolume;
  630. params.soundlevel = sound.Soundlevel;
  631. params.flags = sound.nFlags;
  632. params.pitch = sound.nPitch;
  633. params.fromserver = true;
  634. params.delay = sound.fDelay;
  635. params.speakerentity = sound.nSpeakerEntity;
  636. params.m_bIsScriptHandle = ( sound.nFlags & SND_IS_SCRIPTHANDLE ) ? true : false ;
  637. // handle soundentries separately
  638. if ( params.m_bIsScriptHandle )
  639. {
  640. // Don't actually play sounds if playing a demo and skipping ahead
  641. // but always stop sounds
  642. if ( demoplayer->IsSkipping() && !(sound.nFlags&SND_STOP) )
  643. {
  644. return;
  645. }
  646. params.m_nSoundScriptHash = ( HSOUNDSCRIPTHASH ) sound.nSoundNum;
  647. S_StartSoundEntry( params, sound.nRandomSeed, false );
  648. return;
  649. }
  650. // get actual soundfile for old style
  651. CSfxTable *pSfx;
  652. char name[ MAX_QPATH ];
  653. name[ 0 ] = 0;
  654. if ( sound.bIsSentence )
  655. {
  656. // make dummy sfx for sentences
  657. const char *pSentenceName = VOX_SentenceNameFromIndex( sound.nSoundNum );
  658. if ( !pSentenceName )
  659. {
  660. pSentenceName = "";
  661. }
  662. Q_snprintf( name, sizeof( name ), "%c%s", CHAR_SENTENCE, pSentenceName );
  663. pSfx = S_DummySfx( name );
  664. }
  665. else
  666. {
  667. pSfx = GetBaseLocalClient().GetSound( sound.nSoundNum );
  668. if ( ( pSfx != NULL ) && pSfx->m_bIsLateLoad )
  669. {
  670. DevMsg(" Entity '%d' created the late load.\n", sound.nEntityIndex );
  671. }
  672. Q_strncpy( name, GetBaseLocalClient().GetSoundName( sound.nSoundNum ), sizeof( name ) );
  673. }
  674. params.pSfx = pSfx;
  675. CL_SndShow( name, sound );
  676. // Don't actually play sounds if playing a demo and skipping ahead
  677. // but always stop sounds
  678. if ( demoplayer->IsSkipping() && !(sound.nFlags&SND_STOP) )
  679. {
  680. return;
  681. }
  682. S_StartSound( params );
  683. }
  684. //-----------------------------------------------------------------------------
  685. // Purpose: Called after reading network messages to play sounds encoded in the network packet
  686. //-----------------------------------------------------------------------------
  687. void CL_DispatchSounds( void )
  688. {
  689. int i;
  690. // Walk list in sequence order
  691. i = g_SoundMessages.FirstInorder();
  692. while ( i != g_SoundMessages.InvalidIndex() )
  693. {
  694. SoundInfo_t const *msg = &g_SoundMessages[ i ];
  695. Assert( msg );
  696. if ( msg )
  697. {
  698. // Play the sound
  699. CL_DispatchSound( *msg );
  700. }
  701. i = g_SoundMessages.NextInorder( i );
  702. }
  703. // Reset the queue each time we empty it!!!
  704. g_SoundMessages.RemoveAll();
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Retry last connection (e.g., after we enter a password)
  708. //-----------------------------------------------------------------------------
  709. void CL_Retry()
  710. {
  711. CClientState &cl =GetBaseLocalClient();
  712. if ( cl.m_Remote.Count() <= 0 )
  713. {
  714. ConMsg( "Can't retry, no previous connection\n" );
  715. return;
  716. }
  717. ConMsg( "Commencing connection retry to " );
  718. CUtlString cnx;
  719. for ( int i = 0; i < cl.m_Remote.Count(); ++i )
  720. {
  721. const Remote_t &remote = cl.m_Remote.Get( i );
  722. ConMsg( "%s(%s)", remote.m_szAlias.String(), remote.m_szRetryAddress.String() );
  723. //cnx += va( "\"%s\" ", remote.m_szRetryAddress.String() );
  724. cnx += va( "%s ", remote.m_szRetryAddress.String() );
  725. }
  726. ConMsg( "\n" );
  727. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "connect %s\n", cnx.String() ) );
  728. }
  729. //REMOVED FOR PORTAL2:
  730. CON_COMMAND_F( retry, "Retry connection to last server.", FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENTCMD_CAN_EXECUTE )
  731. {
  732. CL_Retry();
  733. }
  734. void SplitString( char const *pchString, CUtlVector< CUtlString > &list )
  735. {
  736. characterset_t breakOnSpaces;
  737. CharacterSetBuild( &breakOnSpaces, " " );
  738. char token[ 1024 ] = { 0 };
  739. const char *pIn = pchString;
  740. char *pOut = token;
  741. while ( *pIn &&
  742. ( ( pOut - token ) < sizeof( token ) - 1 ) )
  743. {
  744. if ( IN_CHARACTERSET( breakOnSpaces, *pIn ) )
  745. {
  746. *pOut = 0;
  747. list.AddToTail( CUtlString( token ) );
  748. pOut = token;
  749. // Skip the space
  750. ++pIn;
  751. continue;
  752. }
  753. *pOut++ = *pIn++;
  754. }
  755. *pOut = 0;
  756. if ( Q_strlen( token ) > 0 )
  757. {
  758. list.AddToTail( CUtlString( token ) );
  759. }
  760. }
  761. /*
  762. =====================
  763. CL_Connect_f
  764. User command to connect to server
  765. =====================
  766. */
  767. CON_COMMAND_F( connect, "Connect to specified server.", FCVAR_DONTRECORD )
  768. {
  769. if ( args.ArgC() < 2 )
  770. {
  771. ConMsg( "Usage: connect <server>\n" );
  772. return;
  773. }
  774. if ( !net_time && !NET_IsMultiplayer() )
  775. {
  776. ConMsg( "Deferring connect command!\n" );
  777. return;
  778. }
  779. char const *pchArgs = args.ArgS();
  780. CUtlVector< CUtlString > argValues;
  781. SplitString( pchArgs, argValues );
  782. if ( argValues.Count() != 1 &&
  783. argValues.Count() != 2 &&
  784. argValues.Count() != 3 )
  785. {
  786. ConMsg( "connect: can't parse '%s'\n", pchArgs );
  787. return;
  788. }
  789. // If it's not a single player connection to "localhost", initialize networking & stop listenserver
  790. if ( !StringHasPrefixCaseSensitive( argValues[ 0 ].String(), "localhost" ) )
  791. {
  792. Host_Disconnect(false);
  793. // allow remote
  794. NET_SetMultiplayer( true );
  795. // start progress bar immediately for remote connection
  796. EngineVGui()->EnabledProgressBarForNextLoad();
  797. SCR_BeginLoadingPlaque();
  798. EngineVGui()->UpdateProgressBar(PROGRESS_BEGINCONNECT);
  799. }
  800. else
  801. {
  802. // we are connecting/reconnecting to local game
  803. // so don't stop listenserver
  804. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  805. {
  806. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  807. GetLocalClient().Disconnect( false );
  808. }
  809. }
  810. // When using '+connect ip:port' in a Steam URL 'steam://run/730//+connect ip:port', we can't use the : character.
  811. // Support using '?' instead and replace all '?' with ':' in the string.
  812. char strAddress[2][64];
  813. int addressCount = 0;
  814. const char* szJoinType = "UnknownJoinType";
  815. for ( int i = 0; i < argValues.Count(); i++ )
  816. {
  817. // Any params starting with dash are join type
  818. if ( argValues[ i ].String()[0] == '-' && argValues[ i ].String()[1] )
  819. {
  820. szJoinType = argValues[ i ].String()+1;
  821. }
  822. else
  823. {
  824. if ( addressCount >= 2 )
  825. continue;
  826. V_strncpy( strAddress[ i ], argValues[ i ].String(), sizeof( strAddress[ i ] ));
  827. char* qmindex = strAddress[ i ];
  828. addressCount++;
  829. while ( ( qmindex = strchr( qmindex ,'?') ) != NULL )
  830. {
  831. *qmindex = ':';
  832. }
  833. }
  834. }
  835. GetBaseLocalClient().Connect( strAddress[ 0 ], addressCount == 2 ? strAddress[ 1 ] : strAddress[ 0 ], szJoinType );
  836. // Reset error conditions
  837. gfExtendedError = false;
  838. }
  839. /*
  840. =====================
  841. CL_Connect_SplitScreen_f
  842. User command to connect to server with multiple players
  843. =====================
  844. */
  845. CON_COMMAND_F( connect_splitscreen, "Connect to specified server. With multiple players.", FCVAR_DONTRECORD | FCVAR_RELEASE | FCVAR_HIDDEN )
  846. {
  847. if ( args.ArgC() < 3 )
  848. {
  849. ConMsg( "Usage: connect <server> <# of players>\n" );
  850. return;
  851. }
  852. char const *pchArgs = args.ArgS();
  853. CUtlVector< CUtlString > argValues;
  854. SplitString( pchArgs, argValues );
  855. if ( argValues.Count() != 2 &&
  856. argValues.Count() != 3 )
  857. {
  858. ConMsg( "connect_splitscreen: can't parse '%s'\n", pchArgs );
  859. return;
  860. }
  861. int last = argValues.Count() - 1;
  862. int numPlayers = Q_atoi( argValues[ last ].String() );
  863. if ( numPlayers <= 0 )
  864. {
  865. ConMsg( "Must have at least one player.\n" );
  866. return;
  867. }
  868. if ( numPlayers > host_state.max_splitscreen_players )
  869. {
  870. ConMsg( "Too many players\n" );
  871. return;
  872. }
  873. // If it's not a single player connection to "localhost", initialize networking & stop listenserver
  874. if ( !StringHasPrefixCaseSensitive( argValues[ 0 ].String(), "localhost" ) )
  875. {
  876. if ( !IsGameConsole() )
  877. return;
  878. Host_Disconnect(false);
  879. // allow remote
  880. NET_SetMultiplayer( true );
  881. // start progress bar immediately for remote connection
  882. EngineVGui()->EnabledProgressBarForNextLoad();
  883. SCR_BeginLoadingPlaque();
  884. EngineVGui()->UpdateProgressBar(PROGRESS_BEGINCONNECT);
  885. }
  886. else
  887. {
  888. // we are connecting/reconnecting to local game
  889. // so don't stop listenserver
  890. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  891. {
  892. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  893. GetLocalClient().Disconnect( false );
  894. }
  895. }
  896. const char* szJoinType = "UnknownJoinType";
  897. for ( int i = 0; i < argValues.Count(); i++ )
  898. {
  899. // Any params starting with dash are join type
  900. if ( argValues[ i ].String()[0] == '-' && argValues[ i ].String()[1] )
  901. {
  902. szJoinType = argValues[ i ].String()+1;
  903. }
  904. }
  905. GetBaseLocalClient().ConnectSplitScreen( argValues[ 0 ].String(), argValues.Count() == 3 ? argValues[ 1 ].String() : argValues[ 0 ].String(), numPlayers, szJoinType );
  906. // Reset error conditions
  907. gfExtendedError = false;
  908. }
  909. #if defined( _X360 ) && !defined( _CERT )
  910. //-----------------------------------------------------------------------------
  911. // Caller must provide secure address string. Establishes connection.
  912. // Returns TRUE if successful. Non-shipping development helper only.
  913. //-----------------------------------------------------------------------------
  914. static bool XLSP_StartConnection( const char *pXLSPAddress, char *pOutAddress, int outAddressSize, unsigned int timeout = 10 * 1000 )
  915. {
  916. // remove the possible port
  917. int nPort = -1;
  918. char cleanAddress[128];
  919. V_strncpy( cleanAddress, pXLSPAddress, sizeof( cleanAddress ) );
  920. char *pPort = strchr( cleanAddress, ':' );
  921. if ( pPort )
  922. {
  923. *pPort++ = '\0';
  924. if ( pPort[0] )
  925. {
  926. nPort = atoi( pPort );
  927. }
  928. }
  929. IN_ADDR xlspAddress;
  930. int ip4[4];
  931. sscanf( cleanAddress, "%d.%d.%d.%d", &ip4[0], &ip4[1], &ip4[2], &ip4[3] );
  932. xlspAddress.S_un.S_un_b.s_b1 = ip4[0];
  933. xlspAddress.S_un.S_un_b.s_b2 = ip4[1];
  934. xlspAddress.S_un.S_un_b.s_b3 = ip4[2];
  935. xlspAddress.S_un.S_un_b.s_b4 = ip4[3];
  936. // track and free last known secure address
  937. static IN_ADDR s_lastSecureAddress;
  938. if ( s_lastSecureAddress.S_un.S_addr != 0x0 )
  939. {
  940. XNetUnregisterInAddr( s_lastSecureAddress );
  941. s_lastSecureAddress.S_un.S_addr = 0x0;
  942. }
  943. IN_ADDR secureAddress;
  944. DWORD dwResult = XNetServerToInAddr( xlspAddress, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &secureAddress );
  945. if ( dwResult != ERROR_SUCCESS )
  946. {
  947. Warning( "Failed to resolve XLSP secure address for %d.%d.%d.%d\n",
  948. xlspAddress.S_un.S_un_b.s_b1,
  949. xlspAddress.S_un.S_un_b.s_b2,
  950. xlspAddress.S_un.S_un_b.s_b3,
  951. xlspAddress.S_un.S_un_b.s_b4 );
  952. return false;
  953. }
  954. s_lastSecureAddress = secureAddress;
  955. Msg( "Attempting connection to XLSP title server using, XLSP:%d.%d.%d.%d -> Secure:%d.%d.%d.%d\n",
  956. xlspAddress.S_un.S_un_b.s_b1,
  957. xlspAddress.S_un.S_un_b.s_b2,
  958. xlspAddress.S_un.S_un_b.s_b3,
  959. xlspAddress.S_un.S_un_b.s_b4,
  960. secureAddress.S_un.S_un_b.s_b1,
  961. secureAddress.S_un.S_un_b.s_b2,
  962. secureAddress.S_un.S_un_b.s_b3,
  963. secureAddress.S_un.S_un_b.s_b4 );
  964. // Connect to server
  965. int iResult;
  966. if ( ( iResult = XNetConnect( secureAddress ) ) != 0 )
  967. {
  968. Warning( "XNetConnect: failed with %d.\n", iResult );
  969. return false;
  970. }
  971. // Wait for the SG connection to complete
  972. unsigned long startTime = Plat_MSTime();
  973. while ( ( dwResult = XNetGetConnectStatus( secureAddress ) ) == XNET_CONNECT_STATUS_PENDING )
  974. {
  975. if ( ( Plat_MSTime() - startTime ) > timeout )
  976. {
  977. Warning( "XNetConnect: Timeout, took longer than %ums to complete.\n", timeout );
  978. return false;
  979. }
  980. Sleep( 100 );
  981. }
  982. if ( dwResult != XNET_CONNECT_STATUS_CONNECTED )
  983. {
  984. Warning( "XNetGetConnectStatus: connection failed with %d.\n", dwResult );
  985. return false;
  986. }
  987. if ( nPort != -1 )
  988. {
  989. V_snprintf(
  990. pOutAddress,
  991. outAddressSize,
  992. "%d.%d.%d.%d:%d",
  993. secureAddress.S_un.S_un_b.s_b1,
  994. secureAddress.S_un.S_un_b.s_b2,
  995. secureAddress.S_un.S_un_b.s_b3,
  996. secureAddress.S_un.S_un_b.s_b4,
  997. nPort );
  998. }
  999. else
  1000. {
  1001. V_snprintf(
  1002. pOutAddress,
  1003. outAddressSize,
  1004. "%d.%d.%d.%d",
  1005. secureAddress.S_un.S_un_b.s_b1,
  1006. secureAddress.S_un.S_un_b.s_b2,
  1007. secureAddress.S_un.S_un_b.s_b3,
  1008. secureAddress.S_un.S_un_b.s_b4 );
  1009. }
  1010. Msg( "Connected to XLSP title server.\n" );
  1011. return true;
  1012. }
  1013. #endif
  1014. #if defined( _X360 ) && !defined( _CERT )
  1015. //-----------------------------------------------------------------------------
  1016. // Assume non-numeric name string address represents XLSP because system link
  1017. // can only be IP4. Resolve name to secure IP4.
  1018. // Returns -1 if error, Returns 0 if not XLSP, Returns 1 if valid.
  1019. // Non-shipping development helper only.
  1020. //-----------------------------------------------------------------------------
  1021. static int XLSP_NameToAddress( const char *pAddress, char *pOutAddress, int outAddressSize )
  1022. {
  1023. // want string names, but not this one
  1024. if ( StringHasPrefixCaseSensitive( pAddress, "localhost" ) )
  1025. {
  1026. return 0;
  1027. }
  1028. // do not use DNS, not trying to resolve the name
  1029. netadr_t netadr;
  1030. netadr.SetFromString( pAddress, false );
  1031. if ( netadr.IsBaseAdrValid() )
  1032. {
  1033. // not a string name, ignore
  1034. return 0;
  1035. }
  1036. // remove the possible port
  1037. int nPort = -1;
  1038. char cleanAddress[128];
  1039. V_strncpy( cleanAddress, pAddress, sizeof( cleanAddress ) );
  1040. char *pPort = strchr( cleanAddress, ':' );
  1041. if ( pPort )
  1042. {
  1043. *pPort++ = '\0';
  1044. if ( pPort[0] )
  1045. {
  1046. nPort = atoi( pPort );
  1047. }
  1048. }
  1049. XTITLE_SERVER_INFO serverInfos[XTITLE_SERVER_MAX_LSP_INFO];
  1050. V_memset( serverInfos, 0, sizeof( serverInfos ) );
  1051. // get a title server enumerator
  1052. IN_ADDR serverAddr = { 0 };
  1053. bool bFound = false;
  1054. DWORD dwBufferSize = 0;
  1055. HANDLE hServerEnum = INVALID_HANDLE_VALUE;
  1056. DWORD dwServerCount = 0;
  1057. DWORD dwResult = XTitleServerCreateEnumerator( NULL, XTITLE_SERVER_MAX_LSP_INFO, &dwBufferSize, &hServerEnum );
  1058. if ( dwResult == ERROR_SUCCESS )
  1059. {
  1060. // synchronous population
  1061. dwResult = XEnumerate( hServerEnum, serverInfos, sizeof( serverInfos ), &dwServerCount, NULL );
  1062. CloseHandle( hServerEnum );
  1063. if ( dwResult == ERROR_SUCCESS )
  1064. {
  1065. // iterate all known servers, try and find match
  1066. for ( unsigned int i = 0; i < dwServerCount; i++ )
  1067. {
  1068. // get the clean name
  1069. char cleanName[XTITLE_SERVER_MAX_SERVER_INFO_LEN];
  1070. V_strncpy( cleanName, serverInfos[i].szServerInfo, sizeof( cleanName ) );
  1071. char *pArgs = V_stristr( cleanName, "**" );
  1072. if ( pArgs )
  1073. {
  1074. *pArgs = '\0';
  1075. }
  1076. if ( !V_stricmp( cleanAddress, cleanName ) )
  1077. {
  1078. serverAddr = serverInfos[i].inaServer;
  1079. bFound = true;
  1080. break;
  1081. }
  1082. }
  1083. }
  1084. }
  1085. if ( !bFound )
  1086. {
  1087. return -1;
  1088. }
  1089. if ( nPort != -1 )
  1090. {
  1091. V_snprintf(
  1092. pOutAddress,
  1093. outAddressSize,
  1094. "%d.%d.%d.%d:%d",
  1095. serverAddr.S_un.S_un_b.s_b1,
  1096. serverAddr.S_un.S_un_b.s_b2,
  1097. serverAddr.S_un.S_un_b.s_b3,
  1098. serverAddr.S_un.S_un_b.s_b4,
  1099. nPort );
  1100. }
  1101. else
  1102. {
  1103. V_snprintf(
  1104. pOutAddress,
  1105. outAddressSize,
  1106. "%d.%d.%d.%d",
  1107. serverAddr.S_un.S_un_b.s_b1,
  1108. serverAddr.S_un.S_un_b.s_b2,
  1109. serverAddr.S_un.S_un_b.s_b3,
  1110. serverAddr.S_un.S_un_b.s_b4 );
  1111. }
  1112. // success
  1113. return 1;
  1114. }
  1115. #endif
  1116. #if defined( _X360 ) && !defined( _CERT )
  1117. // Non-shipping development helper only.
  1118. CON_COMMAND( xlsp_list_servers, "Spew XLSP title servers" )
  1119. {
  1120. XTITLE_SERVER_INFO serverInfos[XTITLE_SERVER_MAX_LSP_INFO];
  1121. V_memset( serverInfos, 0, sizeof( serverInfos ) );
  1122. // get a title server enumerator
  1123. DWORD dwBufferSize = 0;
  1124. HANDLE hServerEnum = INVALID_HANDLE_VALUE;
  1125. DWORD dwResult = XTitleServerCreateEnumerator( NULL, XTITLE_SERVER_MAX_LSP_INFO, &dwBufferSize, &hServerEnum );
  1126. if ( dwResult != ERROR_SUCCESS )
  1127. {
  1128. Warning( "XTitleServerCreateEnumerator: failed with 0x%0x.\n", dwResult );
  1129. return;
  1130. }
  1131. // synchronous population
  1132. DWORD dwServerCount = 0;
  1133. dwResult = XEnumerate( hServerEnum, serverInfos, sizeof( serverInfos ), &dwServerCount, NULL );
  1134. CloseHandle( hServerEnum );
  1135. if ( dwResult != ERROR_SUCCESS || !dwServerCount )
  1136. {
  1137. Msg( "No XLSP servers found.\n" );
  1138. return;
  1139. }
  1140. // iterate and spew
  1141. Msg( "\nXLSP Title Servers:\n" );
  1142. for ( DWORD i = 0; i < dwServerCount; ++i )
  1143. {
  1144. // decode private additional args
  1145. char *pToken = V_stristr( serverInfos[i].szServerInfo, "**" );
  1146. if ( !pToken )
  1147. {
  1148. // bad non-conformant syntax, unknown
  1149. continue;
  1150. }
  1151. *pToken = '\0';
  1152. pToken += 2;
  1153. // change to whitespace for easy tokenization
  1154. char argString[XTITLE_SERVER_MAX_SERVER_INFO_LEN];
  1155. V_strncpy( argString, pToken, sizeof( argString ) );
  1156. pToken = argString;
  1157. while ( 1 )
  1158. {
  1159. pToken = strchr( pToken, '_' );
  1160. if ( !pToken )
  1161. {
  1162. break;
  1163. }
  1164. *pToken++ = ' ';
  1165. }
  1166. // get the port and range
  1167. int nPort = 0;
  1168. int nNumPorts = 0;
  1169. int nMasterPort = 0;
  1170. int nNumMasterPorts = 0;
  1171. sscanf( argString, "%d %d %d %d", &nMasterPort, &nNumMasterPorts, &nPort, &nNumPorts );
  1172. // send an async QOS probe to XLSP SG and wait
  1173. DWORD serviceID = g_pMatchFramework->GetMatchTitle()->GetTitleServiceID();
  1174. XNQOS *pXNQOS = NULL;
  1175. DWORD wRttMinInMsecs = 0;
  1176. DWORD wRttMedInMsecs = 0;
  1177. DWORD dwUpBitsPerSec = 0;
  1178. DWORD dwDnBitsPerSec = 0;
  1179. dwResult = XNetQosLookup( 0, NULL, NULL, NULL, 1, &serverInfos[i].inaServer, &serviceID, 8, 0, 0, NULL, &pXNQOS );
  1180. if ( dwResult == ERROR_SUCCESS )
  1181. {
  1182. while ( pXNQOS->cxnqosPending != 0 )
  1183. {
  1184. Sleep( 100 );
  1185. }
  1186. if ( pXNQOS->axnqosinfo[0].bFlags & XNET_XNQOSINFO_COMPLETE )
  1187. {
  1188. // meaningful results
  1189. wRttMinInMsecs = pXNQOS->axnqosinfo[0].wRttMinInMsecs;
  1190. wRttMedInMsecs = pXNQOS->axnqosinfo[0].wRttMedInMsecs;
  1191. dwUpBitsPerSec = pXNQOS->axnqosinfo[0].dwUpBitsPerSec;
  1192. dwDnBitsPerSec = pXNQOS->axnqosinfo[0].dwDnBitsPerSec;
  1193. }
  1194. XNetQosRelease( pXNQOS );
  1195. pXNQOS = NULL;
  1196. }
  1197. // spew
  1198. Msg( "Virtual IP4 RTT AvgRTT Upstream Dnstream\n" );
  1199. Msg( "--------------- ------ ------ -------- --------\n" );
  1200. Msg( "%3d.%3d.%3d.%3d %4dms %4dms %4dkbps %4dkbps \"%s\"\n",
  1201. serverInfos[i].inaServer.S_un.S_un_b.s_b1,
  1202. serverInfos[i].inaServer.S_un.S_un_b.s_b2,
  1203. serverInfos[i].inaServer.S_un.S_un_b.s_b3,
  1204. serverInfos[i].inaServer.S_un.S_un_b.s_b4,
  1205. wRttMinInMsecs,
  1206. wRttMedInMsecs,
  1207. dwUpBitsPerSec/1024,
  1208. dwDnBitsPerSec/1024,
  1209. serverInfos[i].szServerInfo );
  1210. Msg( " Title Server Ports: [%d..%d]\n", nPort, nPort + nNumPorts - 1 );
  1211. Msg( " Master Server Ports: [%d..%d]\n", nMasterPort, nMasterPort + nNumMasterPorts - 1 );
  1212. }
  1213. }
  1214. #endif
  1215. #if defined( _X360 ) && !defined( _CERT )
  1216. // Non-shipping development helper only.
  1217. CON_COMMAND_F( connect_xlsp, "Direct connection to specified xlsp server.", FCVAR_DONTRECORD )
  1218. {
  1219. // must have xlsp port specifier
  1220. if ( args.ArgC() < 4 || args.Arg( 2 )[0] != ':' )
  1221. {
  1222. ConMsg( "Usage: connect_xlsp <xlsp_server:port> [# of players]\n" );
  1223. return;
  1224. }
  1225. // the tokenizer breaks the address up if a port is provided, we need to reassemble.
  1226. char address[128];
  1227. V_strcpy_safe( address, args.Arg( 1 ) );
  1228. Q_strcat( address, args.Arg( 2 ), sizeof( address ) );
  1229. Q_strcat( address, args.Arg( 3 ), sizeof( address ) );
  1230. int numPlayers = 1;
  1231. if ( args.ArgC() >= 5 )
  1232. {
  1233. numPlayers = atoi( args.Arg( 4 ) );
  1234. }
  1235. // due to dev purposes, do xlsp resolve synchronously
  1236. char xlspAddress[128];
  1237. char secureAddress[128];
  1238. int result = XLSP_NameToAddress( address, xlspAddress, sizeof( xlspAddress ) );
  1239. if ( result > 0 )
  1240. {
  1241. // have valid xlsp, establish connection
  1242. if ( !XLSP_StartConnection( xlspAddress, secureAddress, sizeof( secureAddress ) ) )
  1243. {
  1244. // failed
  1245. Warning( "Could not connect to XLSP server %s.\n", address );
  1246. return;
  1247. }
  1248. // address has been convoluted to the XLSP secure private address
  1249. V_strncpy( address, secureAddress, sizeof( address ) );
  1250. }
  1251. else if ( result == 0 )
  1252. {
  1253. Warning( "Not an XLSP server address %s.\n", address );
  1254. return;
  1255. }
  1256. else
  1257. {
  1258. Warning( "No XLSP server found matching %s.\n", address );
  1259. return;
  1260. }
  1261. CCommand newArgs;
  1262. newArgs.Tokenize( CFmtStr( "connect_splitscreen %s %d", secureAddress, numPlayers ) );
  1263. connect_splitscreen( newArgs );
  1264. }
  1265. #endif
  1266. //-----------------------------------------------------------------------------
  1267. // Takes the map name, strips path and extension
  1268. //-----------------------------------------------------------------------------
  1269. void CL_SetupMapName( const char* pName, char* pFixedName, int maxlen )
  1270. {
  1271. const char* pSlash = strrchr( pName, '\\' );
  1272. const char* pSlash2 = strrchr( pName, '/' );
  1273. if (pSlash2 > pSlash)
  1274. pSlash = pSlash2;
  1275. if (pSlash)
  1276. ++pSlash;
  1277. else
  1278. pSlash = pName;
  1279. Q_strncpy( pFixedName, pSlash, maxlen );
  1280. char* pExt = strchr( pFixedName, '.' );
  1281. if (pExt)
  1282. *pExt = 0;
  1283. }
  1284. CPureServerWhitelist* CL_LoadWhitelist( INetworkStringTable *pTable, const char *pName )
  1285. {
  1286. // If there is no entry for the pure server whitelist, then sv_pure is off and the client can do whatever it wants.
  1287. int iString = pTable->FindStringIndex( pName );
  1288. if ( iString == INVALID_STRING_INDEX )
  1289. return NULL;
  1290. int dataLen;
  1291. const void *pData = pTable->GetStringUserData( iString, &dataLen );
  1292. if ( pData )
  1293. {
  1294. CUtlBuffer buf( pData, dataLen, CUtlBuffer::READ_ONLY );
  1295. CPureServerWhitelist *pWhitelist = CPureServerWhitelist::Create( g_pFullFileSystem );
  1296. pWhitelist->Decode( buf );
  1297. return pWhitelist;
  1298. }
  1299. else
  1300. {
  1301. return NULL;
  1302. }
  1303. }
  1304. void CL_CheckForPureServerWhitelist()
  1305. {
  1306. #ifdef DISABLE_PURE_SERVER_STUFF
  1307. return;
  1308. #endif
  1309. // Don't do sv_pure stuff in SP games or HLTV/replay
  1310. if ( GetBaseLocalClient().m_nMaxClients <= 1 || GetBaseLocalClient().ishltv
  1311. #ifdef REPLAY_ENABLED
  1312. || GetBaseLocalClient().isreplay
  1313. #endif // ifdef REPLAY_ENABLED
  1314. )
  1315. return;
  1316. CPureServerWhitelist *pWhitelist = NULL;
  1317. if ( GetBaseLocalClient().m_pServerStartupTable )
  1318. pWhitelist = CL_LoadWhitelist( GetBaseLocalClient().m_pServerStartupTable, "PureServerWhitelist" );
  1319. if ( pWhitelist )
  1320. {
  1321. if ( pWhitelist->IsInFullyPureMode() )
  1322. Msg( "Got pure server whitelist: sv_pure = 2.\n" );
  1323. else
  1324. Msg( "Got pure server whitelist: sv_pure = 1.\n" );
  1325. CL_HandlePureServerWhitelist( pWhitelist );
  1326. }
  1327. else
  1328. {
  1329. Msg( "No pure server whitelist. sv_pure = 0\n" );
  1330. CL_HandlePureServerWhitelist( NULL );
  1331. }
  1332. }
  1333. int CL_GetServerQueryPort()
  1334. {
  1335. // Yes, this is ugly getting this data out of a string table. Would be better to have it in our network protocol,
  1336. // but we don't have a way to change the protocol without breaking things for people.
  1337. if ( !GetBaseLocalClient().m_pServerStartupTable )
  1338. return 0;
  1339. int iString = GetBaseLocalClient().m_pServerStartupTable->FindStringIndex( "QueryPort" );
  1340. if ( iString == INVALID_STRING_INDEX )
  1341. return 0;
  1342. int dataLen;
  1343. const void *pData = GetBaseLocalClient().m_pServerStartupTable->GetStringUserData( iString, &dataLen );
  1344. if ( pData && dataLen == sizeof( int ) )
  1345. return *((const int*)pData);
  1346. else
  1347. return 0;
  1348. }
  1349. /*
  1350. ==================
  1351. CL_RegisterResources
  1352. Clean up and move to next part of sequence.
  1353. ==================
  1354. */
  1355. void CL_RegisterResources( void )
  1356. {
  1357. // All done precaching.
  1358. host_state.SetWorldModel( GetBaseLocalClient().GetModel( 1 ) );
  1359. if ( !host_state.worldmodel )
  1360. {
  1361. Host_Error( "CL_RegisterResources: host_state.worldmodel/GetBaseLocalClient().GetModel( 1 )==NULL\n" );
  1362. }
  1363. // Force main window to repaint... (only does something if running shaderapi
  1364. videomode->InvalidateWindow();
  1365. }
  1366. #ifndef DEDICATED
  1367. class CEngineReliableAvatarCallback_t
  1368. {
  1369. public:
  1370. CEngineReliableAvatarCallback_t() : m_steamID( Steam3Client().SteamUser()->GetSteamID() )
  1371. , m_CallbackPersonaStateChanged( this, &CEngineReliableAvatarCallback_t::Steam_OnPersonaStateChanged )
  1372. , m_CallbackAvatarImageLoaded( this, &CEngineReliableAvatarCallback_t::Steam_OnAvatarImageLoaded )
  1373. {
  1374. }
  1375. void UploadMyOwnAvatarToGameServer();
  1376. private:
  1377. STEAM_CALLBACK( CEngineReliableAvatarCallback_t, Steam_OnPersonaStateChanged, PersonaStateChange_t, m_CallbackPersonaStateChanged );
  1378. STEAM_CALLBACK( CEngineReliableAvatarCallback_t, Steam_OnAvatarImageLoaded, AvatarImageLoaded_t, m_CallbackAvatarImageLoaded );
  1379. CSteamID m_steamID;
  1380. };
  1381. void CEngineReliableAvatarCallback_t::Steam_OnAvatarImageLoaded( AvatarImageLoaded_t *pParam )
  1382. {
  1383. if ( pParam && ( pParam->m_steamID == m_steamID ) )
  1384. UploadMyOwnAvatarToGameServer();
  1385. }
  1386. void CEngineReliableAvatarCallback_t::Steam_OnPersonaStateChanged( PersonaStateChange_t *pParam )
  1387. {
  1388. if ( pParam && ( pParam->m_nChangeFlags & k_EPersonaChangeAvatar ) && ( pParam->m_ulSteamID == m_steamID.ConvertToUint64() ) )
  1389. UploadMyOwnAvatarToGameServer();
  1390. }
  1391. void CEngineReliableAvatarCallback_t::UploadMyOwnAvatarToGameServer()
  1392. {
  1393. if ( GetBaseLocalClient().IsActive() )
  1394. {
  1395. // Kick off upload for our own avatar data now
  1396. if ( CNETMsg_PlayerAvatarData_t *pMsgMyAvatarData = GetBaseLocalClient().AllocOwnPlayerAvatarData() )
  1397. {
  1398. GetBaseLocalClient().m_NetChannel->EnqueueVeryLargeAsyncTransfer( *pMsgMyAvatarData );
  1399. delete pMsgMyAvatarData;
  1400. }
  1401. }
  1402. }
  1403. #endif
  1404. void CL_FullyConnected( void )
  1405. {
  1406. CETWScope timer( "CL_FullyConnected" );
  1407. EngineVGui()->UpdateProgressBar( PROGRESS_FULLYCONNECTED );
  1408. // This has to happen HERE. ***** PRIOR TO g_pQueuedLoader->EndMapLoading() *****
  1409. // in phase 3, because it is in this phase
  1410. // that raycasts against the world is supported (owing to the fact
  1411. // that the world entity has been created by this point)
  1412. StaticPropMgr()->LevelInitClient();
  1413. if ( IsGameConsole() )
  1414. {
  1415. // Notify the loader the end of the loading context, preloads are about to be purged
  1416. g_pQueuedLoader->EndMapLoading( false );
  1417. if ( IsPS3() )
  1418. {
  1419. // PS3 static prop lighting (legacy async IO still in flight catching
  1420. // non reslist-lighting buffers) is writing data into raw pointers
  1421. // to RSX memory which have been acquired before material system
  1422. // switches to multithreaded mode. During switch to multithreaded
  1423. // mode RSX moves its memory so pointers become invalid and thus
  1424. // all IO must be finished and callbacks fired before
  1425. // Host_AllowQueuedMaterialSystem
  1426. g_pFullFileSystem->AsyncFinishAll();
  1427. }
  1428. }
  1429. // loading completed
  1430. // flush client-side dynamic models that have no refcount
  1431. modelloader->FlushDynamicModels();
  1432. // can NOW safely purge unused models and their data hierarchy (materials, shaders, etc)
  1433. modelloader->PurgeUnusedModels();
  1434. #ifdef _PS3
  1435. g_pMaterialSystem->CompactRsxLocalMemory( "FULLY CONNECTED0" );
  1436. #endif
  1437. // loading is complete, hint the view model cache to its initial state and evict everybody
  1438. // this is a safety catch for any view models that leaked in, the eviction should be a near no-op
  1439. modelloader->EvictAllWeaponsFromModelCache( true );
  1440. // Purge the preload stores, oreder is critical
  1441. g_pMDLCache->ShutdownPreloadData();
  1442. // NOTE: purposely disabling...
  1443. // THIS IS A BAD THING, so purposely disabled... it saves a few MB that otherwise prevents a bunch of hitchy sync i/o during gameplay.
  1444. // Also, memory spike causing issues, so preload's stay in
  1445. // This may show up on the memory users, and the preloads can be made smaller, or maybe accidentally too large, but ditching them is not the right thing to do.
  1446. // g_pFileSystem->DiscardPreloadData();
  1447. // ***************************************************************
  1448. // NO MORE PRELOAD DATA AVAILABLE PAST THIS POINT!!!
  1449. // ***************************************************************
  1450. g_ClientDLL->LevelInitPostEntity();
  1451. // communicate to tracker that we're in a game
  1452. // WHY ARE WE USING NETWORK BYTE ORDER HERE?
  1453. uint32 ip = 0;
  1454. uint16 port = 0;
  1455. ns_address remoteAdr = GetBaseLocalClient().m_NetChannel->GetRemoteAddress();
  1456. if ( remoteAdr.IsType<netadr_t>() )
  1457. {
  1458. ip = remoteAdr.AsType<netadr_t>().GetIPNetworkByteOrder();
  1459. port = remoteAdr.AsType<netadr_t>().GetPort();
  1460. if (!port)
  1461. {
  1462. ip = net_local_adr.GetIPNetworkByteOrder();
  1463. port = net_local_adr.GetPort();
  1464. }
  1465. }
  1466. else
  1467. {
  1468. #ifdef _DEBUG
  1469. Warning( "CL_FullyConnected - NotifyOfServerConnect assumes remote host is always IPv4 address, but we're connected to %s\n", ns_address_render( remoteAdr ).String() );
  1470. #endif
  1471. }
  1472. int iQueryPort = CL_GetServerQueryPort();
  1473. EngineVGui()->NotifyOfServerConnect(com_gamedir, ip, port, iQueryPort);
  1474. GetTestScriptMgr()->CheckPoint( "FinishedMapLoad" );
  1475. EngineVGui()->UpdateProgressBar( PROGRESS_READYTOPLAY );
  1476. if ( ( !IsX360() && !IsPS3() ) || GetBaseLocalClient().m_nMaxClients == 1 )
  1477. {
  1478. // Need this to persist for multiplayer respawns, 360 can't reload
  1479. // [mhansen] PS3 is failing to reload also. We should probably figure out why that is
  1480. // but keeping it around should fix it for now.
  1481. CM_DiscardEntityString();
  1482. }
  1483. g_pMDLCache->EndMapLoad();
  1484. #if defined( _MEMTEST )
  1485. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "mem_dump\n" );
  1486. #endif
  1487. if ( developer.GetInt() > 0 )
  1488. {
  1489. CClientState &cl = GetBaseLocalClient();
  1490. ConDMsg( "Signon traffic \"%s\": incoming %s [%d pkts], outgoing %s [%d pkts]\n",
  1491. GetBaseLocalClient().m_NetChannel->GetName(),
  1492. Q_pretifymem( cl.m_NetChannel->GetTotalData( FLOW_INCOMING ), 3 ),
  1493. cl.m_NetChannel->GetTotalPackets( FLOW_INCOMING ),
  1494. Q_pretifymem( cl.m_NetChannel->GetTotalData( FLOW_OUTGOING ), 3 ),
  1495. cl.m_NetChannel->GetTotalPackets( FLOW_OUTGOING ) );
  1496. }
  1497. if ( IsX360() )
  1498. {
  1499. // Reset material system temporary memory (once loading is complete), ready for in-map use
  1500. bool bOnLevelShutdown = false;
  1501. materials->ResetTempHWMemory( bOnLevelShutdown );
  1502. }
  1503. // matsys gets a hint that loading operations have ceased and gameplay is about to start
  1504. g_pMaterialSystem->OnLevelLoadingComplete();
  1505. // allow normal screen updates
  1506. SCR_EndLoadingPlaque();
  1507. EndLoadingUpdates();
  1508. #ifdef _PS3
  1509. g_pMaterialSystem->CompactRsxLocalMemory( "FULLY CONNECTED" );
  1510. #endif
  1511. // background maps are for main menu UI, QMS not needed or used, easier context
  1512. if ( !engineClient->IsLevelMainMenuBackground() )
  1513. {
  1514. // map load complete, safe to allow QMS
  1515. ConVarRef mat_queue_mode( "mat_queue_mode" );
  1516. if ( mat_queue_mode.GetInt() != 0 )
  1517. {
  1518. Host_AllowQueuedMaterialSystem( true );
  1519. }
  1520. }
  1521. // This is a Hack, but we need to suppress rendering for a bit in single player to let values settle on the client
  1522. if ( (GetBaseLocalClient().m_nMaxClients == 1) && !demoplayer->IsPlayingBack() )
  1523. {
  1524. scr_nextdrawtick = host_tickcount + TIME_TO_TICKS( 0.25f );
  1525. }
  1526. #ifdef _X360
  1527. // At the conclusion of loading with a valid user check for a valid controller connection.
  1528. // If it's been lost, then we need to pop our game UI up
  1529. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  1530. {
  1531. int iController = XBX_GetUserId( k );
  1532. if ( iController != XBX_INVALID_USER_ID )
  1533. {
  1534. XINPUT_CAPABILITIES caps;
  1535. if ( XInputGetCapabilities( iController, XINPUT_FLAG_GAMEPAD, &caps ) == ERROR_DEVICE_NOT_CONNECTED )
  1536. {
  1537. EngineVGui()->ActivateGameUI();
  1538. break;
  1539. }
  1540. }
  1541. }
  1542. #endif
  1543. // Now that we're connected, toggle the clan tag so it gets sent to the server
  1544. //ConVarRef cl_clanid( "cl_clanid" );
  1545. //const char *pClanID = cl_clanid.GetString();
  1546. //cl_clanid.SetValue( 0 );
  1547. //cl_clanid.SetValue( id );
  1548. SplitScreenConVarRef varOption( "cl_clanid" );
  1549. const char *pClanID = varOption.GetString( 0 );
  1550. varOption.SetValue( 0, pClanID );
  1551. g_pMemAlloc->CompactHeap();
  1552. extern double g_flAccumulatedModelLoadTime;
  1553. extern double g_flAccumulatedSoundLoadTime;
  1554. extern double g_flAccumulatedModelLoadTimeStudio;
  1555. extern double g_flAccumulatedModelLoadTimeVCollideSync;
  1556. extern double g_flAccumulatedModelLoadTimeVCollideAsync;
  1557. extern double g_flAccumulatedModelLoadTimeVirtualModel;
  1558. extern double g_flAccumulatedModelLoadTimeStaticMesh;
  1559. extern double g_flAccumulatedModelLoadTimeBrush;
  1560. extern double g_flAccumulatedModelLoadTimeSprite;
  1561. extern double g_flAccumulatedModelLoadTimeMaterialNamesOnly;
  1562. // extern double g_flLoadStudioHdr;
  1563. COM_TimestampedLog( "Sound Loading time %.4f", g_flAccumulatedSoundLoadTime );
  1564. COM_TimestampedLog( "Model Loading time %.4f", g_flAccumulatedModelLoadTime );
  1565. COM_TimestampedLog( " Model Loading time studio %.4f", g_flAccumulatedModelLoadTimeStudio );
  1566. COM_TimestampedLog( " Model Loading time GetVCollide %.4f -sync", g_flAccumulatedModelLoadTimeVCollideSync );
  1567. COM_TimestampedLog( " Model Loading time GetVCollide %.4f -async", g_flAccumulatedModelLoadTimeVCollideAsync );
  1568. COM_TimestampedLog( " Model Loading time GetVirtualModel %.4f", g_flAccumulatedModelLoadTimeVirtualModel );
  1569. COM_TimestampedLog( " Model loading time Mod_GetModelMaterials only %.4f", g_flAccumulatedModelLoadTimeMaterialNamesOnly );
  1570. COM_TimestampedLog( " Model Loading time world %.4f", g_flAccumulatedModelLoadTimeBrush );
  1571. COM_TimestampedLog( " Model Loading time sprites %.4f", g_flAccumulatedModelLoadTimeSprite );
  1572. COM_TimestampedLog( " Model Loading time meshes %.4f", g_flAccumulatedModelLoadTimeStaticMesh );
  1573. // COM_TimestampedLog( " Model Loading time meshes studiohdr load %.4f", g_flLoadStudioHdr );
  1574. COM_TimestampedLog( "*** Map Load Complete" );
  1575. // The reslist generator can't start its timeout until the level loading is absolutely finished
  1576. MapReslistGenerator().OnFullyConnected();
  1577. if ( CommandLine()->FindParm( "-profilemapload" ) )
  1578. {
  1579. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "quit\n" );
  1580. }
  1581. if ( CommandLine()->FindParm( "-interactivecaster" ) )
  1582. {
  1583. engineClient->ClientCmd( "cameraman_request" );
  1584. }
  1585. #ifndef DEDICATED
  1586. // Register a listener that will be uploading our own avatar data to the game server
  1587. static CEngineReliableAvatarCallback_t s_EngineReliableAvatarCallback;
  1588. s_EngineReliableAvatarCallback.UploadMyOwnAvatarToGameServer();
  1589. #endif
  1590. }
  1591. /*
  1592. =====================
  1593. CL_NextDemo
  1594. Called to play the next demo in the demo loop
  1595. =====================
  1596. */
  1597. void CL_NextDemo (void)
  1598. {
  1599. char str[1024];
  1600. if (GetBaseLocalClient().demonum == -1)
  1601. return; // don't play demos
  1602. SCR_BeginLoadingPlaque ();
  1603. if (!GetBaseLocalClient().demos[GetBaseLocalClient().demonum][0] || GetBaseLocalClient().demonum == MAX_DEMOS)
  1604. {
  1605. GetBaseLocalClient().demonum = 0;
  1606. if (!GetBaseLocalClient().demos[GetBaseLocalClient().demonum][0])
  1607. {
  1608. scr_disabled_for_loading = false;
  1609. ConMsg ("No demos listed with startdemos\n");
  1610. GetBaseLocalClient().demonum = -1;
  1611. return;
  1612. }
  1613. }
  1614. Q_snprintf (str,sizeof( str ), "playdemo %s", GetBaseLocalClient().demos[GetBaseLocalClient().demonum]);
  1615. Cbuf_AddText( Cbuf_GetCurrentPlayer(), str );
  1616. GetBaseLocalClient().demonum++;
  1617. }
  1618. ConVar cl_screenshotname( "cl_screenshotname", "", 0, "Custom Screenshot name" );
  1619. // We'll take a snapshot at the next available opportunity
  1620. void CL_TakeScreenshot(const char *name)
  1621. {
  1622. cl_takesnapshot = true;
  1623. cl_snapshot_fullpathname[0] = 0;
  1624. cl_takejpeg = false;
  1625. if ( name != NULL )
  1626. {
  1627. Q_strncpy( cl_snapshotname, name, sizeof( cl_snapshotname ) );
  1628. }
  1629. else
  1630. {
  1631. cl_snapshotname[0] = 0;
  1632. if ( Q_strlen( cl_screenshotname.GetString() ) > 0 )
  1633. {
  1634. Q_snprintf( cl_snapshotname, sizeof( cl_snapshotname ), "%s", cl_screenshotname.GetString() );
  1635. }
  1636. }
  1637. cl_snapshot_subdirname[0] = 0;
  1638. }
  1639. CON_COMMAND_F( screenshot, "Take a screenshot.", FCVAR_CLIENTCMD_CAN_EXECUTE )
  1640. {
  1641. GetTestScriptMgr()->SetWaitCheckPoint( "screenshot" );
  1642. // Don't playback screenshots unless specifically requested.
  1643. if ( demoplayer->IsPlayingBack() && !cl_playback_screenshots.GetBool() )
  1644. return;
  1645. if( args.ArgC() == 2 )
  1646. {
  1647. CL_TakeScreenshot( args[ 1 ] );
  1648. }
  1649. else
  1650. {
  1651. CL_TakeScreenshot( NULL );
  1652. }
  1653. }
  1654. 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 )
  1655. {
  1656. CL_TakeScreenshot( NULL );
  1657. // See if we got a subdirectory to store the devshots in
  1658. if ( args.ArgC() == 2 )
  1659. {
  1660. Q_strncpy( cl_snapshot_subdirname, args[1], sizeof( cl_snapshot_subdirname ) );
  1661. // Use the first available shot in each subdirectory
  1662. cl_snapshotnum = 0;
  1663. }
  1664. }
  1665. // We'll take a snapshot at the next available opportunity
  1666. void CL_TakeJpeg(const char *name, int quality)
  1667. {
  1668. // Don't playback screenshots unless specifically requested.
  1669. if ( demoplayer->IsPlayingBack() && !cl_playback_screenshots.GetBool() )
  1670. return;
  1671. cl_takesnapshot = true;
  1672. cl_snapshot_fullpathname[0] = 0;
  1673. cl_takejpeg = true;
  1674. cl_jpegquality = clamp( quality, 1, 100 );
  1675. if ( name != NULL )
  1676. {
  1677. Q_strncpy( cl_snapshotname, name, sizeof( cl_snapshotname ) );
  1678. }
  1679. else
  1680. {
  1681. cl_snapshotname[0] = 0;
  1682. }
  1683. }
  1684. CON_COMMAND( jpeg, "Take a jpeg screenshot: jpeg <filename> <quality 1-100>." )
  1685. {
  1686. if( args.ArgC() >= 2 )
  1687. {
  1688. if ( args.ArgC() == 3 )
  1689. {
  1690. CL_TakeJpeg( args[ 1 ], Q_atoi( args[2] ) );
  1691. }
  1692. else
  1693. {
  1694. CL_TakeJpeg( args[ 1 ], jpeg_quality.GetInt() );
  1695. }
  1696. }
  1697. else
  1698. {
  1699. CL_TakeJpeg( NULL, jpeg_quality.GetInt() );
  1700. }
  1701. }
  1702. static ConVar host_syncfps( "host_syncfps", "0", FCVAR_DEVELOPMENTONLY, "Synchronize real render time to host_framerate if possible." );
  1703. // [mhansen] Added a screen shot dump when the fps gets low
  1704. static ConVar fps_screenshot_threshold("fps_screenshot_threshold", "-1", FCVAR_CHEAT, "Dump a screenshot when the FPS drops below the given value.");
  1705. static ConVar fps_screenshot_frequency("fps_screenshot_frequency", "10", FCVAR_CHEAT, "While the fps is below the threshold we will dump a screen shot this often in seconds (i.e. 10 = screen shot every 10 seconds when under the given fps.)");
  1706. void CL_TakeSnapshotAndSwap()
  1707. {
  1708. bool bReadPixelsFromFrontBuffer = g_pMaterialSystemHardwareConfig->ReadPixelsFromFrontBuffer();
  1709. if ( bReadPixelsFromFrontBuffer )
  1710. {
  1711. Shader_SwapBuffers();
  1712. }
  1713. // [mhansen] Added a screen shot dump when the fps gets low
  1714. if ( fps_screenshot_threshold.GetInt() > 0 )
  1715. {
  1716. static float sLastRealTime = 0.0f;
  1717. float realFrameTime = realtime - sLastRealTime;
  1718. sLastRealTime = realtime;
  1719. static float timer = 0.0f;
  1720. float screenshotFrameTime = 1.0f / fps_screenshot_threshold.GetFloat();
  1721. if ( realFrameTime > screenshotFrameTime && realFrameTime < 10.0f )
  1722. {
  1723. if ( timer >= 0.0f )
  1724. {
  1725. // Figure out a good name
  1726. //Q_snprintf( cl_snapshotname, sizeof( cl_snapshotname ), "fps_%.2f", (1.0f / realFrameTime) );
  1727. cl_takesnapshot = true;
  1728. timer -= fps_screenshot_frequency.GetFloat();
  1729. }
  1730. }
  1731. if (timer < 0.0f)
  1732. {
  1733. timer += realFrameTime;
  1734. }
  1735. }
  1736. if (cl_takesnapshot)
  1737. {
  1738. bool bEnabled = materials->AllowThreading( false, g_nMaterialSystemThread );
  1739. char base[MAX_OSPATH];
  1740. char filename[MAX_OSPATH];
  1741. IClientEntity *world = entitylist->GetClientEntity( 0 );
  1742. g_pFileSystem->CreateDirHierarchy( "screenshots", "DEFAULT_WRITE_PATH" );
  1743. if ( cl_snapshot_fullpathname[0] )
  1744. {
  1745. char dir[MAX_OSPATH];
  1746. V_ExtractFilePath( cl_snapshot_fullpathname, dir, MAX_OSPATH );
  1747. g_pFileSystem->CreateDirHierarchy( dir );
  1748. }
  1749. if ( world && world->GetModel() )
  1750. {
  1751. Q_FileBase( modelloader->GetName( ( model_t *)world->GetModel() ), base, sizeof( base ) );
  1752. if ( PLATFORM_EXT[0] )
  1753. {
  1754. // map name has an additional extension
  1755. V_StripExtension( base, base, sizeof( base ) );
  1756. }
  1757. }
  1758. else
  1759. {
  1760. Q_strncpy( base, "Snapshot", sizeof( base ) );
  1761. }
  1762. char extension[MAX_OSPATH];
  1763. Q_snprintf( extension, sizeof( extension ), "%s.%s", GetPlatformExt(), cl_takejpeg ? "jpg" : "tga" );
  1764. // Using a subdir? If so, create it
  1765. if ( cl_snapshot_subdirname[0] )
  1766. {
  1767. Q_snprintf( filename, sizeof( filename ), "screenshots/%s/%s", base, cl_snapshot_subdirname );
  1768. g_pFileSystem->CreateDirHierarchy( filename, "DEFAULT_WRITE_PATH" );
  1769. }
  1770. if ( cl_snapshotname[0] )
  1771. {
  1772. #if defined( PLATFORM_X360 )
  1773. if ( ( tolower( cl_snapshotname[0] ) == 'd' ) && ( cl_snapshotname[1] == ':' ) && ( cl_snapshotname[2] == '\\' ) )
  1774. {
  1775. // Filename begins with "d:\" on X360, so assume the user knows what they're doing and has specified the full absolute filename.
  1776. V_strcpy( filename, cl_snapshotname );
  1777. remove( filename );
  1778. }
  1779. else
  1780. #endif
  1781. {
  1782. Q_strncpy( base, cl_snapshotname, sizeof( base ) );
  1783. Q_snprintf( filename, sizeof( filename ), "screenshots/%s%s", base, extension );
  1784. // See if the base file already exists and rename if so and if possible.
  1785. if( g_pFileSystem->GetFileTime( filename ) != 0 )
  1786. {
  1787. char renamedfile[MAX_OSPATH];
  1788. // Only loop until we hit 9999 (incremented to 10000 afterwards). More screen shots than that, out of luck.
  1789. for( int iNumber = 0; iNumber < 10000 ; iNumber++)
  1790. {
  1791. Q_snprintf( renamedfile, sizeof( renamedfile ), "screenshots/%s_%04d%s", base, iNumber, extension );
  1792. // GetFileTime() is used as a file exists check
  1793. if( !g_pFileSystem->GetFileTime( renamedfile ) )
  1794. {
  1795. g_pFileSystem->RenameFile(filename, renamedfile, "DEFAULT_WRITE_PATH");
  1796. break;
  1797. }
  1798. }
  1799. // Note: If we have 10000 files, just overwrite the base entry
  1800. }
  1801. }
  1802. cl_screenshotname.SetValue( "" );
  1803. }
  1804. else
  1805. {
  1806. while( 1 )
  1807. {
  1808. if ( cl_snapshot_subdirname[0] )
  1809. {
  1810. Q_snprintf( filename, sizeof( filename ), "screenshots/%s/%s/%s%04d%s", base, cl_snapshot_subdirname, base, cl_snapshotnum++, extension );
  1811. }
  1812. else
  1813. {
  1814. Q_snprintf( filename, sizeof( filename ), "screenshots/%s%04d%s", base, cl_snapshotnum++, extension );
  1815. }
  1816. if( !g_pFileSystem->GetFileTime( filename, "DEFAULT_WRITE_PATH" ) )
  1817. {
  1818. // woo hoo! The file doesn't exist already, so use it.
  1819. break;
  1820. }
  1821. }
  1822. }
  1823. if ( cl_snapshot_fullpathname[0] )
  1824. {
  1825. V_strncpy( filename, cl_snapshot_fullpathname, MAX_OSPATH );
  1826. cl_snapshot_fullpathname[0] = 0;
  1827. }
  1828. if ( cl_takejpeg )
  1829. {
  1830. videomode->TakeSnapshotJPEG( filename, cl_jpegquality );
  1831. g_ServerRemoteAccess.UploadScreenshot( filename );
  1832. }
  1833. else
  1834. {
  1835. videomode->TakeSnapshotTGA( filename );
  1836. }
  1837. cl_takesnapshot = false;
  1838. GetTestScriptMgr()->CheckPoint( "screenshot" );
  1839. materials->AllowThreading( bEnabled, g_nMaterialSystemThread );
  1840. }
  1841. // If recording movie and the console is totally up, then write out this frame to movie file.
  1842. if ( cl_movieinfo.IsRecording() && !Con_IsVisible() && !scr_drawloading )
  1843. {
  1844. videomode->WriteMovieFrame( cl_movieinfo );
  1845. ++cl_movieinfo.movieframe;
  1846. }
  1847. static double flLastTime = 0.0f;
  1848. bool bSyncFramerate = false;
  1849. if ( host_syncfps.GetBool() &&
  1850. host_framerate.GetFloat() != 0.0f )
  1851. {
  1852. bSyncFramerate = true;
  1853. }
  1854. if ( bSyncFramerate )
  1855. {
  1856. double curtime = Plat_FloatTime();
  1857. double dt = MIN( curtime - flLastTime, MAX_FRAMETIME );
  1858. double desiredInterval = 1.0 / fabsf( host_framerate.GetFloat() );
  1859. double flSleepInterval = desiredInterval - dt;
  1860. if ( flSleepInterval > 0.0f )
  1861. {
  1862. Sys_Sleep( (int)( flSleepInterval * 1000.0f ) );
  1863. }
  1864. }
  1865. if( !bReadPixelsFromFrontBuffer )
  1866. {
  1867. Shader_SwapBuffers();
  1868. }
  1869. if ( bSyncFramerate )
  1870. {
  1871. flLastTime = Plat_FloatTime();
  1872. }
  1873. // take a screenshot for savegames if necessary
  1874. saverestore->UpdateSaveGameScreenshots();
  1875. // take screenshot for bx movie maker
  1876. EngineTool_UpdateScreenshot();
  1877. }
  1878. static float s_flPreviousHostFramerate = 0;
  1879. void CL_StartMovie( const char *filename, int flags, int nWidth, int nHeight, float flFrameRate, int jpeg_quality )
  1880. {
  1881. Assert( g_hCurrentAVI == AVIHANDLE_INVALID );
  1882. // StartMove depends on host_framerate not being 0.
  1883. s_flPreviousHostFramerate = host_framerate.GetFloat();
  1884. host_framerate.SetValue( flFrameRate );
  1885. cl_movieinfo.Reset();
  1886. Q_strncpy( cl_movieinfo.moviename, filename, sizeof( cl_movieinfo.moviename ) );
  1887. cl_movieinfo.type = flags;
  1888. cl_movieinfo.jpeg_quality = jpeg_quality;
  1889. if ( cl_movieinfo.DoAVI() || cl_movieinfo.DoAVISound() )
  1890. {
  1891. // HACK: THIS MUST MATCH snd_device.h. Should be exposed more cleanly!!!
  1892. #define SOUND_DMA_SPEED 44100 // hardware playback rate
  1893. AVIParams_t params;
  1894. Q_strncpy( params.m_pFileName, filename, sizeof( params.m_pFileName ) );
  1895. Q_strncpy( params.m_pPathID, "MOD", sizeof( params.m_pPathID ) );
  1896. params.m_nNumChannels = 2;
  1897. params.m_nSampleBits = 16;
  1898. params.m_nSampleRate = SOUND_DMA_SPEED;
  1899. params.m_nWidth = nWidth;
  1900. params.m_nHeight = nHeight;
  1901. if ( IsIntegralValue( flFrameRate ) )
  1902. {
  1903. params.m_nFrameRate = RoundFloatToInt( flFrameRate );
  1904. params.m_nFrameScale = 1;
  1905. }
  1906. else if ( IsIntegralValue( flFrameRate * 1001.0f / 1000.0f ) ) // 1001 is the ntsc divisor (30*1000/1001 = 29.97, etc)
  1907. {
  1908. params.m_nFrameRate = RoundFloatToInt( flFrameRate * 1001 );
  1909. params.m_nFrameScale = 1001;
  1910. }
  1911. else
  1912. {
  1913. // arbitrarily choosing 1000 as the divisor
  1914. params.m_nFrameRate = RoundFloatToInt( flFrameRate * 1000 );
  1915. params.m_nFrameScale = 1000;
  1916. }
  1917. g_hCurrentAVI = avi->StartAVI( params );
  1918. }
  1919. SND_MovieStart();
  1920. }
  1921. void CL_EndMovie()
  1922. {
  1923. if ( !CL_IsRecordingMovie() )
  1924. return;
  1925. host_framerate.SetValue( s_flPreviousHostFramerate );
  1926. s_flPreviousHostFramerate = 0.0f;
  1927. SND_MovieEnd();
  1928. if ( cl_movieinfo.DoAVI() || cl_movieinfo.DoAVISound() )
  1929. {
  1930. avi->FinishAVI( g_hCurrentAVI );
  1931. g_hCurrentAVI = AVIHANDLE_INVALID;
  1932. }
  1933. cl_movieinfo.Reset();
  1934. }
  1935. bool CL_IsRecordingMovie()
  1936. {
  1937. return cl_movieinfo.IsRecording();
  1938. }
  1939. /*
  1940. ===============
  1941. CL_StartMovie_f
  1942. Sets the engine up to dump frames
  1943. ===============
  1944. */
  1945. CON_COMMAND_F( startmovie, "Start recording movie frames.", FCVAR_DONTRECORD )
  1946. {
  1947. if( args.ArgC() < 2 )
  1948. {
  1949. ConMsg( "startmovie <filename>\n [\n" );
  1950. ConMsg( " (default = TGAs + .wav file)\n" );
  1951. ConMsg( " avi = AVI + AVISOUND\n" );
  1952. ConMsg( " raw = TGAs + .wav file, same as default\n" );
  1953. ConMsg( " tga = TGAs\n" );
  1954. ConMsg( " jpg/jpeg = JPegs\n" );
  1955. ConMsg( " wav = Write .wav audio file\n" );
  1956. ConMsg( " jpeg_quality nnn = set jpeq quality to nnn (range 1 to 100), default %d\n", DEFAULT_JPEG_QUALITY );
  1957. ConMsg( " ]\n" );
  1958. ConMsg( "e.g.: startmovie testmovie jpg wav jpeg_qality 75\n" );
  1959. ConMsg( "Using AVI will bring up a dialog for choosing the codec, which may not show if you are running the engine in fullscreen mode!\n" );
  1960. return;
  1961. }
  1962. if ( CL_IsRecordingMovie() )
  1963. {
  1964. ConMsg( "Already recording movie!\n" );
  1965. return;
  1966. }
  1967. int flags = MovieInfo_t::FMOVIE_TGA | MovieInfo_t::FMOVIE_WAV;
  1968. int jpeg_quality = DEFAULT_JPEG_QUALITY;
  1969. if ( args.ArgC() > 2 )
  1970. {
  1971. flags = 0;
  1972. for ( int i = 2; i < args.ArgC(); ++i )
  1973. {
  1974. if ( !Q_stricmp( args[ i ], "avi" ) )
  1975. {
  1976. flags |= MovieInfo_t::FMOVIE_AVI | MovieInfo_t::FMOVIE_AVISOUND;
  1977. }
  1978. if ( !Q_stricmp( args[ i ], "raw" ) )
  1979. {
  1980. flags |= MovieInfo_t::FMOVIE_TGA | MovieInfo_t::FMOVIE_WAV;
  1981. }
  1982. if ( !Q_stricmp( args[ i ], "tga" ) )
  1983. {
  1984. flags |= MovieInfo_t::FMOVIE_TGA;
  1985. }
  1986. if ( !Q_stricmp( args[ i ], "jpeg" ) || !Q_stricmp( args[ i ], "jpg" ) )
  1987. {
  1988. flags &= ~MovieInfo_t::FMOVIE_TGA;
  1989. flags |= MovieInfo_t::FMOVIE_JPG;
  1990. }
  1991. if ( !Q_stricmp( args[ i ], "jpeg_quality" ) )
  1992. {
  1993. jpeg_quality = clamp( Q_atoi( args[ ++i ] ), 1, 100 );
  1994. }
  1995. if ( !Q_stricmp( args[ i ], "wav" ) )
  1996. {
  1997. flags |= MovieInfo_t::FMOVIE_WAV;
  1998. }
  1999. }
  2000. }
  2001. if ( flags == 0 )
  2002. {
  2003. Warning( "Missing or unknown recording types, must specify one or both of 'avi' or 'raw'\n" );
  2004. return;
  2005. }
  2006. float flFrameRate = host_framerate.GetFloat();
  2007. if ( flFrameRate == 0.0f )
  2008. {
  2009. flFrameRate = 30.0f;
  2010. }
  2011. CL_StartMovie( args[ 1 ], flags, videomode->GetModeWidth(), videomode->GetModeHeight(), flFrameRate, jpeg_quality );
  2012. ConMsg( "Started recording movie, frames will record after console is cleared...\n" );
  2013. }
  2014. //-----------------------------------------------------------------------------
  2015. // Ends frame dumping
  2016. //-----------------------------------------------------------------------------
  2017. CON_COMMAND_F( endmovie, "Stop recording movie frames.", FCVAR_DONTRECORD )
  2018. {
  2019. if( !CL_IsRecordingMovie() )
  2020. {
  2021. ConMsg( "No movie started.\n" );
  2022. }
  2023. else
  2024. {
  2025. CL_EndMovie();
  2026. ConMsg( "Stopped recording movie...\n" );
  2027. }
  2028. }
  2029. /*
  2030. =====================
  2031. CL_Rcon_f
  2032. Send the rest of the command line over as
  2033. an unconnected command.
  2034. =====================
  2035. */
  2036. CON_COMMAND_F( rcon, "Issue an rcon command.", FCVAR_DONTRECORD )
  2037. {
  2038. char message[1024]; // Command message
  2039. char szParam[ 256 ];
  2040. message[0] = 0;
  2041. for (int i=1 ; i<args.ArgC() ; i++)
  2042. {
  2043. const char *pParam = args[i];
  2044. // put quotes around empty arguments so we can pass things like this: rcon sv_password ""
  2045. // otherwise the "" on the end is lost
  2046. if ( strchr( pParam, ' ' ) || ( Q_strlen( pParam ) == 0 ) )
  2047. {
  2048. Q_snprintf( szParam, sizeof( szParam ), "\"%s\"", pParam );
  2049. Q_strncat( message, szParam, sizeof( message ), COPY_ALL_CHARACTERS );
  2050. }
  2051. else
  2052. {
  2053. Q_strncat( message, pParam, sizeof( message ), COPY_ALL_CHARACTERS );
  2054. }
  2055. if ( i != ( args.ArgC() - 1 ) )
  2056. {
  2057. Q_strncat (message, " ", sizeof( message ), COPY_ALL_CHARACTERS);
  2058. }
  2059. }
  2060. RCONClient().SendCmd( message );
  2061. }
  2062. CON_COMMAND_F( box, "Draw a debug box.", FCVAR_CHEAT )
  2063. {
  2064. if( args.ArgC() != 7 )
  2065. {
  2066. ConMsg ("box x1 y1 z1 x2 y2 z2\n");
  2067. return;
  2068. }
  2069. Vector mins, maxs;
  2070. for (int i = 0; i < 3; ++i)
  2071. {
  2072. mins[i] = atof(args[i + 1]);
  2073. maxs[i] = atof(args[i + 4]);
  2074. }
  2075. CDebugOverlay::AddBoxOverlay( vec3_origin, mins, maxs, vec3_angle, 255, 0, 0, 0, 100 );
  2076. }
  2077. /*
  2078. ==============
  2079. CL_View_f
  2080. Debugging changes the view entity to the specified index
  2081. ===============
  2082. */
  2083. CON_COMMAND_F( cl_view, "Set the view entity index.", FCVAR_CHEAT )
  2084. {
  2085. int nNewView;
  2086. if( args.ArgC() != 2 )
  2087. {
  2088. ConMsg ("cl_view entity#\nCurrent %i\n", GetLocalClient().GetViewEntity() );
  2089. return;
  2090. }
  2091. // Only valid in single player!!!
  2092. if ( GetBaseLocalClient().m_nMaxClients > 1 )
  2093. return;
  2094. nNewView = atoi( args[1] );
  2095. if (!nNewView)
  2096. return;
  2097. if ( nNewView > entitylist->GetHighestEntityIndex() )
  2098. return;
  2099. GetLocalClient().m_nViewEntity = nNewView;
  2100. videomode->MarkClientViewRectDirty(); // Force recalculation
  2101. ConMsg("View entity set to %i\n", nNewView);
  2102. }
  2103. bool IsLowPriorityLight( int key )
  2104. {
  2105. return ( key >= LIGHT_INDEX_LOW_PRIORITY );
  2106. }
  2107. static int CL_AllocLightFromArray( dlight_t *pLights, int lightCount, int key )
  2108. {
  2109. int i;
  2110. // first look for an exact key match
  2111. if (key)
  2112. {
  2113. for ( i = 0; i < lightCount; i++ )
  2114. {
  2115. if (pLights[i].key == key)
  2116. return i;
  2117. }
  2118. }
  2119. // then look for anything else
  2120. for ( i = 0; i < lightCount; i++ )
  2121. {
  2122. if (pLights[i].die < GetBaseLocalClient().GetTime())
  2123. return i;
  2124. }
  2125. if ( cl_retire_low_priority_lights.GetBool() && !IsLowPriorityLight( key ) )
  2126. {
  2127. // find the smallest radius low priority light
  2128. int iSmallest = -1;
  2129. float fSmallestRadius = 0;
  2130. for ( i = 1; i < lightCount; i++ ) // skip light zero since it's the default overflow one
  2131. {
  2132. if ( IsLowPriorityLight( pLights[i].key ) )
  2133. {
  2134. if ( fSmallestRadius == 0 || pLights[i].radius < fSmallestRadius )
  2135. {
  2136. fSmallestRadius = pLights[i].radius;
  2137. iSmallest = i;
  2138. }
  2139. }
  2140. }
  2141. if (iSmallest != -1)
  2142. {
  2143. return iSmallest;
  2144. }
  2145. }
  2146. return 0;
  2147. }
  2148. bool g_bActiveDlights = false;
  2149. bool g_bActiveElights = false;
  2150. /*
  2151. ===============
  2152. CL_AllocDlight
  2153. ===============
  2154. */
  2155. dlight_t *CL_AllocDlight (int key)
  2156. {
  2157. int i = CL_AllocLightFromArray( cl_dlights, MAX_DLIGHTS, key );
  2158. dlight_t *dl = &cl_dlights[i];
  2159. R_MarkDLightNotVisible( i );
  2160. memset (dl, 0, sizeof(*dl));
  2161. dl->key = key;
  2162. r_dlightchanged |= (1 << i);
  2163. r_dlightactive |= (1 << i);
  2164. g_bActiveDlights = true;
  2165. return dl;
  2166. }
  2167. /*
  2168. ===============
  2169. CL_AllocElight
  2170. ===============
  2171. */
  2172. dlight_t *CL_AllocElight (int key)
  2173. {
  2174. int i = CL_AllocLightFromArray( cl_elights, MAX_ELIGHTS, key );
  2175. dlight_t *el = &cl_elights[i];
  2176. memset (el, 0, sizeof(*el));
  2177. el->key = key;
  2178. g_bActiveElights = true;
  2179. return el;
  2180. }
  2181. /*
  2182. ===============
  2183. CL_DecayLights
  2184. ===============
  2185. */
  2186. void CL_DecayLights (void)
  2187. {
  2188. float time;
  2189. time = GetBaseLocalClient().GetFrameTime();
  2190. if ( time <= 0.0f )
  2191. return;
  2192. CL_UpdateDAndELights( true );
  2193. }
  2194. void CL_UpdateDAndELights( bool bUpdateDecay )
  2195. {
  2196. int i;
  2197. dlight_t *dl;
  2198. float time;
  2199. time = GetBaseLocalClient().GetFrameTime();
  2200. g_bActiveDlights = false;
  2201. g_bActiveElights = false;
  2202. dl = cl_dlights;
  2203. r_dlightchanged = 0;
  2204. r_dlightactive = 0;
  2205. g_nNumActiveDLights = 0;
  2206. for ( i=0 ; i<MAX_DLIGHTS ; i++, dl++ )
  2207. {
  2208. if ( !dl->IsRadiusGreaterThanZero() )
  2209. {
  2210. R_MarkDLightNotVisible( i );
  2211. continue;
  2212. }
  2213. if ( dl->die < GetBaseLocalClient().GetTime() )
  2214. {
  2215. r_dlightchanged |= (1 << i);
  2216. dl->radius = 0;
  2217. }
  2218. else if ( dl->decay && bUpdateDecay )
  2219. {
  2220. r_dlightchanged |= (1 << i);
  2221. dl->radius -= time*dl->decay;
  2222. if ( dl->radius < 0 )
  2223. {
  2224. dl->radius = 0;
  2225. }
  2226. }
  2227. if ( dl->IsRadiusGreaterThanZero() )
  2228. {
  2229. g_bActiveDlights = true;
  2230. r_dlightactive |= (1 << i);
  2231. g_ActiveDLightIndex[g_nNumActiveDLights] = i;
  2232. g_nNumActiveDLights++;
  2233. }
  2234. else
  2235. {
  2236. R_MarkDLightNotVisible( i );
  2237. }
  2238. }
  2239. g_nNumActiveELights = 0;
  2240. dl = cl_elights;
  2241. for ( i=0 ; i<MAX_ELIGHTS ; i++, dl++ )
  2242. {
  2243. if ( !dl->IsRadiusGreaterThanZero() )
  2244. continue;
  2245. if ( dl->die < GetBaseLocalClient().GetTime() )
  2246. {
  2247. dl->radius = 0;
  2248. continue;
  2249. }
  2250. if ( bUpdateDecay )
  2251. {
  2252. dl->radius -= time*dl->decay;
  2253. }
  2254. if ( dl->radius < 0 )
  2255. {
  2256. dl->radius = 0;
  2257. }
  2258. if ( dl->IsRadiusGreaterThanZero() )
  2259. {
  2260. g_bActiveElights = true;
  2261. g_ActiveELightIndex[g_nNumActiveELights] = i;
  2262. g_nNumActiveELights++;
  2263. }
  2264. }
  2265. }
  2266. void CL_ExtraMouseUpdate( float frametime )
  2267. {
  2268. if ( !Host_ShouldRun() )
  2269. return;
  2270. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  2271. {
  2272. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  2273. // Not ready for commands yet.
  2274. if ( !GetLocalClient().IsActive() )
  2275. continue;
  2276. // Don't create usercmds here during playback, they were encoded into the packet already
  2277. if ( demoplayer->IsPlayingBack() && !GetLocalClient().ishltv
  2278. # ifdef REPLAY_ENABLED
  2279. && !GetLocalClient().isreplay
  2280. # endif
  2281. )
  2282. continue;
  2283. // Have client .dll create and store usercmd structure
  2284. g_ClientDLL->ExtraMouseSample( frametime,
  2285. !GetLocalClient().m_bPaused
  2286. );
  2287. }
  2288. }
  2289. /*
  2290. =================
  2291. CL_SendMove
  2292. Constructs the movement command and sends it to the server if it's time.
  2293. =================
  2294. */
  2295. void CL_SendMove( void )
  2296. {
  2297. int nextcommandnr = GetBaseLocalClient().lastoutgoingcommand + GetBaseLocalClient().chokedcommands + 1;
  2298. int nChokedCommands = GetBaseLocalClient().chokedcommands;
  2299. // send the client update packet
  2300. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  2301. {
  2302. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  2303. if ( splitscreen->IsDisconnecting( i ) )
  2304. continue;
  2305. bf_write DataOut;
  2306. byte data[ MAX_CMD_BUFFER ];
  2307. CCLCMsg_Move_t moveMsg;
  2308. DataOut.StartWriting( data, sizeof( data ) );
  2309. // Determine number of backup commands to send along
  2310. int cl_cmdbackup = 2;
  2311. int nBackupCommands = clamp( cl_cmdbackup, 0, MAX_BACKUP_COMMANDS );
  2312. // How many real new commands have queued up
  2313. int nNewCommands = clamp( nChokedCommands + 1, 0, MAX_NEW_COMMANDS );
  2314. moveMsg.set_num_backup_commands( nBackupCommands );
  2315. moveMsg.set_num_new_commands( nNewCommands );
  2316. int numcmds = nNewCommands + nBackupCommands;
  2317. int from = -1; // first command is deltaed against zeros
  2318. bool bOK = true;
  2319. for ( int to = nextcommandnr - numcmds + 1; to <= nextcommandnr; ++to )
  2320. {
  2321. bool isnewcmd = to >= (nextcommandnr - nNewCommands + 1);
  2322. // first valid command number is 1
  2323. bOK = bOK && g_ClientDLL->WriteUsercmdDeltaToBuffer( i, &DataOut, from, to, isnewcmd );
  2324. from = to;
  2325. }
  2326. if ( bOK )
  2327. {
  2328. moveMsg.set_data( ( const char * )DataOut.GetData(), DataOut.GetNumBytesWritten() );
  2329. // only write message if all CUserCmds were written correctly, otherwise parsing would fail
  2330. GetLocalClient().m_NetChannel->SendNetMsg( moveMsg );
  2331. }
  2332. }
  2333. }
  2334. void CL_Move(float accumulated_extra_samples, bool bFinalTick )
  2335. {
  2336. CClientState &cl = GetBaseLocalClient();
  2337. if ( !cl.IsConnected() )
  2338. return;
  2339. if ( !Host_ShouldRun() )
  2340. return;
  2341. // only send packets on the final tick in one engine frame
  2342. bool bSendPacket = true;
  2343. // Don't create usercmds here during playback, they were encoded into the packet already
  2344. if ( demoplayer->IsPlayingBack() )
  2345. {
  2346. if ( cl.ishltv
  2347. # ifdef REPLAY_ENABLED
  2348. || cl.isreplay
  2349. # endif // ifdef REPLAY_ENABLED
  2350. )
  2351. {
  2352. // still do it when playing back a HLTV/replay demo
  2353. bSendPacket = false;
  2354. }
  2355. else
  2356. {
  2357. return;
  2358. }
  2359. }
  2360. // don't send packets if update time not reached or chnnel still sending
  2361. // in loopback mode don't send only if host_limitlocal is enabled
  2362. if ( ( !cl.m_NetChannel->IsLoopback() || host_limitlocal.GetInt() ) &&
  2363. ( ( net_time < cl.m_flNextCmdTime ) || !cl.m_NetChannel->CanPacket() || !bFinalTick ) )
  2364. {
  2365. bSendPacket = false;
  2366. }
  2367. if ( cl.IsActive() )
  2368. {
  2369. VPROF( "CL_Move" );
  2370. int nextcommandnr = cl.lastoutgoingcommand + cl.chokedcommands + 1;
  2371. // Have client .dll create and store CUserCmd structure(s)
  2372. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  2373. {
  2374. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  2375. if ( splitscreen->IsDisconnecting( i ) )
  2376. continue;
  2377. g_ClientDLL->CreateMove(
  2378. nextcommandnr,
  2379. host_state.interval_per_tick - accumulated_extra_samples,
  2380. !cl.IsPaused()
  2381. );
  2382. // Store new usercmd to dem file
  2383. if ( demorecorder->IsRecording() )
  2384. {
  2385. // Back up one because we've incremented outgoing_sequence each frame by 1 unit
  2386. demorecorder->RecordUserInput( nextcommandnr );
  2387. }
  2388. }
  2389. if ( bSendPacket )
  2390. {
  2391. CL_SendMove();
  2392. }
  2393. else
  2394. {
  2395. // netchannel will increase internal outgoing sequence number too
  2396. cl.m_NetChannel->SetChoked();
  2397. // Mark command as held back so we'll send it next time
  2398. cl.chokedcommands++;
  2399. }
  2400. }
  2401. if ( !bSendPacket )
  2402. return;
  2403. // Request non delta compression if high packet loss, show warning message
  2404. bool hasProblem = cl.m_NetChannel->IsTimingOut() && !demoplayer->IsPlayingBack() && cl.IsActive();
  2405. // check timeout, but not if running _DEBUG engine
  2406. bool bAllowTimeout = true;
  2407. #if defined( _DEBUG )
  2408. bAllowTimeout = false;
  2409. #endif
  2410. //
  2411. // See sfhud_autodisconnect.cpp for parsing in:
  2412. // void SFHudAutodisconnect::ProcessInput( void )
  2413. //
  2414. // Request non delta compressed update if high packet loss
  2415. // show warning message/UI
  2416. if ( hasProblem )
  2417. {
  2418. #if !defined( CSTRIKE15 )
  2419. con_nprint_t np;
  2420. np.time_to_live = 1.0;
  2421. np.index = 2;
  2422. np.fixed_width_font = false;
  2423. np.color[ 0 ] = 1.0;
  2424. np.color[ 1 ] = 0.2;
  2425. np.color[ 2 ] = 0.2;
  2426. Con_NXPrintf( &np, "WARNING: Connection Problem" );
  2427. #endif
  2428. if ( bAllowTimeout )
  2429. {
  2430. float flTimeOut = cl.m_NetChannel->GetTimeoutSeconds();
  2431. Assert( flTimeOut != -1.0f );
  2432. float flRemainingTime = MAX( flTimeOut - cl.m_NetChannel->GetTimeSinceLastReceived(), 0.0f );
  2433. // write time until connection is dropped to a convar
  2434. cl_connection_trouble_info.SetValue( CFmtStr( "disconnect(%0.3f)", flRemainingTime ) );
  2435. #if !defined( CSTRIKE15 )
  2436. np.index = 3;
  2437. Con_NXPrintf( &np, "Auto-disconnect in %.1f seconds", flRemainingTime );
  2438. #endif
  2439. EngineVGui()->NeedConnectionProblemWaitScreen();
  2440. }
  2441. // sets m_nDeltaTick to -1
  2442. cl.ForceFullUpdate( "connection problem" );
  2443. }
  2444. else
  2445. {
  2446. // We are no longer timing out
  2447. float flLastTransientProblemTime = 0.0f;
  2448. if ( cl_connection_trouble_info.GetString()[0] == '@' )
  2449. flLastTransientProblemTime = V_atof( cl_connection_trouble_info.GetString() + 1 );
  2450. float flTimeNow = Plat_FloatTime(); // See if sufficient time elapsed since we started showing a problem info (don't change the error on user too quickly)
  2451. if ( ( flLastTransientProblemTime < flTimeNow - 0.5f ) || ( flLastTransientProblemTime > flTimeNow + 0.5f ) )
  2452. {
  2453. // Are we experiencing packet loss? (router dropping packets, need to slow down!)
  2454. float const flPacketLoss = cl.m_NetChannel->GetAvgLoss( FLOW_INCOMING );
  2455. // ... or are we choking (insufficient bandwidth, get better internet!)
  2456. float const flPacketChoke = cl.m_NetChannel->GetAvgChoke( FLOW_INCOMING );
  2457. if ( flPacketLoss > 0 )
  2458. cl_connection_trouble_info.SetValue( CFmtStr( "@%.03f:loss(%.05f)", flTimeNow, flPacketLoss ) );
  2459. else if ( flPacketChoke > 0 )
  2460. cl_connection_trouble_info.SetValue( CFmtStr( "@%.03f:choke(%.05f)", flTimeNow, flPacketChoke ) );
  2461. else if ( cl_connection_trouble_info.GetString()[ 0 ] )
  2462. cl_connection_trouble_info.SetValue( "" );
  2463. }
  2464. }
  2465. if ( cl.IsActive() )
  2466. {
  2467. int tick = cl.m_nDeltaTick;
  2468. CNETMsg_Tick_t mymsg( tick, host_frameendtime_computationduration, host_frametime_stddeviation, host_framestarttime_stddeviation );
  2469. if ( cl.GetHltvReplayDelay() )
  2470. {
  2471. mymsg.set_hltv_replay_flags( 1 ); // signal that this ack is from hltv replay
  2472. }
  2473. cl.m_NetChannel->SendNetMsg( mymsg );
  2474. }
  2475. //COM_Log( "cl.log", "Sending command number %i(%i) to server\n", cl.m_NetChan->m_nOutSequenceNr, GetLocalClient().m_NetChan->m_nOutSequenceNr & CL_UPDATE_MASK );
  2476. // Remember outgoing command that we are sending
  2477. cl.lastoutgoingcommand = cl.m_NetChannel->SendDatagram( NULL );
  2478. cl.chokedcommands = 0;
  2479. // calc next packet send time
  2480. if ( cl.IsActive() )
  2481. {
  2482. // use full update rate when active
  2483. float commandInterval = 1.0f / cl_cmdrate->GetFloat();
  2484. float maxDelta = MIN( host_state.interval_per_tick, commandInterval );
  2485. float delta = clamp( net_time - cl.m_flNextCmdTime, 0.0f, maxDelta );
  2486. cl.m_flNextCmdTime = net_time + commandInterval - delta;
  2487. }
  2488. else
  2489. {
  2490. // during signon process send only 5 packets/second
  2491. cl.m_flNextCmdTime = net_time + ( 1.0f / 5.0f );
  2492. }
  2493. }
  2494. #define TICK_INTERVAL (host_state.interval_per_tick)
  2495. #define ROUND_TO_TICKS( t ) ( TICK_INTERVAL * TIME_TO_TICKS( t ) )
  2496. void CL_LatchInterpolationAmount()
  2497. {
  2498. CClientState &cl = GetBaseLocalClient();
  2499. if ( !cl.IsConnected() )
  2500. return;
  2501. float dt = cl.m_NetChannel->GetTimeSinceLastReceived();
  2502. float flClientInterpolationAmount = ROUND_TO_TICKS( cl.GetClientInterpAmount() );
  2503. float flInterp = 0.0f;
  2504. if ( flClientInterpolationAmount > 0.001 )
  2505. {
  2506. flInterp = clamp( dt / flClientInterpolationAmount, 0.0f, 3.0f );
  2507. }
  2508. cl.m_NetChannel->SetInterpolationAmount( flInterp );
  2509. }
  2510. //-----------------------------------------------------------------------------
  2511. // Purpose:
  2512. // Input : *pMessage -
  2513. //-----------------------------------------------------------------------------
  2514. void CL_HudMessage( const char *pMessage )
  2515. {
  2516. if ( g_ClientDLL )
  2517. {
  2518. g_ClientDLL->HudText( pMessage );
  2519. }
  2520. }
  2521. CON_COMMAND_F( cl_showents, "Dump entity list to console.", FCVAR_CHEAT )
  2522. {
  2523. for ( int i = 0; i < entitylist->GetMaxEntities(); i++ )
  2524. {
  2525. char entStr[256], classStr[256];
  2526. IClientNetworkable *pEnt;
  2527. if((pEnt = entitylist->GetClientNetworkable(i)) != NULL)
  2528. {
  2529. entStr[0] = 0;
  2530. Q_snprintf(classStr, sizeof( classStr ), "'%s'", pEnt->GetClientClass()->m_pNetworkName);
  2531. }
  2532. else
  2533. {
  2534. Q_snprintf(entStr, sizeof( entStr ), "(missing), ");
  2535. Q_snprintf(classStr, sizeof( classStr ), "(missing)");
  2536. }
  2537. if ( pEnt )
  2538. ConMsg("Ent %3d: %s class %s\n", i, entStr, classStr);
  2539. }
  2540. }
  2541. //-----------------------------------------------------------------------------
  2542. // Purpose: returns true if the background level should be loaded on startup
  2543. //-----------------------------------------------------------------------------
  2544. bool CL_ShouldLoadBackgroundLevel( const CCommand &args )
  2545. {
  2546. // portal2 is not using a background map
  2547. return false;
  2548. if ( CommandLine()->CheckParm( "-nostartupmenu" ) )
  2549. return false;
  2550. if ( CommandLine()->CheckParm("-makereslists") )
  2551. return false;
  2552. if ( InEditMode() )
  2553. return false;
  2554. if ( args.ArgC() == 2 )
  2555. {
  2556. // presence of args identifies an end-of-game situation
  2557. if ( IsGameConsole() )
  2558. {
  2559. // Console needs to get UI in the correct state to transition to the Background level
  2560. // from the credits.
  2561. return true;
  2562. }
  2563. if ( !Q_stricmp( args[1], "force" ) )
  2564. {
  2565. // Adrian: Have to do this so the menu shows up if we ever call this while in a level.
  2566. Host_Disconnect( true );
  2567. // pc can't get into background maps fast enough, so just show main menu
  2568. return false;
  2569. }
  2570. if ( !Q_stricmp( args[1], "playendgamevid" ) )
  2571. {
  2572. // Bail back to the menu and play the end game video.
  2573. CommandLine()->AppendParm( "-endgamevid", NULL );
  2574. CommandLine()->RemoveParm( "-recapvid" );
  2575. HostState_Restart();
  2576. return false;
  2577. }
  2578. if ( !Q_stricmp( args[1], "playrecapvid" ) )
  2579. {
  2580. // Bail back to the menu and play the recap video
  2581. CommandLine()->AppendParm( "-recapvid", NULL );
  2582. CommandLine()->RemoveParm( "-endgamevid" );
  2583. HostState_Restart();
  2584. return false;
  2585. }
  2586. }
  2587. // don't load the map if we're going straight into a level
  2588. if ( CommandLine()->CheckParm("+map") ||
  2589. CommandLine()->CheckParm("+connect") ||
  2590. CommandLine()->CheckParm("+playdemo") ||
  2591. CommandLine()->CheckParm("+timedemo") ||
  2592. CommandLine()->CheckParm("+timedemoquit") ||
  2593. CommandLine()->CheckParm("+load") )
  2594. return false;
  2595. // nothing else is going on, so load the startup level
  2596. return true;
  2597. }
  2598. #define DEFAULT_BACKGROUND_NAME "background01"
  2599. int CL_GetBackgroundLevelIndex( int nNumChapters )
  2600. {
  2601. int iChapterIndex = sv_unlockedchapters.GetInt();
  2602. iChapterIndex = MIN( iChapterIndex, nNumChapters );
  2603. if ( iChapterIndex <= 0 )
  2604. {
  2605. // expected to be [1..N]
  2606. iChapterIndex = 1;
  2607. }
  2608. return iChapterIndex;
  2609. }
  2610. //-----------------------------------------------------------------------------
  2611. // Purpose: returns the name of the background level to load
  2612. //-----------------------------------------------------------------------------
  2613. void CL_GetBackgroundLevelName( char *pszBackgroundName, int bufSize, bool bMapName )
  2614. {
  2615. Q_strncpy( pszBackgroundName, DEFAULT_BACKGROUND_NAME, bufSize );
  2616. KeyValues *pChapterFile = new KeyValues( pszBackgroundName );
  2617. if ( pChapterFile->LoadFromFile( g_pFileSystem, "scripts/ChapterBackgrounds.txt" ) )
  2618. {
  2619. KeyValues *pChapterRoot = pChapterFile;
  2620. const char *szChapterIndex;
  2621. int nNumChapters = 1;
  2622. KeyValues *pChapters = pChapterFile->GetNextKey();
  2623. if ( bMapName && pChapters )
  2624. {
  2625. const char *pszName = pChapters->GetName();
  2626. if ( pszName && pszName[0] && StringHasPrefix( pszName, "BackgroundMaps" ) )
  2627. {
  2628. pChapterRoot = pChapters;
  2629. pChapters = pChapters->GetFirstSubKey();
  2630. }
  2631. else
  2632. {
  2633. pChapters = NULL;
  2634. }
  2635. }
  2636. else
  2637. {
  2638. pChapters = NULL;
  2639. }
  2640. if ( !pChapters )
  2641. {
  2642. pChapters = pChapterFile->GetFirstSubKey();
  2643. }
  2644. // Find the highest indexed chapter
  2645. while ( pChapters )
  2646. {
  2647. szChapterIndex = pChapters->GetName();
  2648. if ( szChapterIndex )
  2649. {
  2650. int nChapter = atoi(szChapterIndex);
  2651. if( nChapter > nNumChapters )
  2652. nNumChapters = nChapter;
  2653. }
  2654. pChapters = pChapters->GetNextKey();
  2655. }
  2656. int nChapterToLoad = CL_GetBackgroundLevelIndex( nNumChapters );
  2657. // Find the chapter background with this index
  2658. char buf[4];
  2659. Q_snprintf( buf, sizeof(buf), "%d", nChapterToLoad );
  2660. KeyValues *pLoadChapter = pChapterRoot->FindKey(buf);
  2661. // Copy the background name
  2662. if ( pLoadChapter )
  2663. {
  2664. Q_strncpy( pszBackgroundName, pLoadChapter->GetString(), bufSize );
  2665. }
  2666. }
  2667. pChapterFile->deleteThis();
  2668. }
  2669. //-----------------------------------------------------------------------------
  2670. // A random number at startup (sequential for 360), drives the product
  2671. // startup screen choice.
  2672. //-----------------------------------------------------------------------------
  2673. #define NUM_STARTUP_IMAGES 2
  2674. unsigned int CL_GetStartupIndex()
  2675. {
  2676. static unsigned int nWhich = 0;
  2677. if ( !nWhich )
  2678. {
  2679. // once set, stays set to the same value for this entire session
  2680. int nOverride = CommandLine()->ParmValue( "-startup", 0 );
  2681. if ( nOverride > 0 )
  2682. {
  2683. nWhich = clamp( nOverride, 1, NUM_STARTUP_IMAGES );
  2684. }
  2685. else
  2686. {
  2687. nWhich = Plat_GetClockStart();
  2688. nWhich = ( nWhich % NUM_STARTUP_IMAGES ) + 1;
  2689. }
  2690. }
  2691. return nWhich;
  2692. }
  2693. //-----------------------------------------------------------------------------
  2694. // Isolated startup from menu backgrounds, which are not used for startup but
  2695. // may be used for different cases. Needs to be the same for everybody, isolated here.
  2696. //-----------------------------------------------------------------------------
  2697. void CL_GetStartupImage( char *pOutBuffer, int nOutBufferSize )
  2698. {
  2699. #if defined( CSTRIKE15)
  2700. // CStrike15 uses a specific startup image instead of the random image.
  2701. // CSGO always uses a widescreen format image, regardless of the screen resolution,
  2702. // to match how the Scaleform background is drawn. CVideoMode_Common::DrawStartupGraphic
  2703. // takes care of repositioning and scaling this image to match the method
  2704. // used in Scaleform.
  2705. V_strncpy( pOutBuffer, "console/background01_widescreen", nOutBufferSize );
  2706. #else
  2707. const AspectRatioInfo_t &aspectRatioInfo = materials->GetAspectRatioInfo();
  2708. int nWhich = CL_GetStartupIndex();
  2709. V_snprintf( pOutBuffer, nOutBufferSize, "console/portal2_product_%d%s", nWhich, ( aspectRatioInfo.m_bIsWidescreen ? "_widescreen" : "" ) );
  2710. #endif // CSTRIKE15
  2711. }
  2712. //-----------------------------------------------------------------------------
  2713. // Purpose: Callback to open the game menus
  2714. //-----------------------------------------------------------------------------
  2715. void CL_CheckToDisplayStartupMenus( const CCommand &args )
  2716. {
  2717. if ( CL_ShouldLoadBackgroundLevel( args ) )
  2718. {
  2719. char szBackgroundName[256];
  2720. CL_GetBackgroundLevelName( szBackgroundName, sizeof(szBackgroundName), true );
  2721. char cmd[256];
  2722. Q_snprintf( cmd, sizeof(cmd), "map_background %s\n", szBackgroundName );
  2723. Cbuf_AddText( Cbuf_GetCurrentPlayer(), cmd );
  2724. }
  2725. }
  2726. static float s_fDemoRevealGameUITime = -1;
  2727. float s_fDemoPlayMusicTime = -1;
  2728. static bool s_bIsRavenHolmn = false;
  2729. //-----------------------------------------------------------------------------
  2730. // Purpose: run the special demo logic when transitioning from the trainstation levels
  2731. //----------------------------------------------------------------------------
  2732. void CL_DemoTransitionFromTrainstation()
  2733. {
  2734. // kick them out to GameUI instead and bring up the chapter page with raveholm unlocked
  2735. sv_unlockedchapters.SetValue(6); // unlock ravenholm
  2736. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "sv_cheats 1; fadeout 1.5; sv_cheats 0;");
  2737. Cbuf_Execute();
  2738. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.5;
  2739. s_bIsRavenHolmn = false;
  2740. }
  2741. void CL_DemoTransitionFromRavenholm()
  2742. {
  2743. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "sv_cheats 1; fadeout 2; sv_cheats 0;");
  2744. Cbuf_Execute();
  2745. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.9;
  2746. s_bIsRavenHolmn = true;
  2747. }
  2748. void CL_DemoTransitionFromTestChmb()
  2749. {
  2750. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "sv_cheats 1; fadeout 2; sv_cheats 0;");
  2751. Cbuf_Execute();
  2752. s_fDemoRevealGameUITime = Sys_FloatTime() + 1.9;
  2753. }
  2754. //-----------------------------------------------------------------------------
  2755. // Purpose: make the gameui appear after a certain interval
  2756. //----------------------------------------------------------------------------
  2757. void V_RenderVGuiOnly();
  2758. bool V_CheckGamma();
  2759. void CL_DemoCheckGameUIRevealTime( )
  2760. {
  2761. if ( s_fDemoRevealGameUITime > 0 )
  2762. {
  2763. if ( s_fDemoRevealGameUITime < Sys_FloatTime() )
  2764. {
  2765. s_fDemoRevealGameUITime = -1;
  2766. SCR_BeginLoadingPlaque();
  2767. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "disconnect;");
  2768. CCommand args;
  2769. CL_CheckToDisplayStartupMenus( args );
  2770. s_fDemoPlayMusicTime = Sys_FloatTime() + 1.0;
  2771. }
  2772. }
  2773. if ( s_fDemoPlayMusicTime > 0 )
  2774. {
  2775. V_CheckGamma();
  2776. V_RenderVGuiOnly();
  2777. if ( s_fDemoPlayMusicTime < Sys_FloatTime() )
  2778. {
  2779. s_fDemoPlayMusicTime = -1;
  2780. EngineVGui()->ActivateGameUI();
  2781. if ( CL_IsHL2Demo() )
  2782. {
  2783. if ( s_bIsRavenHolmn )
  2784. {
  2785. Cbuf_AddText( Cbuf_GetCurrentPlayer(), "play music/ravenholm_1.mp3;" );
  2786. }
  2787. // else
  2788. // {
  2789. // EngineVGui()->ShowNewGameDialog(6);// bring up the new game dialog in game UI
  2790. // }
  2791. }
  2792. }
  2793. }
  2794. }
  2795. //-----------------------------------------------------------------------------
  2796. // Purpose: setup a debug string that is uploaded on crash
  2797. //----------------------------------------------------------------------------
  2798. char g_minidumpinfo[ 4094 ] = {0};
  2799. PAGED_POOL_INFO_t g_pagedpoolinfo = { 0 };
  2800. #if !defined( NO_STEAM )
  2801. extern bool g_bV3SteamInterface;
  2802. #endif
  2803. void DisplaySystemVersion( char *osversion, int maxlen );
  2804. void CL_SetPagedPoolInfo()
  2805. {
  2806. if ( IsGameConsole() )
  2807. return;
  2808. #if !defined( _GAMECONSOLE ) && !defined(NO_STEAM) && !defined(DEDICATED)
  2809. Plat_GetPagedPoolInfo( &g_pagedpoolinfo );
  2810. #endif
  2811. }
  2812. void CL_SetSteamCrashComment()
  2813. {
  2814. if ( IsGameConsole() )
  2815. return;
  2816. char map[ 80 ];
  2817. char videoinfo[ 2048 ];
  2818. char misc[ 256 ];
  2819. char driverinfo[ 2048 ];
  2820. char osversion[ 256 ];
  2821. map[ 0 ] = 0;
  2822. driverinfo[ 0 ] = 0;
  2823. videoinfo[ 0 ] = 0;
  2824. misc[ 0 ] = 0;
  2825. osversion[ 0 ] = 0;
  2826. if ( host_state.worldmodel )
  2827. {
  2828. CL_SetupMapName( modelloader->GetName( host_state.worldmodel ), map, sizeof( map ) );
  2829. }
  2830. DisplaySystemVersion( osversion, sizeof( osversion ) );
  2831. MaterialAdapterInfo_t info;
  2832. materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), info );
  2833. const char *dxlevel = "Unk";
  2834. if ( g_pMaterialSystemHardwareConfig )
  2835. {
  2836. dxlevel = COM_DXLevelToString( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() ) ;
  2837. }
  2838. // Make a string out of the high part and low parts of driver version
  2839. char szDXDriverVersion[ 64 ];
  2840. Q_snprintf( szDXDriverVersion, sizeof( szDXDriverVersion ), "%ld.%ld.%ld.%ld",
  2841. ( long )( info.m_nDriverVersionHigh>>16 ),
  2842. ( long )( info.m_nDriverVersionHigh & 0xffff ),
  2843. ( long )( info.m_nDriverVersionLow>>16 ),
  2844. ( long )( info.m_nDriverVersionLow & 0xffff ) );
  2845. 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\nVid: %i x %i",
  2846. info.m_pDriverName,
  2847. szDXDriverVersion,
  2848. info.m_VendorID,
  2849. info.m_DeviceID,
  2850. info.m_SubSysID,
  2851. info.m_Revision,
  2852. dxlevel ? dxlevel : "Unk",
  2853. videomode->GetModeWidth(), videomode->GetModeHeight() );
  2854. ConVarRef mat_picmip( "mat_picmip" );
  2855. ConVarRef mat_forceaniso( "mat_forceaniso" );
  2856. ConVarRef mat_antialias( "mat_antialias" );
  2857. ConVarRef mat_aaquality( "mat_aaquality" );
  2858. ConVarRef r_shadowrendertotexture( "r_shadowrendertotexture" );
  2859. ConVarRef r_flashlightdepthtexture( "r_flashlightdepthtexture" );
  2860. #ifndef _X360
  2861. ConVarRef csm_quality_level( "csm_quality_level" );
  2862. ConVarRef r_waterforceexpensive( "r_waterforceexpensive" );
  2863. #endif
  2864. ConVarRef r_waterforcereflectentities( "r_waterforcereflectentities" );
  2865. ConVarRef mat_vsync( "mat_vsync" );
  2866. ConVarRef r_rootlod( "r_rootlod" );
  2867. ConVarRef mat_motion_blur_enabled( "mat_motion_blur_enabled" );
  2868. ConVarRef mat_queue_mode( "mat_queue_mode" );
  2869. ConVarRef mat_triplebuffered( "mat_triplebuffered" );
  2870. #ifdef _X360
  2871. Q_snprintf( videoinfo, sizeof(videoinfo), "picmip: %i forceaniso: %i antialias: %i (%i) vsync: %i rootlod: %i\nshadowrendertotexture: %i r_flashlightdepthtexture %i"\
  2872. "waterforcereflectentities: %i mat_motion_blur_enabled: %i mat_triplebuffered: %i",
  2873. mat_picmip.GetInt(), mat_forceaniso.GetInt(), mat_antialias.GetInt(), mat_aaquality.GetInt(), mat_vsync.GetInt(), r_rootlod.GetInt(), r_shadowrendertotexture.GetInt(),
  2874. r_flashlightdepthtexture.GetInt(), r_waterforcereflectentities.GetInt(), mat_motion_blur_enabled.GetInt(), mat_triplebuffered.GetInt() );
  2875. #else
  2876. Q_snprintf( videoinfo, sizeof(videoinfo), "picmip: %i\nforceaniso: %i\nantialias: %i (%i)\nvsync: %i\nrootlod: %i\nshadowrendertotexture: %i\nr_flashlightdepthtexture %i\n"\
  2877. "waterforceexpensive: %i\nwaterforcereflectentities: %i\nmat_motion_blur_enabled: %i\nmat_queue_mode %i\nmat_triplebuffered: %i\ncsm_quality_level: %i",
  2878. mat_picmip.GetInt(), mat_forceaniso.GetInt(), mat_antialias.GetInt(), mat_aaquality.GetInt(), mat_vsync.GetInt(), r_rootlod.GetInt(), r_shadowrendertotexture.GetInt(),
  2879. r_flashlightdepthtexture.GetInt(), r_waterforceexpensive.GetInt(), r_waterforcereflectentities.GetInt(), mat_motion_blur_enabled.GetInt(), mat_queue_mode.GetInt(),
  2880. mat_triplebuffered.GetInt(), csm_quality_level.GetInt() );
  2881. #endif
  2882. int latency = 0;
  2883. if ( GetBaseLocalClient().m_NetChannel )
  2884. {
  2885. latency = (int)( 1000.0f * GetBaseLocalClient().m_NetChannel->GetAvgLatency( FLOW_OUTGOING ) );
  2886. }
  2887. Q_snprintf( misc, sizeof( misc ), "skill:%i rate %i update %i cmd %i latency %i msec",
  2888. skill.GetInt(),
  2889. cl_rate->GetInt(),
  2890. (int)cl_updaterate->GetFloat(),
  2891. (int)cl_cmdrate->GetFloat(),
  2892. latency
  2893. );
  2894. const char *pNetChannel = "Not Connected";
  2895. if ( GetBaseLocalClient().m_NetChannel )
  2896. {
  2897. pNetChannel = GetBaseLocalClient().m_NetChannel->GetAddress();
  2898. }
  2899. CL_SetPagedPoolInfo();
  2900. char am_pm[] = "AM";
  2901. struct tm newtime;
  2902. Plat_GetLocalTime( &newtime );
  2903. if( newtime.tm_hour > 12 ) /* Set up extension. */
  2904. Q_strncpy( am_pm, "PM", sizeof( am_pm ) );
  2905. if( newtime.tm_hour > 12 ) /* Convert from 24-hour */
  2906. newtime.tm_hour -= 12; /* to 12-hour clock. */
  2907. if( newtime.tm_hour == 0 ) /*Set hour to 12 if midnight. */
  2908. newtime.tm_hour = 12;
  2909. char tString[ 128 ];
  2910. Plat_GetTimeString( &newtime, tString, sizeof( tString ) );
  2911. int tLen = Q_strlen( tString );
  2912. if ( tLen > 0 && tString[ tLen - 1 ] == '\n' )
  2913. {
  2914. tString[ tLen - 1 ] = 0;
  2915. }
  2916. Q_snprintf( g_minidumpinfo, sizeof(g_minidumpinfo),
  2917. "Map: %s\n"\
  2918. "Game: %s\n"\
  2919. "Build: %i\n"\
  2920. "OS: %s\n"\
  2921. "Misc: %s\n"\
  2922. "Net: %s\n"\
  2923. "Time: %s\n"\
  2924. "cmdline:%s\n"\
  2925. "protocol:%s\n"
  2926. "driver: %s\n"\
  2927. "video: %s\n"\
  2928. "PP PAGES: used: %d, free %d\n",
  2929. map, com_gamedir, build_number(), osversion, misc, pNetChannel, tString, CommandLine()->GetCmdLine(), Sys_GetVersionString(),
  2930. driverinfo, videoinfo, g_pagedpoolinfo.numPagesUsed, (int)g_pagedpoolinfo.numPagesFree );
  2931. #ifndef NO_STEAM
  2932. SteamAPI_SetMiniDumpComment( g_minidumpinfo );
  2933. #endif
  2934. }
  2935. //
  2936. // register commands
  2937. //
  2938. 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." );
  2939. ConVar cl_language( "cl_language", "english", FCVAR_HIDDEN, "Language (from Steam API)" );
  2940. void CL_InitLanguageCvar()
  2941. {
  2942. // !! bug do i need to do something linux-wise here.
  2943. char language[64];
  2944. // Fallback to English
  2945. V_strncpy( language, "english", sizeof( language ) );
  2946. if ( IsPC() )
  2947. {
  2948. #if !defined( NO_STEAM )
  2949. if ( CommandLine()->CheckParm( "-language" ) )
  2950. {
  2951. Q_strncpy( language, CommandLine()->ParmValue( "-language", "english"), sizeof( language ) );
  2952. }
  2953. else
  2954. {
  2955. // Use steam client language
  2956. memset( language, 0, sizeof( language ) );
  2957. #ifdef PLATFORM_WINDOWS
  2958. vgui::system()->GetRegistryString( "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Language", language, sizeof( language ) - 1 );
  2959. if ( Q_strlen( language ) == 0 || Q_stricmp( language, "unknown" ) == 0 )
  2960. {
  2961. Q_strncpy( language, "english", sizeof( language ) );
  2962. }
  2963. #elif defined(OSX)
  2964. if ( Steam3Client().SteamApps() )
  2965. {
  2966. // just follow the language steam wants you to be
  2967. const char *lang = Steam3Client().SteamApps()->GetCurrentGameLanguage();
  2968. if ( lang && Q_strlen(lang) )
  2969. {
  2970. Q_strncpy( language, lang, sizeof( language ) );
  2971. }
  2972. else
  2973. Q_strncpy( language, "english", sizeof( language ) );
  2974. }
  2975. else
  2976. {
  2977. Q_strncpy( language, "english", sizeof( language ) );
  2978. }
  2979. #endif
  2980. }
  2981. #endif
  2982. }
  2983. else
  2984. {
  2985. Q_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
  2986. }
  2987. cl_language.SetValue( language );
  2988. }
  2989. void CL_ChangeCloudSettingsCvar( IConVar *var, const char *pOldValue, float flOldValue )
  2990. {
  2991. // !! bug do i need to do something linux-wise here.
  2992. if ( IsPC() && Steam3Client().SteamUtils() )
  2993. {
  2994. #ifdef PLATFORM_WINDOWS
  2995. char szRegistryKeyLocation[ 256 ];
  2996. Q_snprintf( szRegistryKeyLocation, sizeof( szRegistryKeyLocation ), "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Apps\\%d\\Cloud", Steam3Client().SteamUtils()->GetAppID() );
  2997. ConVarRef ref( var->GetName() );
  2998. vgui::system()->SetRegistryInteger( szRegistryKeyLocation, ref.GetInt() );
  2999. #endif
  3000. }
  3001. }
  3002. ConVar cl_cloud_settings( "cl_cloud_settings", "-1", FCVAR_HIDDEN, "Cloud enabled from (from HKCU\\Software\\Valve\\Steam\\Apps\\appid\\Cloud)", CL_ChangeCloudSettingsCvar );
  3003. void CL_InitCloudSettingsCvar()
  3004. {
  3005. if ( IsPC() && Steam3Client().SteamUtils() )
  3006. {
  3007. int iCloudSettings = -1;
  3008. #ifdef PLATFORM_WINDOWS
  3009. char szRegistryKeyLocation[ 256 ];
  3010. Q_snprintf( szRegistryKeyLocation, sizeof( szRegistryKeyLocation ), "HKEY_CURRENT_USER\\Software\\Valve\\Steam\\Apps\\%d\\Cloud", Steam3Client().SteamUtils()->GetAppID() );
  3011. bool bFound = vgui::system()->GetRegistryInteger( szRegistryKeyLocation, iCloudSettings );
  3012. if ( !bFound )
  3013. {
  3014. #ifndef PORTAL2
  3015. // No key yet, use the uninitialized value
  3016. iCloudSettings = -1;
  3017. #else
  3018. // Portal 2 will cloud everything by default if no registry key
  3019. iCloudSettings = STEAMREMOTESTORAGE_CLOUD_ALL;
  3020. #endif
  3021. }
  3022. #if defined( CSTRIKE15 )
  3023. // Cloud isn't used in CS:GO for now
  3024. // This may eventually become optional, but we need to wipe out people's settings
  3025. // So that it's opt-in for existing players in the future
  3026. iCloudSettings = 0;
  3027. #endif
  3028. #else
  3029. iCloudSettings = STEAMREMOTESTORAGE_CLOUD_ALL;
  3030. #endif
  3031. cl_cloud_settings.SetValue( iCloudSettings );
  3032. }
  3033. else
  3034. {
  3035. // If not on PC or steam not available, set to 0 to make sure no replication occurs or is attempted
  3036. cl_cloud_settings.SetValue( 0 );
  3037. }
  3038. }
  3039. /*
  3040. =================
  3041. CL_Init
  3042. =================
  3043. */
  3044. void CL_Init( void )
  3045. {
  3046. for ( int hh = 0; hh < host_state.max_splitscreen_players; ++hh )
  3047. {
  3048. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  3049. GetLocalClient().Clear();
  3050. }
  3051. CL_InitLanguageCvar();
  3052. CL_InitCloudSettingsCvar();
  3053. #if defined( REPLAY_ENABLED )
  3054. g_pClientReplayHistoryManager->Init();
  3055. #endif
  3056. }
  3057. //-----------------------------------------------------------------------------
  3058. // Purpose:
  3059. //-----------------------------------------------------------------------------
  3060. void CL_Shutdown( void )
  3061. {
  3062. #if defined( REPLAY_ENABLED )
  3063. g_pClientReplayHistoryManager->Shutdown();
  3064. #endif
  3065. }
  3066. CON_COMMAND_F( cl_fullupdate, "Forces the server to send a full update packet", FCVAR_CHEAT )
  3067. {
  3068. GetLocalClient().ForceFullUpdate( "cl_fullupdate command" );
  3069. }
  3070. #ifdef _DEBUG
  3071. CON_COMMAND( cl_download, "Downloads a file from server." )
  3072. {
  3073. if ( args.ArgC() != 2 )
  3074. return;
  3075. if ( !GetBaseLocalClient().m_NetChannel )
  3076. return;
  3077. GetBaseLocalClient().m_NetChannel->RequestFile( args[ 1 ], false ); // just for testing stuff
  3078. }
  3079. #endif // _DEBUG
  3080. CON_COMMAND_F( setinfo, "Adds a new user info value", FCVAR_CLIENTCMD_CAN_EXECUTE )
  3081. {
  3082. if ( args.ArgC() != 3 )
  3083. {
  3084. Msg("Syntax: setinfo <key> <value>\n");
  3085. return;
  3086. }
  3087. const char *name = args[ 1 ];
  3088. const char *value = args[ 2 ];
  3089. ConCommandBase *pCommand = g_pCVar->FindCommandBase( name );
  3090. ConVarRef sv_cheats( "sv_cheats" );
  3091. if ( pCommand )
  3092. {
  3093. if ( pCommand->IsCommand() )
  3094. {
  3095. Msg("Name %s is already registered as console command\n", name );
  3096. return;
  3097. }
  3098. if ( !pCommand->IsFlagSet(FCVAR_USERINFO) )
  3099. {
  3100. Msg("Convar %s is already registered but not as user info value\n", name );
  3101. return;
  3102. }
  3103. if ( pCommand->IsFlagSet( FCVAR_NOT_CONNECTED ) )
  3104. {
  3105. #ifndef DEDICATED
  3106. // Connected to server?
  3107. if ( GetBaseLocalClient().IsConnected() )
  3108. {
  3109. extern IBaseClientDLL *g_ClientDLL;
  3110. if ( pCommand->IsFlagSet( FCVAR_USERINFO ) && g_ClientDLL && g_ClientDLL->IsConnectedUserInfoChangeAllowed( NULL ) )
  3111. {
  3112. // Client.dll is allowing the convar change
  3113. }
  3114. else
  3115. {
  3116. ConMsg( "Can't change %s when playing, disconnect from the server or switch team to spectators\n", pCommand->GetName() );
  3117. return;
  3118. }
  3119. }
  3120. #endif
  3121. }
  3122. if ( IsPC() )
  3123. {
  3124. #if !defined(NO_STEAM)
  3125. EUniverse eUniverse = GetSteamUniverse();
  3126. if ( (( eUniverse != k_EUniverseBeta ) && ( eUniverse != k_EUniverseDev )) && pCommand->IsFlagSet( FCVAR_DEVELOPMENTONLY ) )
  3127. return;
  3128. #endif
  3129. }
  3130. if ( pCommand->IsFlagSet( FCVAR_CHEAT ) && sv_cheats.GetBool() == 0 )
  3131. {
  3132. Msg("Convar %s is marked as cheat and cheats are off\n", name );
  3133. return;
  3134. }
  3135. }
  3136. else
  3137. {
  3138. // cvar not found, create it now
  3139. char *pszString = new char[Q_strlen( name ) + 1];
  3140. Q_strcpy( pszString, name );
  3141. pCommand = new ConVar( pszString, "", FCVAR_USERINFO, "Custom user info value" );
  3142. }
  3143. ConVar *pConVar = (ConVar*)pCommand;
  3144. pConVar->SetValue( value );
  3145. if ( GetBaseLocalClient().IsConnected() )
  3146. {
  3147. // send changed cvar to server
  3148. CNETMsg_SetConVar_t convar( name, pConVar->GetString() );
  3149. GetBaseLocalClient().m_NetChannel->SendNetMsg( convar );
  3150. }
  3151. }
  3152. CON_COMMAND( cl_precacheinfo, "Show precache info (client)." )
  3153. {
  3154. if ( args.ArgC() == 2 )
  3155. {
  3156. GetBaseLocalClient().DumpPrecacheStats( args[ 1 ] );
  3157. return;
  3158. }
  3159. // Show all data
  3160. GetBaseLocalClient().DumpPrecacheStats( MODEL_PRECACHE_TABLENAME );
  3161. GetBaseLocalClient().DumpPrecacheStats( DECAL_PRECACHE_TABLENAME );
  3162. GetBaseLocalClient().DumpPrecacheStats( SOUND_PRECACHE_TABLENAME );
  3163. GetBaseLocalClient().DumpPrecacheStats( GENERIC_PRECACHE_TABLENAME );
  3164. }
  3165. CON_COMMAND_F( con_min_severity, "Minimum severity level for messages sent to any logging channel: LS_MESSAGE=0, LS_WARNING=1, LS_ASSERT=2, LS_ERROR=3.", FCVAR_CLIENTCMD_CAN_EXECUTE )
  3166. {
  3167. if ( args.ArgC() > 1 )
  3168. {
  3169. int nSeverity = V_atoi( args[1] );
  3170. clamp( nSeverity, LS_MESSAGE, LS_HIGHEST_SEVERITY );
  3171. LoggingSystem_SetGlobalSpewLevel( static_cast<LoggingSeverity_t>( nSeverity ) );
  3172. }
  3173. }
  3174. //-----------------------------------------------------------------------------
  3175. // Purpose:
  3176. // Input : *object -
  3177. // stringTable -
  3178. // stringNumber -
  3179. // *newString -
  3180. // *newData -
  3181. //-----------------------------------------------------------------------------
  3182. void Callback_ModelChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3183. {
  3184. if ( stringTable == GetBaseLocalClient().m_pModelPrecacheTable )
  3185. {
  3186. // Index 0 is always NULL, just ignore it
  3187. // Index 1 == the world, don't
  3188. if ( stringNumber >= 1 )
  3189. {
  3190. GetBaseLocalClient().SetModel( stringNumber );
  3191. }
  3192. }
  3193. else
  3194. {
  3195. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  3196. }
  3197. }
  3198. //-----------------------------------------------------------------------------
  3199. // Purpose:
  3200. // Input : *object -
  3201. // stringTable -
  3202. // stringNumber -
  3203. // *newString -
  3204. // *newData -
  3205. //-----------------------------------------------------------------------------
  3206. void Callback_GenericChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3207. {
  3208. if ( stringTable == GetBaseLocalClient().m_pGenericPrecacheTable )
  3209. {
  3210. // Index 0 is always NULL, just ignore it
  3211. if ( stringNumber >= 1 )
  3212. {
  3213. GetBaseLocalClient().SetGeneric( stringNumber );
  3214. }
  3215. }
  3216. else
  3217. {
  3218. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  3219. }
  3220. }
  3221. //-----------------------------------------------------------------------------
  3222. // Purpose:
  3223. // Input : *object -
  3224. // stringTable -
  3225. // stringNumber -
  3226. // *newString -
  3227. // *newData -
  3228. //-----------------------------------------------------------------------------
  3229. void Callback_SoundChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3230. {
  3231. if ( stringTable == GetBaseLocalClient().m_pSoundPrecacheTable )
  3232. {
  3233. // Index 0 is always NULL, just ignore it
  3234. if ( stringNumber >= 1 )
  3235. {
  3236. GetBaseLocalClient().SetSound( stringNumber );
  3237. }
  3238. }
  3239. else
  3240. {
  3241. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  3242. }
  3243. }
  3244. void Callback_DecalChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3245. {
  3246. if ( stringTable == GetBaseLocalClient().m_pDecalPrecacheTable )
  3247. {
  3248. GetBaseLocalClient().SetDecal( stringNumber );
  3249. }
  3250. else
  3251. {
  3252. Assert( 0 ) ; // Callback_*Changed called with wrong stringtable
  3253. }
  3254. }
  3255. void Callback_InstanceBaselineChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3256. {
  3257. Assert( stringTable == GetBaseLocalClient().m_pInstanceBaselineTable );
  3258. GetLocalClient().UpdateInstanceBaseline( stringNumber );
  3259. }
  3260. void Callback_UserInfoChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3261. {
  3262. Assert( stringTable == GetBaseLocalClient().m_pUserInfoTable );
  3263. // stringnumber == player slot
  3264. player_info_t *player = (player_info_t*)newData;
  3265. if ( !player )
  3266. return; // player left the game
  3267. // request custom user files if necessary
  3268. for ( int i=0; i<MAX_CUSTOM_FILES; i++ )
  3269. {
  3270. GetBaseLocalClient().CheckOthersCustomFile( player->customFiles[i] );
  3271. }
  3272. // fire local client event game event
  3273. IGameEvent * event = g_GameEventManager.CreateEvent( "player_info" );
  3274. if ( event )
  3275. {
  3276. event->SetInt( "userid", player->userID );
  3277. event->SetInt( "friendsid", player->friendsID );
  3278. event->SetUint64( "xuid", player->xuid );
  3279. event->SetInt( "index", stringNumber );
  3280. event->SetString( "name", player->name );
  3281. event->SetString( "networkid", player->guid );
  3282. event->SetBool( "bot", player->fakeplayer );
  3283. g_GameEventManager.FireEventClientSide( event );
  3284. }
  3285. }
  3286. void Callback_DynamicModelChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, const void *newData )
  3287. {
  3288. extern IVModelInfoClient *modelinfoclient;
  3289. if ( modelinfoclient )
  3290. {
  3291. modelinfoclient->OnDynamicModelStringTableChanged( stringNumber, newString, newData );
  3292. }
  3293. }
  3294. void CL_HookClientStringTables()
  3295. {
  3296. // install hooks
  3297. int numTables = GetBaseLocalClient().m_StringTableContainer->GetNumTables();
  3298. for ( int i =0; i<numTables; i++)
  3299. {
  3300. // iterate through server tables
  3301. CNetworkStringTable *pTable =
  3302. (CNetworkStringTable*)GetBaseLocalClient().m_StringTableContainer->GetTable( i );
  3303. if ( !pTable )
  3304. continue;
  3305. GetBaseLocalClient().HookClientStringTable( pTable->GetTableName() );
  3306. }
  3307. }
  3308. // Installs the all, and invokes cb for all existing items
  3309. void CL_InstallAndInvokeClientStringTableCallbacks()
  3310. {
  3311. VPROF_BUDGET( "CL_InstallAndInvokeClientStringTableCallbacks", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  3312. // install hooks
  3313. int numTables = GetBaseLocalClient().m_StringTableContainer->GetNumTables();
  3314. for ( int i =0; i<numTables; i++)
  3315. {
  3316. // iterate through server tables
  3317. CNetworkStringTable *pTable =
  3318. (CNetworkStringTable*)GetBaseLocalClient().m_StringTableContainer->GetTable( i );
  3319. if ( !pTable )
  3320. continue;
  3321. pfnStringChanged pOldFunction = pTable->GetCallback();
  3322. GetBaseLocalClient().InstallStringTableCallback( pTable->GetTableName() );
  3323. pfnStringChanged pNewFunction = pTable->GetCallback();
  3324. if ( !pNewFunction )
  3325. continue;
  3326. // 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
  3327. if ( pNewFunction == pOldFunction )
  3328. continue;
  3329. COM_TimestampedLog( "String Table Callbacks %s - Start", pTable->GetTableName() );
  3330. for ( int j = 0; j < pTable->GetNumStrings(); ++j )
  3331. {
  3332. if ( !( j % 25 ) )
  3333. {
  3334. EngineVGui()->UpdateProgressBar(PROGRESS_DEFAULT);
  3335. }
  3336. int userDataSize;
  3337. const void *pUserData = pTable->GetStringUserData( j, &userDataSize );
  3338. (*pNewFunction)( NULL, pTable, j, pTable->GetString( j ), pUserData );
  3339. }
  3340. COM_TimestampedLog( "String Table Callbacks %s - Finish", pTable->GetTableName() );
  3341. }
  3342. }