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.

3794 lines
117 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: baseclientstate.cpp: implementation of the CBaseClientState class.
  4. //
  5. //===========================================================================//
  6. #include "client_pch.h"
  7. #include "baseclientstate.h"
  8. #include "inetchannel.h"
  9. #include "netmessages.h"
  10. #include "proto_oob.h"
  11. #include "dt_recv_eng.h"
  12. #include "host_cmd.h"
  13. #include "GameEventManager.h"
  14. #include "cl_rcon.h"
  15. #ifndef DEDICATED
  16. #include "cl_pluginhelpers.h"
  17. #include "vgui_askconnectpanel.h"
  18. #include "cdll_engine_int.h"
  19. #endif
  20. #include "sv_steamauth.h"
  21. #include "snd_audio_source.h"
  22. #include "server.h"
  23. #include "cl_steamauth.h"
  24. #if defined( REPLAY_ENABLED )
  25. #include "replayserver.h"
  26. #include "replayhistorymanager.h"
  27. #endif
  28. #include "filesystem/IQueuedLoader.h"
  29. #include "serializedentity.h"
  30. #include "checksum_engine.h"
  31. #include "matchmaking/imatchframework.h"
  32. #include "mathlib/IceKey.H"
  33. #include "hltvserver.h"
  34. #include "UtlStringMap.h"
  35. #if defined( INCLUDE_SCALEFORM )
  36. #include "scaleformui/scaleformui.h"
  37. #endif
  38. #include "vgui/ILocalize.h"
  39. #include "eiface.h"
  40. #include "cl_broadcast.h"
  41. #include "csgo_limits.h"
  42. #include "csgo_limits.inl"
  43. #if defined( _PS3 )
  44. #include <sysutil/sysutil_userinfo.h>
  45. #endif
  46. // memdbgon must be the last include file in a .cpp file!!!
  47. #include "tier0/memdbgon.h"
  48. ConVar cl_teammate_color_1( "cl_teammate_color_1", "240 243 32" );
  49. ConVar cl_teammate_color_2( "cl_teammate_color_2", "150 34 223" );
  50. ConVar cl_teammate_color_3( "cl_teammate_color_3", "0 165 90" );
  51. ConVar cl_teammate_color_4( "cl_teammate_color_4", "92 168 255" );
  52. ConVar cl_teammate_color_5( "cl_teammate_color_5", "255 155 37" );
  53. #if defined( INCLUDE_SCALEFORM )
  54. const char* g_szDefaultScaleformClientMovieName = "resource/flash/GameUIRootMovie.swf";
  55. #endif
  56. #ifdef ENABLE_RPT
  57. void CL_NotifyRPTOfDisconnect( );
  58. #endif // ENABLE_RPT
  59. #if ( !defined( NO_STEAM ) && (!defined( DEDICATED ) ) )
  60. void UpdateNameFromSteamID( IConVar *pConVar, CSteamID *pSteamID )
  61. {
  62. #if !defined( DEDICATED )
  63. if ( !pConVar || !pSteamID || !Steam3Client().SteamFriends() )
  64. return;
  65. #if defined( _PS3 )
  66. CSteamID sPsnId = Steam3Client().SteamUser()->GetConsoleSteamID();
  67. if ( sPsnId.IsValid() )
  68. {
  69. const char *pszName = Steam3Client().SteamFriends()->GetFriendPersonaName( sPsnId );
  70. pConVar->SetValue( pszName );
  71. }
  72. #else
  73. Assert( pSteamID->GetAccountID() != 0 || CommandLine()->FindParm( "-ignoreSteamAsserts" ) );
  74. const char *pszName = Steam3Client().SteamFriends()->GetFriendPersonaName( *pSteamID );
  75. pConVar->SetValue( pszName );
  76. #endif // _PS3
  77. #endif
  78. }
  79. void SetNameToSteamIDName( IConVar *pConVar )
  80. {
  81. #if !defined( DEDICATED )
  82. if ( Steam3Client().SteamUtils() && Steam3Client().SteamFriends() && Steam3Client().SteamUser() )
  83. {
  84. CSteamID steamID = Steam3Client().SteamUser()->GetSteamID();
  85. UpdateNameFromSteamID( pConVar, &steamID );
  86. }
  87. #endif
  88. }
  89. #endif
  90. void CL_NameCvarChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
  91. {
  92. CSplitScreenAddedConVar *pCheck = dynamic_cast< CSplitScreenAddedConVar * >( pConVar );
  93. if ( pCheck )
  94. return;
  95. #ifndef DEDICATED
  96. #if !defined( NO_STEAM )
  97. static bool bPreventRent = false;
  98. if ( !bPreventRent )
  99. {
  100. bPreventRent = true;
  101. SetNameToSteamIDName( pConVar );
  102. bPreventRent = false;
  103. }
  104. #endif
  105. #endif
  106. ConVarRef var( pConVar );
  107. // store off the last known name, that isn't default, in the registry
  108. // this is a transition step so it can be used to display in friends
  109. if ( 0 != Q_stricmp( var.GetString(), var.GetDefault() )
  110. && 0 != Q_stricmp( var.GetString(), "player" ) )
  111. {
  112. Sys_SetRegKeyValue( "Software\\Valve\\Steam", "LastGameNameUsed", (char *)var.GetString() );
  113. }
  114. }
  115. #ifndef DEDICATED
  116. void askconnect_accept_f()
  117. {
  118. char szHostName[256];
  119. if ( IsAskConnectPanelActive( szHostName, sizeof( szHostName ) ) )
  120. {
  121. char szCommand[512];
  122. V_snprintf( szCommand, sizeof( szCommand ), "connect %s", szHostName );
  123. Cbuf_AddText( Cbuf_GetCurrentPlayer(), szCommand );
  124. HideAskConnectPanel();
  125. }
  126. }
  127. ConCommand askconnect_accept( "askconnect_accept", askconnect_accept_f, "Accept a redirect request by the server.", FCVAR_DONTRECORD );
  128. #endif
  129. #ifndef SWDS
  130. extern IVEngineClient *engineClient;
  131. // ---------------------------------------------------------------------------------------- //
  132. static void SendClanTag( const char *pTag, const char *pName )
  133. {
  134. KeyValues *kv = new KeyValues( "ClanTagChanged" );
  135. kv->SetString( "tag", pTag );
  136. kv->SetString( "name", pName );
  137. engineClient->ServerCmdKeyValues( kv );
  138. }
  139. #endif
  140. // ---------------------------------------------------------------------------------------- //
  141. void CL_ClanIdChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
  142. {
  143. #ifndef SWDS
  144. // Get the clan ID we're trying to select
  145. ConVarRef var( pConVar );
  146. uint32 newId = var.GetInt();
  147. if ( newId == 0 )
  148. {
  149. // Default value, equates to no tag
  150. SendClanTag( "", "" );
  151. return;
  152. }
  153. #if !defined( NO_STEAM )
  154. // Make sure this player is actually part of the desired clan
  155. ISteamFriends *pFriends = Steam3Client().SteamFriends();
  156. if ( pFriends )
  157. {
  158. int iGroupCount = pFriends->GetClanCount();
  159. for ( int k = 0; k < iGroupCount; ++ k )
  160. {
  161. CSteamID clanID = pFriends->GetClanByIndex( k );
  162. if ( clanID.GetAccountID() == newId )
  163. {
  164. CSteamID clanID( newId, Steam3Client().SteamUtils()->GetConnectedUniverse(), k_EAccountTypeClan );
  165. // valid clan, accept the change
  166. const char *szClanTag = pFriends->GetClanTag( clanID );
  167. char chLimitedTag[ MAX_CLAN_TAG_LENGTH ];
  168. CopyStringTruncatingMalformedUTF8Tail( chLimitedTag, szClanTag, MAX_CLAN_TAG_LENGTH );
  169. const char *szClanName = pFriends->GetClanName( clanID );
  170. SendClanTag( chLimitedTag, szClanName );
  171. return;
  172. }
  173. }
  174. }
  175. #endif // NO_STEAM
  176. // Couldn't validate the ID, so clear to the default (no tag)
  177. var.SetValue( 0 );
  178. #endif // !SWDS
  179. }
  180. ConVar cl_resend ( "cl_resend", "2", FCVAR_RELEASE, "Delay in seconds before the client will resend the 'connect' attempt", true, CL_MIN_RESEND_TIME, true, CL_MAX_RESEND_TIME );
  181. ConVar cl_resend_timeout ( "cl_resend_timeout", "60", FCVAR_RELEASE, "Total time allowed for the client to resend the 'connect' attempt", true, CL_MIN_RESEND_TIME, true, 1000 * CL_MAX_RESEND_TIME );
  182. ConVar cl_name ( "name","unnamed", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_SS | FCVAR_PRINTABLEONLY | FCVAR_SERVER_CAN_EXECUTE, "Current user name", CL_NameCvarChanged );
  183. ConVar password ( "password", "", FCVAR_ARCHIVE | FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD, "Current server access password" );
  184. static ConVar cl_interpolate( "cl_interpolate", "1", FCVAR_RELEASE, "Enables or disables interpolation on listen servers or during demo playback" );
  185. ConVar cl_clanid( "cl_clanid", "0", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_HIDDEN, "Current clan ID for name decoration", CL_ClanIdChanged );
  186. ConVar cl_color( "cl_color", "0", FCVAR_ARCHIVE | FCVAR_USERINFO, "Preferred teammate color", true, 0, true, 4 );
  187. ConVar cl_decryptdata_key( "cl_decryptdata_key", "", FCVAR_RELEASE, "Key to decrypt encrypted GOTV messages" );
  188. ConVar cl_decryptdata_key_pub( "cl_decryptdata_key_pub", "", FCVAR_RELEASE, "Key to decrypt public encrypted GOTV messages" );
  189. ConVar cl_hideserverip( "cl_hideserverip", "0", FCVAR_RELEASE, "If set to 1, server IPs will be hidden in the console (except when you type 'status')" );
  190. #ifdef _X360
  191. ConVar cl_networkid_force ( "networkid_force", "", FCVAR_USERINFO | FCVAR_SS | FCVAR_PRINTABLEONLY | FCVAR_SERVER_CAN_EXECUTE | FCVAR_DEVELOPMENTONLY, "Forceful value for network id (e.g. XUID)" );
  192. #endif
  193. static ConVar cl_failremoteconnections( "cl_failremoteconnections", "0", FCVAR_DEVELOPMENTONLY, "Force connection attempts to time out" );
  194. static uint32 GetPrivateIPDelayMsecs()
  195. {
  196. // Lesser of 1/2 cl_resend interval or 1000 msecs
  197. float flSeconds = clamp( cl_resend.GetFloat() * 0.5f, 0.0f, 1.0f );
  198. return (uint32)( flSeconds * 1000.0f );
  199. }
  200. // ---------------------------------------------------------------------------------------- //
  201. // C_ServerClassInfo implementation.
  202. // ---------------------------------------------------------------------------------------- //
  203. C_ServerClassInfo::C_ServerClassInfo()
  204. {
  205. m_ClassName = NULL;
  206. m_DatatableName = NULL;
  207. m_InstanceBaselineIndex = INVALID_STRING_INDEX;
  208. }
  209. C_ServerClassInfo::~C_ServerClassInfo()
  210. {
  211. delete [] m_ClassName;
  212. delete [] m_DatatableName;
  213. }
  214. // ---------------------------------------------------------------------------------------- //
  215. // Server messaging helpers
  216. // ---------------------------------------------------------------------------------------- //
  217. CServerMsg::CServerMsg( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback,
  218. const ns_address& serverAdr, int socket, uint32 maxAttempts, double timeout ):
  219. m_pParent( pParent )
  220. {
  221. m_eState = AOS_RUNNING;
  222. m_pCallback = pCallback;
  223. m_serverAdr = serverAdr;
  224. m_socket = socket;
  225. m_lastMsgSendTime = 0.0;
  226. m_timeOut = timeout;
  227. m_maxAttempts = maxAttempts;
  228. m_numAttempts = 0;
  229. m_result = 0;
  230. }
  231. void CServerMsg::Update()
  232. {
  233. if ( m_eState != AOS_RUNNING )
  234. {
  235. return;
  236. }
  237. double dt = net_time - m_lastMsgSendTime;
  238. if ( dt < m_timeOut )
  239. {
  240. return;
  241. }
  242. if ( m_numAttempts >= m_maxAttempts )
  243. {
  244. // Failed to receive a reply
  245. m_eState = AOS_FAILED;
  246. m_pCallback->OnOperationFinished( this );
  247. }
  248. else
  249. {
  250. m_lastToken = RandomInt( INT_MIN, INT_MAX );
  251. SendMsg( m_serverAdr, m_socket, m_lastToken );
  252. m_lastMsgSendTime = net_time;
  253. m_numAttempts++;
  254. }
  255. }
  256. bool CServerMsg::IsValidResponse( const ns_address& from, uint32 token )
  257. {
  258. if ( GetState() != AOS_RUNNING )
  259. {
  260. // We are not expecting any responses
  261. return false;
  262. }
  263. if ( !from.CompareAdr(GetServerAddr()) )
  264. {
  265. // Not expecting a response from this address
  266. return false;
  267. }
  268. if ( token != GetLastToken() )
  269. {
  270. // This response is not for the last message sent
  271. return false;
  272. }
  273. return true;
  274. }
  275. void CServerMsg::ResponseReceived( uint64 result )
  276. {
  277. if ( GetState() == AOS_RUNNING )
  278. {
  279. m_eState = AOS_SUCCEEDED;
  280. m_result = result;
  281. m_pCallback->OnOperationFinished( this );
  282. }
  283. }
  284. extern ConVar sv_mmqueue_reservation_timeout;
  285. extern ConVar sv_mmqueue_reservation_extended_timeout;
  286. CServerMsg_CheckReservation::CServerMsg_CheckReservation( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback,
  287. const ns_address &serverAdr, int socket, uint64 reservationCookie, uint32 uiReservationStage ) :
  288. CServerMsg( pParent, pCallback, serverAdr, socket, (uiReservationStage > 1) ? sv_mmqueue_reservation_extended_timeout.GetInt() : sv_mmqueue_reservation_timeout.GetInt(), 1.0 ) // Try as many times as server reservation seconds
  289. {
  290. m_reservationCookie = reservationCookie;
  291. m_uiReservationStage = uiReservationStage;
  292. }
  293. void CServerMsg_CheckReservation::Release()
  294. {
  295. if ( m_pParent )
  296. m_pParent->m_arrSvReservationCheck.FindAndFastRemove( this );
  297. delete this;
  298. }
  299. void CServerMsg_CheckReservation::SendMsg( const ns_address &serverAdr, int socket, uint32 token )
  300. {
  301. // send the reservation message
  302. char buffer[64];
  303. bf_write msg(buffer,sizeof(buffer));
  304. msg.WriteLong( CONNECTIONLESS_HEADER );
  305. msg.WriteByte( A2S_RESERVE_CHECK );
  306. msg.WriteLong( GetHostVersion() );
  307. msg.WriteLong( token );
  308. msg.WriteLong( m_uiReservationStage );
  309. msg.WriteLongLong( m_reservationCookie );
  310. #ifndef SWDS
  311. msg.WriteLongLong( Steam3Client().SteamUser()->GetSteamID().ConvertToUint64() );
  312. #else
  313. msg.WriteLongLong( 0 );
  314. #endif
  315. #ifndef DEDICATED
  316. if ( serverAdr.GetAddressType() == NSAT_PROXIED_GAMESERVER )
  317. NET_InitSteamDatagramProxiedGameserverConnection( serverAdr );
  318. #endif
  319. NET_SendPacket( NULL, socket, serverAdr, msg.GetData(), msg.GetNumBytesWritten() );
  320. }
  321. void CServerMsg_CheckReservation::ResponseReceived( const ns_address &from, bf_read &msg, int32 hostVersion, uint32 token )
  322. {
  323. if ( hostVersion != GetHostVersion() )
  324. return;
  325. if ( !IsValidResponse( from, token ) )
  326. return;
  327. uint32 uiReservationStage = msg.ReadLong();
  328. int numPlayersAwaiting = msg.ReadByte();
  329. if ( numPlayersAwaiting == 0 )
  330. {
  331. DevMsg( "Server confirmed all players reservation%u\n", uiReservationStage );
  332. CServerMsg::ResponseReceived( numPlayersAwaiting );
  333. }
  334. else
  335. {
  336. DevMsg( "Server reservation%u is awaiting %d\n", uiReservationStage, numPlayersAwaiting );
  337. if ( 0x7F == numPlayersAwaiting )
  338. {
  339. // Failed to receive a reply
  340. m_eState = AOS_FAILED;
  341. m_pCallback->OnOperationFinished( this );
  342. }
  343. else
  344. {
  345. m_result = numPlayersAwaiting;
  346. m_pCallback->OnOperationFinished( this ); // we remain in AOS_RUNNING, just notify the callback
  347. }
  348. }
  349. }
  350. CServerMsg_Ping::CServerMsg_Ping( CBaseClientState *pParent, IMatchAsyncOperationCallback *pCallback, const ns_address &serverAdr, int socket ) :
  351. CServerMsg( pParent, pCallback, serverAdr, socket, 3, 5.0 )
  352. {
  353. m_timeLastMsgSent = 0.0;
  354. }
  355. void CServerMsg_Ping::Release()
  356. {
  357. if ( m_pParent )
  358. m_pParent->m_arrSvPing.FindAndFastRemove( this );
  359. delete this;
  360. }
  361. void CServerMsg_Ping::SendMsg( const ns_address &serverAdr, int socket, uint32 token )
  362. {
  363. m_timeLastMsgSent = net_time;
  364. char buffer[64];
  365. bf_write msg(buffer,sizeof(buffer));
  366. msg.WriteLong( CONNECTIONLESS_HEADER );
  367. msg.WriteByte( A2S_PING );
  368. msg.WriteLong( GetHostVersion() );
  369. msg.WriteLong( token );
  370. #ifndef DEDICATED
  371. if ( serverAdr.GetAddressType() == NSAT_PROXIED_GAMESERVER )
  372. NET_InitSteamDatagramProxiedGameserverConnection( serverAdr );
  373. #endif
  374. DevMsg( "Pinging %s\n", ns_address_render( serverAdr ).String() );
  375. NET_SendPacket( NULL, socket, serverAdr, msg.GetData(), msg.GetNumBytesWritten() );
  376. }
  377. void CServerMsg_Ping::ResponseReceived( const ns_address& from, bf_read &msg, int32 hostVersion, uint32 token )
  378. {
  379. if ( hostVersion != GetHostVersion() )
  380. return;
  381. if ( !IsValidResponse( from, token ) )
  382. return;
  383. double dt = net_time - m_timeLastMsgSent;
  384. uint64 result = (uint64)( dt * 1000 );
  385. CServerMsg::ResponseReceived( result );
  386. }
  387. // ---------------------------------------------------------------------------------------- //
  388. // C_ServerClassInfo implementation.
  389. // ---------------------------------------------------------------------------------------- //
  390. CBaseClientState::CBaseClientState() :
  391. m_BaselineHandles( DefLessFunc( int ) )
  392. {
  393. m_bSplitScreenUser = false;
  394. m_Socket = NS_CLIENT;
  395. m_pServerClasses = NULL;
  396. m_StringTableContainer = NULL;
  397. m_NetChannel = NULL;
  398. m_nSignonState = SIGNONSTATE_NONE;
  399. m_nChallengeNr = 0;
  400. m_flConnectTime = 0;
  401. m_nRetryNumber = 0;
  402. m_nRetryMax = CL_CONNECTION_RETRIES;
  403. m_nServerCount = 0;
  404. m_nCurrentSequence = 0;
  405. m_nDeltaTick = 0;
  406. m_bPaused = 0;
  407. m_nViewEntity = 0;
  408. m_nPlayerSlot = 0;
  409. m_nSplitScreenSlot = 0;
  410. m_nMaxClients = 0;
  411. m_nNumPlayersToConnect = 1;
  412. Q_memset( m_pEntityBaselines, 0, sizeof( m_pEntityBaselines ) );
  413. m_nServerClasses = 0;
  414. m_nServerClassBits = 0;
  415. m_ListenServerSteamID = 0ull;
  416. m_flNextCmdTime = -1.0f;
  417. Q_memset( m_szLevelName, 0, sizeof( m_szLevelName ) );
  418. Q_memset( m_szLevelNameShort, 0, sizeof( m_szLevelNameShort ) );
  419. Q_memset( m_szLastLevelNameShort, 0, sizeof( m_szLevelNameShort ) );
  420. m_iEncryptionKeySize = 0;
  421. Q_memset( m_szEncryptionKey, 0, sizeof( m_szEncryptionKey ) );
  422. m_bRestrictServerCommands = true;
  423. m_bRestrictClientCommands = true;
  424. m_bServerConnectionRedirect = false;
  425. m_bServerInfoProcessed = false;
  426. m_nServerProtocolVersion = 0;
  427. m_nServerInfoMsgProtocol = 0;
  428. m_pServerReservationOperation = NULL;
  429. m_pServerReservationCallback = NULL;
  430. m_flReservationMsgSendTime = 0;
  431. m_nReservationMsgRetryNumber = 0;
  432. m_bEnteredPassword = false;
  433. m_bWaitingForPassword = false;
  434. #if ENGINE_CONNECT_VIA_MMS
  435. m_bWaitingForServerGameDetails = false;
  436. #endif
  437. m_nServerReservationCookie = 0;
  438. m_pKVGameSettings = NULL;
  439. m_unUGCMapFileID = 0;
  440. m_ulGameServerSteamID = 0;
  441. }
  442. CBaseClientState::~CBaseClientState()
  443. {
  444. if ( m_pKVGameSettings )
  445. {
  446. m_pKVGameSettings->deleteThis();
  447. m_pKVGameSettings = NULL;
  448. }
  449. FOR_EACH_MAP( m_BaselineHandles, i )
  450. {
  451. g_pSerializedEntities->ReleaseSerializedEntity( m_BaselineHandles[ i ] );
  452. }
  453. m_BaselineHandles.RemoveAll();
  454. }
  455. void CBaseClientState::Clear( void )
  456. {
  457. m_nServerCount = -1;
  458. m_nDeltaTick = -1;
  459. m_ClockDriftMgr.Clear();
  460. m_nCurrentSequence = 0;
  461. m_nServerClasses = 0;
  462. m_nServerClassBits = 0;
  463. m_nPlayerSlot = 0;
  464. m_nSplitScreenSlot = 0;
  465. m_szLevelName[0] = 0;
  466. m_nMaxClients = 0;
  467. m_unUGCMapFileID = 0;
  468. // m_nNumPlayersToConnect = 1; <-- when clearing state we need to preserve num players to properly "reconnect" to the server
  469. // Need to cache off the name of the current map before clearing it so we can use when doing a memory flush.
  470. if ( m_szLevelNameShort[0] )
  471. {
  472. V_strncpy( m_szLastLevelNameShort, m_szLevelNameShort, sizeof( m_szLastLevelNameShort ) );
  473. }
  474. m_szLevelNameShort[ 0 ] = 0;
  475. if ( m_pServerClasses )
  476. {
  477. delete[] m_pServerClasses;
  478. m_pServerClasses = NULL;
  479. }
  480. if ( m_StringTableContainer )
  481. {
  482. #ifndef SHARED_NET_STRING_TABLES
  483. m_StringTableContainer->RemoveAllTables();
  484. #endif
  485. m_StringTableContainer = NULL;
  486. }
  487. FreeEntityBaselines();
  488. if ( !m_bSplitScreenUser )
  489. {
  490. RecvTable_Term( false );
  491. }
  492. if ( m_NetChannel )
  493. m_NetChannel->Reset();
  494. m_bPaused = 0;
  495. m_nViewEntity = 0;
  496. m_nChallengeNr = 0;
  497. m_flConnectTime = 0.0f;
  498. m_bServerInfoProcessed = false;
  499. m_nServerProtocolVersion = 0;
  500. m_nServerInfoMsgProtocol = 0;
  501. // Free all avatar data
  502. m_mapPlayerAvatarData.PurgeAndDeleteElements();
  503. }
  504. void CBaseClientState::FileReceived( const char * fileName, unsigned int transferID, bool bIsReplayDemoFile )
  505. {
  506. ConMsg( "CBaseClientState::FileReceived: %s.\n", fileName );
  507. #if defined( REPLAY_ENABLED )
  508. if ( isReplayDemoFile )
  509. {
  510. CClientReplayHistoryEntryData *pEntry = static_cast< CClientReplayHistoryEntryData *>( g_pClientReplayHistoryManager->FindEntry( fileName ) ); Assert( pEntry );
  511. if ( pEntry )
  512. {
  513. pEntry->m_bTransferComplete = true;
  514. g_pClientReplayHistoryManager->FlushEntriesToDisk();
  515. }
  516. }
  517. #endif
  518. }
  519. void CBaseClientState::FileDenied(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile )
  520. {
  521. ConMsg( "CBaseClientState::FileDenied: %s.\n", fileName );
  522. }
  523. void CBaseClientState::FileRequested(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile )
  524. {
  525. ConMsg( "File '%s' requested from %s.\n", fileName, m_NetChannel->GetAddress() );
  526. m_NetChannel->SendFile( fileName, transferID, bIsReplayDemoFile ); // CBaseClientState always sends file
  527. }
  528. void CBaseClientState::FileSent(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile )
  529. {
  530. ConMsg( "File '%s' sent.\n", fileName );
  531. }
  532. void CBaseClientState::ConnectionStart(INetChannel *chan)
  533. {
  534. m_NETMsgTick.Bind< CNETMsg_Tick_t >( chan, UtlMakeDelegate( this, &CBaseClientState::NETMsg_Tick ) );
  535. m_NETMsgStringCmd.Bind< CNETMsg_StringCmd_t >( chan, UtlMakeDelegate( this, &CBaseClientState::NETMsg_StringCmd ) );
  536. m_NETMsgSignonState.Bind< CNETMsg_SignonState_t >( chan, UtlMakeDelegate( this, &CBaseClientState::NETMsg_SignonState ) );
  537. m_NETMsgSetConVar.Bind< CNETMsg_SetConVar_t >( chan, UtlMakeDelegate( this, &CBaseClientState::NETMsg_SetConVar ) );
  538. m_NETMsgPlayerAvatarData.Bind< CNETMsg_PlayerAvatarData_t >( chan, UtlMakeDelegate( this, &CBaseClientState::NETMsg_PlayerAvatarData ) );
  539. m_SVCMsgServerInfo.Bind< CSVCMsg_ServerInfo_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_ServerInfo ) );
  540. m_SVCMsgClassInfo.Bind< CSVCMsg_ClassInfo_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_ClassInfo ) );
  541. m_SVCMsgSendTable.Bind< CSVCMsg_SendTable_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_SendTable ) );
  542. m_SVCMsgCmdKeyValues.Bind< CSVCMsg_CmdKeyValues_t>( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_CmdKeyValues ) );
  543. m_SVCMsg_EncryptedData.Bind< CSVCMsg_EncryptedData_t>( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_EncryptedData ) );
  544. m_SVCMsgPrint.Bind< CSVCMsg_Print_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_Print ) );
  545. m_SVCMsgSetPause.Bind< CSVCMsg_SetPause_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_SetPause ) );
  546. m_SVCMsgSetView.Bind< CSVCMsg_SetView_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_SetView ) );
  547. m_SVCMsgCreateStringTable.Bind< CSVCMsg_CreateStringTable_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_CreateStringTable ) );
  548. m_SVCMsgUpdateStringTable.Bind< CSVCMsg_UpdateStringTable_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_UpdateStringTable ) );
  549. m_SVCMsgVoiceInit.Bind< CSVCMsg_VoiceInit_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_VoiceInit ) );
  550. m_SVCMsgVoiceData.Bind< CSVCMsg_VoiceData_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_VoiceData ) );
  551. m_SVCMsgFixAngle.Bind< CSVCMsg_FixAngle_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_FixAngle ) );
  552. m_SVCMsgPrefetch.Bind< CSVCMsg_Prefetch_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_Prefetch ) );
  553. m_SVCMsgCrosshairAngle.Bind< CSVCMsg_CrosshairAngle_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_CrosshairAngle ) );
  554. m_SVCMsgBSPDecal.Bind< CSVCMsg_BSPDecal_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_BSPDecal ) );
  555. m_SVCMsgSplitScreen.Bind< CSVCMsg_SplitScreen_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_SplitScreen ) );
  556. m_SVCMsgGetCvarValue.Bind< CSVCMsg_GetCvarValue_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_GetCvarValue ) );
  557. m_SVCMsgMenu.Bind< CSVCMsg_Menu_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_Menu ) );
  558. m_SVCMsgUserMessage.Bind< CSVCMsg_UserMessage_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_UserMessage ) );
  559. m_SVCMsgPaintmapData.Bind< CSVCMsg_PaintmapData_t >( chan, UtlMakeDelegate(this, &CBaseClientState::SVCMsg_PaintmapData ) );
  560. m_SVCMsgGameEvent.Bind< CSVCMsg_GameEvent_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_GameEvent ) );
  561. m_SVCMsgGameEventList.Bind< CSVCMsg_GameEventList_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_GameEventList ) );
  562. m_SVCMsgTempEntities.Bind< CSVCMsg_TempEntities_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_TempEntities ) );
  563. m_SVCMsgPacketEntities.Bind< CSVCMsg_PacketEntities_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_PacketEntities ) );
  564. m_SVCMsgSounds.Bind< CSVCMsg_Sounds_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_Sounds ) );
  565. m_SVCMsgEntityMsg.Bind< CSVCMsg_EntityMsg_t >( chan, UtlMakeDelegate( this, &CBaseClientState::SVCMsg_EntityMsg ) );
  566. }
  567. void CBaseClientState::ConnectionStop( )
  568. {
  569. m_NETMsgTick.Unbind();
  570. m_NETMsgStringCmd.Unbind();
  571. m_NETMsgSignonState.Unbind();
  572. m_NETMsgSetConVar.Unbind();
  573. m_NETMsgPlayerAvatarData.Unbind();
  574. m_SVCMsgServerInfo.Unbind();
  575. m_SVCMsgClassInfo.Unbind();
  576. m_SVCMsgSendTable.Unbind();
  577. m_SVCMsgCmdKeyValues.Unbind();
  578. m_SVCMsg_EncryptedData.Unbind();
  579. m_SVCMsgPrint.Unbind();
  580. m_SVCMsgSetPause.Unbind();
  581. m_SVCMsgSetView.Unbind();
  582. m_SVCMsgCreateStringTable.Unbind();
  583. m_SVCMsgUpdateStringTable.Unbind();
  584. m_SVCMsgVoiceInit.Unbind();
  585. m_SVCMsgVoiceData.Unbind();
  586. m_SVCMsgFixAngle.Unbind();
  587. m_SVCMsgPrefetch.Unbind();
  588. m_SVCMsgCrosshairAngle.Unbind();
  589. m_SVCMsgBSPDecal.Unbind();
  590. m_SVCMsgSplitScreen.Unbind();
  591. m_SVCMsgGetCvarValue.Unbind();
  592. m_SVCMsgMenu.Unbind();
  593. m_SVCMsgUserMessage.Unbind();
  594. m_SVCMsgPaintmapData.Unbind();
  595. m_SVCMsgGameEvent.Unbind();
  596. m_SVCMsgGameEventList.Unbind();
  597. m_SVCMsgTempEntities.Unbind();
  598. m_SVCMsgPacketEntities.Unbind();
  599. m_SVCMsgSounds.Unbind();
  600. m_SVCMsgEntityMsg.Unbind();
  601. }
  602. void CBaseClientState::ConnectionClosing( const char *reason )
  603. {
  604. ConMsg( "Disconnect: %s.\n", reason?reason:"unknown reason" );
  605. Disconnect();
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: A svc_signonnum has been received, perform a client side setup
  609. // Output : void CL_SignonReply
  610. //-----------------------------------------------------------------------------
  611. bool CBaseClientState::SetSignonState ( int state, int count, const CNETMsg_SignonState *msg )
  612. {
  613. // ConDMsg ("CL_SignonReply: %i\n", GetBaseLocalClient().signon);
  614. if ( state < SIGNONSTATE_NONE || state > SIGNONSTATE_CHANGELEVEL )
  615. {
  616. ConMsg ("Received signon %i when at %i\n", state, m_nSignonState );
  617. Assert( 0 );
  618. return false;
  619. }
  620. if ( (state > SIGNONSTATE_CONNECTED) && (state <= m_nSignonState) && !m_NetChannel->IsPlayback() )
  621. {
  622. ConMsg ("Received signon %i when at %i\n", state, m_nSignonState);
  623. Assert( 0 );
  624. return false;
  625. }
  626. if ( (count != m_nServerCount) && (count != -1) && (m_nServerCount != -1) && !m_NetChannel->IsPlayback() )
  627. {
  628. ConMsg ("Received wrong spawn count %i when at %i\n", count, m_nServerCount );
  629. Assert( 0 );
  630. return false;
  631. }
  632. if ( m_nSignonState < SIGNONSTATE_CONNECTED && state >= SIGNONSTATE_CONNECTED )
  633. {
  634. // Reset direct connect lobby once client is in game
  635. m_DirectConnectLobby = DirectConnectLobby_t();
  636. // Reset all client-generated keys that are too old
  637. if ( m_mapGeneratedEncryptionKeys.Count() > 300 )
  638. {
  639. int numPurge = 300 - m_mapGeneratedEncryptionKeys.Count();
  640. numPurge = MIN( numPurge, 3000 );
  641. while ( numPurge -- > 0 )
  642. {
  643. int32 idxOldestKey = m_mapGeneratedEncryptionKeys.FirstInorder();
  644. delete [] m_mapGeneratedEncryptionKeys.Element( idxOldestKey );
  645. m_mapGeneratedEncryptionKeys.RemoveAt( idxOldestKey );
  646. }
  647. }
  648. }
  649. m_nSignonState = state;
  650. return true;
  651. }
  652. //////////////////////////////////////////////////////////////////////////
  653. //
  654. // 3rd party plugins managed encryption keys map
  655. //
  656. static CUtlStringMap< CUtlBuffer * > g_mapServersToCertificates;
  657. void RegisterServerCertificate( char const *szServerAddress, int numBytesPayload, void const *pvPayload )
  658. {
  659. // Allocate new storage
  660. CUtlBuffer *pNew = new CUtlBuffer;
  661. pNew->EnsureCapacity( numBytesPayload );
  662. pNew->SeekPut( CUtlBuffer::SEEK_HEAD, numBytesPayload );
  663. V_memcpy( pNew->Base(), pvPayload, numBytesPayload );
  664. UtlSymId_t symid = g_mapServersToCertificates.Find( szServerAddress );
  665. if ( symid != UTL_INVAL_SYMBOL )
  666. {
  667. delete g_mapServersToCertificates[ symid ];
  668. g_mapServersToCertificates[ symid ] = pNew;
  669. }
  670. else
  671. {
  672. g_mapServersToCertificates.Insert( szServerAddress, pNew );
  673. }
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: called by CL_Connect and CL_CheckResend
  677. // If we are in ca_connecting state and we have gotten a challenge
  678. // response before the timeout, send another "connect" request.
  679. // Output : void CL_SendConnectPacket
  680. //-----------------------------------------------------------------------------
  681. void CBaseClientState::SendConnectPacket ( const ns_address &netAdrRemote, int challengeNr, int authProtocol, uint64 unGSSteamID, bool bGSSecure )
  682. {
  683. COM_TimestampedLog( "SendConnectPacket" );
  684. if ( !netAdrRemote.IsLoopback() )
  685. {
  686. bool bFound = m_Remote.IsAddressInList( netAdrRemote );
  687. if ( !bFound )
  688. {
  689. Warning( "Sending connect packet to unexpected address %s\n", ns_address_render( netAdrRemote ).String() );
  690. }
  691. }
  692. const char *CDKey = "NOCDKEY";
  693. char msg_buffer[MAX_ROUTABLE_PAYLOAD * 2];
  694. bf_write msg( msg_buffer, sizeof(msg_buffer) );
  695. msg.WriteLong( CONNECTIONLESS_HEADER );
  696. msg.WriteByte( C2S_CONNECT );
  697. msg.WriteLong( m_nServerProtocolVersion ? m_nServerProtocolVersion : GetHostVersion() ); // fake to the server as the client with matching version, validate later
  698. msg.WriteLong( authProtocol );
  699. msg.WriteLong( challengeNr );
  700. // msg.WriteString( GetClientName() ); // Name
  701. msg.WriteString( "" ); // Server can find the name in FCVAR_USERINFO block, save on connectionless packet size
  702. msg.WriteString( password.GetString() ); // password
  703. //Send player info for main player and split screen players
  704. msg.WriteByte( m_nNumPlayersToConnect );
  705. int numBytesPacketHeader = msg.GetNumBytesWritten();
  706. for( int playerCount = 0; playerCount < m_nNumPlayersToConnect; ++playerCount )
  707. {
  708. CCLCMsg_SplitPlayerConnect_t splitMsg;
  709. Host_BuildUserInfoUpdateMessage( playerCount, splitMsg.mutable_convars(), false );
  710. if ( CHLTVClientState *pHLTVClientState = dynamic_cast< CHLTVClientState * >( this ) )
  711. {
  712. pHLTVClientState->SetLocalInfoConvarsForUpstreamConnection( *splitMsg.mutable_convars(), true );
  713. }
  714. #ifdef _DEBUG
  715. for ( int ii = 0; ii < splitMsg.convars().cvars_size(); ++ ii )
  716. {
  717. CMsg_CVars::CVar cvinfo( splitMsg.convars().cvars( ii ) );
  718. NetMsgExpandCVarUsingDictionary( &cvinfo );
  719. DevMsg( "[NET] connect user info: '%s' = '%s'\n", cvinfo.name().c_str(), cvinfo.value().c_str() );
  720. }
  721. #endif
  722. splitMsg.WriteToBuffer( msg );
  723. }
  724. int numBytesFcvarUserInfo = msg.GetNumBytesWritten() - numBytesPacketHeader;
  725. // Track cookie and certificate
  726. int numBytesCookie = msg.GetNumBytesWritten();
  727. // add the low violence setting
  728. msg.WriteOneBit( g_bLowViolence );
  729. // add the server reservation cookie, if we have one
  730. msg.WriteLongLong( m_nServerReservationCookie );
  731. msg.WriteByte( (uint8)CROSSPLAYPLATFORM_THISPLATFORM );
  732. //
  733. // write the client encryption key to be used
  734. //
  735. DeferredConnection_t &dc = m_DeferredConnection;
  736. if ( dc.m_nEncryptionKey )
  737. {
  738. msg.WriteLong( dc.m_nEncryptionKey );
  739. byte *pbEncryptionKey = NULL;
  740. int32 idx = m_mapGeneratedEncryptionKeys.Find( dc.m_nEncryptionKey );
  741. if ( idx != m_mapGeneratedEncryptionKeys.InvalidIndex() )
  742. {
  743. pbEncryptionKey = m_mapGeneratedEncryptionKeys.Element( idx );
  744. }
  745. else
  746. {
  747. dc.m_nEncryptedSize = 0;
  748. }
  749. msg.WriteLong( dc.m_nEncryptedSize );
  750. if ( dc.m_nEncryptedSize )
  751. msg.WriteBytes( pbEncryptionKey + NET_CRYPT_KEY_LENGTH, dc.m_nEncryptedSize );
  752. }
  753. else
  754. {
  755. // Try to see if there's a client-plugin override for the encryption key for this server?
  756. bool bWriteZeroEncryptionKey = true;
  757. ns_address_render renderRemoteAsString( netAdrRemote );
  758. UtlSymId_t utlKey = g_mapServersToCertificates.Find( renderRemoteAsString.String() );
  759. if ( utlKey != UTL_INVAL_SYMBOL )
  760. {
  761. CUtlBuffer &buf = *g_mapServersToCertificates[ utlKey ];
  762. const int32 numMetadataBytes = sizeof( int32 ) + NET_CRYPT_KEY_LENGTH;
  763. if ( buf.TellPut() > numMetadataBytes )
  764. {
  765. bWriteZeroEncryptionKey = false;
  766. msg.WriteLong( *reinterpret_cast< int32 * >( buf.Base() ) );
  767. int32 numEncryptedSize = buf.TellPut() - numMetadataBytes;
  768. msg.WriteLong( numEncryptedSize );
  769. msg.WriteBytes( ( const char * )( buf.Base() ) + numMetadataBytes, numEncryptedSize );
  770. }
  771. }
  772. if ( bWriteZeroEncryptionKey )
  773. {
  774. msg.WriteLong( 0 );
  775. }
  776. }
  777. numBytesCookie = msg.GetNumBytesWritten() - numBytesCookie;
  778. int numBytesSteamAuth = msg.GetNumBytesWritten();
  779. switch ( authProtocol )
  780. {
  781. // Fall through, bogus protocol type, use CD key hash.
  782. case PROTOCOL_HASHEDCDKEY: CDKey = GetCDKeyHash();
  783. msg.WriteString( CDKey ); // cdkey
  784. break;
  785. case PROTOCOL_STEAM: if ( !PrepareSteamConnectResponse( unGSSteamID, bGSSecure, netAdrRemote, msg ) )
  786. {
  787. return;
  788. }
  789. break;
  790. default: Host_Error( "Unexepected authentication protocol %i!\n", authProtocol );
  791. return;
  792. }
  793. numBytesSteamAuth = msg.GetNumBytesWritten() - numBytesSteamAuth;
  794. // Mark time of this attempt for retransmit requests
  795. m_flConnectTime = net_time;
  796. // remember challengenr for TCP connection
  797. m_nChallengeNr = challengeNr;
  798. // Send protocol and challenge value
  799. if ( msg.GetNumBytesWritten() > 896 )
  800. {
  801. Warning( "[NET] Client connect packet too large for %s, total size %u bytes ( %u header, %u info, %u cookie, %u auth )\n", ns_address_render( netAdrRemote ).String(), msg.GetNumBytesWritten(),
  802. numBytesPacketHeader, numBytesFcvarUserInfo, numBytesCookie, numBytesSteamAuth );
  803. Assert( 0 );
  804. }
  805. else
  806. {
  807. DevMsg( "[NET] Sending client connect packet to %s, total size %u bytes ( %u header, %u info, %u cookie, %u auth )\n", ns_address_render( netAdrRemote ).String(), msg.GetNumBytesWritten(),
  808. numBytesPacketHeader, numBytesFcvarUserInfo, numBytesCookie, numBytesSteamAuth );
  809. }
  810. NET_SendPacket( NULL, m_Socket, netAdrRemote, msg.GetData(), msg.GetNumBytesWritten() );
  811. // Remember Steam ID, if any
  812. m_ulGameServerSteamID = unGSSteamID;
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose: append steam specific data to a connection response
  816. //-----------------------------------------------------------------------------
  817. bool CBaseClientState::PrepareSteamConnectResponse( uint64 unGSSteamID, bool bGSSecure, const ns_address &adr, bf_write &msg )
  818. {
  819. // X360TBD: Network - Steam Dedicated Server hack
  820. if ( IsX360() )
  821. {
  822. return true;
  823. }
  824. #if !defined( NO_STEAM ) && !defined( DEDICATED )
  825. if ( !Steam3Client().SteamUser() )
  826. {
  827. COM_ExplainDisconnection( true, "The server requires that you be running Steam.\n" );
  828. Disconnect();
  829. return false;
  830. }
  831. #endif
  832. #ifndef DEDICATED
  833. // now append the steam3 cookie
  834. char steam3Cookie[ STEAM_KEYSIZE ];
  835. uint32 steam3CookieLen;
  836. Steam3Client().GetAuthSessionTicket( steam3Cookie, sizeof(steam3Cookie), &steam3CookieLen, unGSSteamID, bGSSecure );
  837. msg.WriteShort( steam3CookieLen );
  838. if ( steam3CookieLen > 0 )
  839. msg.WriteBytes( steam3Cookie, steam3CookieLen );
  840. #endif
  841. return true;
  842. }
  843. bool Remote_t::Resolve()
  844. {
  845. if ( !m_adrRemote.SetFromString( m_szRetryAddress ) )
  846. {
  847. return false;
  848. }
  849. if ( m_adrRemote.IsType<netadr_t>() && m_adrRemote.AsType<netadr_t>().GetPort() == 0 )
  850. {
  851. m_adrRemote.AsType<netadr_t>().SetPort( PORT_SERVER );
  852. }
  853. return true;
  854. }
  855. bool CAddressList::IsRemoteInList( char const *pchAdrCheck ) const
  856. {
  857. for ( int i = 0; i < m_List.Count(); ++i )
  858. {
  859. if ( !Q_stricmp( pchAdrCheck, Get( i ).m_szRetryAddress.String() ) )
  860. return true;
  861. }
  862. return false;
  863. }
  864. bool CAddressList::IsAddressInList( const ns_address &adr ) const
  865. {
  866. for ( int i = 0; i < m_List.Count(); ++i )
  867. {
  868. if ( adr.CompareAdr( Get( i ).m_adrRemote ) )
  869. return true;
  870. }
  871. return false;
  872. }
  873. void CAddressList::RemoveAll()
  874. {
  875. m_List.RemoveAll();
  876. }
  877. void CAddressList::Describe( CUtlString &str )
  878. {
  879. for ( int i = 0; i < m_List.Count(); ++i )
  880. {
  881. str += va( "%s(%s) ", Get( i ).m_szAlias.String(), ns_address_render( Get( i ).m_adrRemote ).String() );
  882. }
  883. }
  884. int CAddressList::Count() const
  885. {
  886. return m_List.Count();
  887. }
  888. Remote_t &CAddressList::Get( int index )
  889. {
  890. Assert( index >= 0 && index < m_List.Count() );
  891. return m_List[ index ];
  892. }
  893. const Remote_t &CAddressList::Get( int index ) const
  894. {
  895. Assert( index >= 0 && index < m_List.Count() );
  896. return m_List[ index ];
  897. }
  898. void CAddressList::AddRemote( char const *pchAddress, char const *pchAlias )
  899. {
  900. if ( IsRemoteInList( pchAddress ) )
  901. return;
  902. Remote_t remote;
  903. remote.m_szRetryAddress = pchAddress;
  904. remote.m_szAlias = pchAlias;
  905. remote.Resolve();
  906. m_List.AddToTail( remote );
  907. }
  908. void CBaseClientState::ConnectInternal( const char *pchPublicAddress, char const *pchPrivateAddress, int numPlayers, const char* szJoinType )
  909. {
  910. #ifndef DEDICATED
  911. #if !defined( NO_STEAM )
  912. if ( !IsX360() ) // X360 matchmaking sets the forced user info values
  913. {
  914. // Get our name from steam. Needs to be done before connecting
  915. // because we won't have triggered a check by changing our name.
  916. IConVar *pVar = g_pCVar->FindVar( "name" );
  917. if ( pVar )
  918. {
  919. SetNameToSteamIDName( pVar );
  920. }
  921. }
  922. #endif
  923. #endif
  924. m_Remote.RemoveAll();
  925. m_Remote.AddRemote( pchPublicAddress, "public" );
  926. m_Remote.AddRemote( pchPrivateAddress, "private" );
  927. if ( ShouldUseDirectConnectAddress( m_Remote ) )
  928. {
  929. ConColorMsg( Color( 0, 255, 0, 255 ), "Adding direct connect address to connection %s\n", ns_address_render( m_DirectConnectLobby.m_adrRemote ).String() );
  930. m_Remote.AddRemote( ns_address_render( m_DirectConnectLobby.m_adrRemote ).String(), "direct" );
  931. }
  932. //standard connect always connects one players.
  933. m_nNumPlayersToConnect = numPlayers;
  934. // For the check for resend timer to fire a connection / getchallenge request.
  935. SetSignonState( SIGNONSTATE_CHALLENGE, -1, NULL );
  936. // Force connection request to fire.
  937. m_flConnectTime = -FLT_MAX;
  938. m_nRetryNumber = 0;
  939. // Retry for up to timeout seconds
  940. m_nRetryMax = cl_resend_timeout.GetFloat() / cl_resend.GetFloat();
  941. m_ulGameServerSteamID = 0;
  942. #if !defined ( DEDICATED )
  943. if ( szJoinType && g_ClientDLL )
  944. g_ClientDLL->RecordUIEvent( szJoinType );
  945. #endif
  946. }
  947. void CBaseClientState::Connect( const char *pchPublicAddress, char const *pchPrivateAddress, const char* szJoinType )
  948. {
  949. ConnectInternal( pchPublicAddress, pchPrivateAddress, 1, szJoinType );
  950. }
  951. void CBaseClientState::ConnectSplitScreen( const char *pchPublicAddress, char const *pchPrivateAddress, int numPlayers, const char* szJoinType )
  952. {
  953. ConnectInternal( pchPublicAddress, pchPrivateAddress, numPlayers, szJoinType );
  954. }
  955. INetworkStringTable *CBaseClientState::GetStringTable( const char * name ) const
  956. {
  957. if ( !m_StringTableContainer )
  958. {
  959. Assert( m_StringTableContainer );
  960. return NULL;
  961. }
  962. return m_StringTableContainer->FindTable( name );
  963. }
  964. void CBaseClientState::ForceFullUpdate( char const *pchReason )
  965. {
  966. if ( m_nDeltaTick == -1 )
  967. return;
  968. FreeEntityBaselines();
  969. m_nDeltaTick = -1;
  970. DevMsg( "Requesting full game update (%s)...\n", pchReason );
  971. }
  972. void CBaseClientState::FullConnect( const ns_address &adr, int nEncryptionKey )
  973. {
  974. // Initiate the network channel
  975. byte *pbEncryptionKey = NULL;
  976. if ( nEncryptionKey )
  977. {
  978. int32 idxEncryptedKey = m_mapGeneratedEncryptionKeys.Find( nEncryptionKey );
  979. if ( idxEncryptedKey != m_mapGeneratedEncryptionKeys.InvalidIndex() )
  980. {
  981. pbEncryptionKey = m_mapGeneratedEncryptionKeys.Element( idxEncryptedKey );
  982. }
  983. else
  984. {
  985. ns_address_render renderRemoteAsString( adr );
  986. UtlSymId_t utlKey = g_mapServersToCertificates.Find( renderRemoteAsString.String() );
  987. if ( utlKey != UTL_INVAL_SYMBOL )
  988. {
  989. CUtlBuffer &buf = *g_mapServersToCertificates[ utlKey ];
  990. const int32 numMetadataBytes = sizeof( int32 ) + NET_CRYPT_KEY_LENGTH;
  991. if ( buf.Size() > numMetadataBytes )
  992. {
  993. pbEncryptionKey = ( ( byte * ) ( buf.Base() ) + sizeof( int32 ) );
  994. }
  995. }
  996. }
  997. }
  998. COM_TimestampedLog( "CBaseClientState::FullConnect" );
  999. m_NetChannel = NET_CreateNetChannel( m_Socket, &adr, "CLIENT", this, pbEncryptionKey, false );
  1000. Assert( m_NetChannel );
  1001. m_NetChannel->StartStreaming( m_nChallengeNr ); // open TCP stream
  1002. // Bump connection time to now so we don't resend a connection
  1003. // Request
  1004. m_flConnectTime = net_time;
  1005. // We'll request a full delta from the baseline
  1006. m_nDeltaTick = -1;
  1007. // We can send a cmd right away
  1008. m_flNextCmdTime = net_time;
  1009. // If we used a server reservation cookie to connect, clear it
  1010. m_nServerReservationCookie = 0;
  1011. // Mark client as connected
  1012. SetSignonState( SIGNONSTATE_CONNECTED, -1, NULL );
  1013. #if !defined(DEDICATED)
  1014. ns_address rconAdr = m_NetChannel->GetRemoteAddress();
  1015. if ( rconAdr.IsType<netadr_t>() )
  1016. {
  1017. RCONClient().SetAddress( rconAdr.AsType<netadr_t>() );
  1018. }
  1019. #endif
  1020. }
  1021. void CBaseClientState::ConnectionCrashed(const char *reason)
  1022. {
  1023. ConMsg( "Connection lost: %s.\n", reason?reason:"unknown reason" );
  1024. Disconnect();
  1025. }
  1026. void CBaseClientState::Disconnect( bool bShowMainMenu )
  1027. {
  1028. m_DeferredConnection.m_bActive = false;
  1029. m_bWaitingForPassword = false;
  1030. #if ENGINE_CONNECT_VIA_MMS
  1031. m_bWaitingForServerGameDetails = false;
  1032. #endif
  1033. m_bEnteredPassword = false;
  1034. m_flConnectTime = -FLT_MAX;
  1035. m_nRetryNumber = 0;
  1036. m_ulGameServerSteamID = 0;
  1037. if ( m_nSignonState == SIGNONSTATE_NONE )
  1038. return;
  1039. #if !defined( DEDICATED ) && defined( ENABLE_RPT )
  1040. CL_NotifyRPTOfDisconnect( );
  1041. #endif
  1042. SetSignonState( SIGNONSTATE_NONE, -1, NULL );
  1043. // Don't clear cookie here as this can get called as part of connection process if changing to new server, etc.
  1044. // m_nServerReservationCookie = 0;
  1045. ns_address adr;
  1046. if ( m_NetChannel )
  1047. {
  1048. adr = m_NetChannel->GetRemoteAddress();
  1049. }
  1050. else if ( m_Remote.Count() > 0 )
  1051. {
  1052. const char *pszAddr = m_Remote.Get( 0 ).m_szRetryAddress;
  1053. if ( !adr.SetFromString( m_Remote.Get( 0 ).m_szRetryAddress ) )
  1054. {
  1055. Warning( "Unable to parse retry address '%s'\n", pszAddr );
  1056. }
  1057. }
  1058. #ifndef DEDICATED
  1059. ns_address checkAdr = adr;
  1060. if ( adr.IsLoopback() || adr.IsLocalhost() )
  1061. {
  1062. checkAdr.AsType<netadr_t>().SetIP( net_local_adr.GetIPHostByteOrder() );
  1063. }
  1064. if ( m_ListenServerSteamID != 0ull && m_Remote.Count() > 0 )
  1065. {
  1066. Assert( g_pSteamSocketMgr->GetSteamIDForRemote( m_Remote.Get( 0 ).m_adrRemote ) == m_ListenServerSteamID );
  1067. NET_TerminateSteamConnection( m_Socket, m_ListenServerSteamID );
  1068. m_ListenServerSteamID = 0ull;
  1069. }
  1070. Steam3Client().CancelAuthTicket();
  1071. #endif
  1072. if ( m_NetChannel )
  1073. {
  1074. m_NetChannel->Shutdown( "Disconnect" );
  1075. m_NetChannel = NULL;
  1076. }
  1077. #ifndef DEDICATED
  1078. // Get rid of any whitelist in our filesystem and reload any files that the previous whitelist forced
  1079. // to come from Steam.
  1080. // MD: This causes an annoying pause when you disconnect from a server, so just leave the last whitelist active
  1081. // until you connect to a new server.
  1082. //CL_HandlePureServerWhitelist( NULL );
  1083. #endif
  1084. #ifndef DEDICATED
  1085. if ( m_bSplitScreenUser &&
  1086. splitscreen->IsValidSplitScreenSlot( m_nSplitScreenSlot ) )
  1087. {
  1088. splitscreen->RemoveSplitScreenUser( m_nSplitScreenSlot, m_nPlayerSlot + 1 );
  1089. }
  1090. #if defined( INCLUDE_SCALEFORM )
  1091. if ( g_pScaleformUI )
  1092. {
  1093. g_pScaleformUI->ShutdownIME();
  1094. g_pScaleformUI->SlotRelease( SF_SS_SLOT( m_nSplitScreenSlot ) );
  1095. }
  1096. #endif
  1097. #endif // DEDICATED
  1098. }
  1099. void CBaseClientState::RunFrame (void)
  1100. {
  1101. VPROF("CBaseClientState::RunFrame");
  1102. if ( (m_nSignonState > SIGNONSTATE_NEW) && m_NetChannel && g_GameEventManager.HasClientListenersChanged() )
  1103. {
  1104. // assemble a list of all events we listening to and tell the server
  1105. CCLCMsg_ListenEvents_t msg;
  1106. g_GameEventManager.WriteListenEventList( &msg );
  1107. m_NetChannel->SendNetMsg( msg );
  1108. }
  1109. if ( m_nSignonState == SIGNONSTATE_CHALLENGE )
  1110. {
  1111. CheckForResend();
  1112. }
  1113. CheckForReservationResend();
  1114. for ( int i = 0; i < m_arrSvReservationCheck.Count(); ++ i )
  1115. {
  1116. CServerMsg_CheckReservation *pSv = m_arrSvReservationCheck[ i ];
  1117. Assert( pSv );
  1118. if ( pSv )
  1119. {
  1120. pSv->Update();
  1121. // Calling "Update" will potentially call handlers' interface async operation
  1122. // finished callback which can release "pSv" object and modify
  1123. // contents of our clientstate m_arrSvPing array. Always must check
  1124. // it again for being in array before attempting to dereference it.
  1125. // Also, it is ok to skip update on some ping objects when array changes
  1126. // since update is only responsible for re-sends of the pings
  1127. }
  1128. }
  1129. for ( int i = 0; i < m_arrSvReservationCheck.Count(); ++ i )
  1130. {
  1131. CServerMsg_CheckReservation *pSv = m_arrSvReservationCheck[ i ];
  1132. Assert( pSv );
  1133. if ( pSv && pSv->IsFinished() )
  1134. {
  1135. // delete pSvPing; -- client will release
  1136. m_arrSvReservationCheck.FastRemove( i -- );
  1137. }
  1138. }
  1139. for ( int iSvPing = 0; iSvPing < m_arrSvPing.Count(); ++ iSvPing )
  1140. {
  1141. CServerMsg_Ping *pSvPing = m_arrSvPing[ iSvPing ];
  1142. Assert( pSvPing );
  1143. if ( pSvPing )
  1144. {
  1145. pSvPing->Update();
  1146. // Calling "Update" will potentially call handlers' interface async operation
  1147. // finished callback which can release "pSvPing" object and modify
  1148. // contents of our clientstate m_arrSvPing array. Always must check
  1149. // it again for being in array before attempting to dereference it.
  1150. // Also, it is ok to skip update on some ping objects when array changes
  1151. // since update is only responsible for re-sends of the pings
  1152. }
  1153. }
  1154. for ( int iSvPing = 0; iSvPing < m_arrSvPing.Count(); ++ iSvPing )
  1155. {
  1156. CServerMsg_Ping *pSvPing = m_arrSvPing[ iSvPing ];
  1157. Assert( pSvPing );
  1158. if ( pSvPing && pSvPing->IsFinished() )
  1159. {
  1160. // delete pSvPing; -- client will release
  1161. m_arrSvPing.FastRemove( iSvPing -- );
  1162. }
  1163. }
  1164. }
  1165. /*
  1166. =================
  1167. ResetConnectionRetries
  1168. Reset the resend state so that the next call to CheckForResend()
  1169. will try. Call this after a listen server connects to Steam.
  1170. =================
  1171. */
  1172. void CBaseClientState::ResetConnectionRetries()
  1173. {
  1174. m_flConnectTime = -FLT_MAX;
  1175. m_nRetryNumber = 0;
  1176. }
  1177. /*
  1178. =================
  1179. CL_CheckForResend
  1180. Resend a connect message if the last one has timed out
  1181. =================
  1182. */
  1183. void CBaseClientState::CheckForResend ( bool bForceResendNow /* = false */ )
  1184. {
  1185. // resend if we haven't gotten a reply yet
  1186. // We only resend during the connection process.
  1187. if ( m_nSignonState != SIGNONSTATE_CHALLENGE )
  1188. return;
  1189. if ( m_bWaitingForPassword )
  1190. return;
  1191. // Wait at least the resend # of seconds.
  1192. if ( !bForceResendNow && ( ( net_time - m_flConnectTime ) < cl_resend.GetFloat() ) )
  1193. return;
  1194. // No addresses in list!
  1195. if ( m_Remote.Count() <= 0 )
  1196. {
  1197. Assert( 0 );
  1198. return;
  1199. }
  1200. for ( int i = 0; i < m_Remote.Count(); ++i )
  1201. {
  1202. if ( !m_Remote.Get( i ).Resolve() )
  1203. {
  1204. ConMsg( "Bad server address %s(%s)\n", m_Remote.Get( i ).m_szAlias.String(), m_Remote.Get( i ).m_szRetryAddress.String() );
  1205. Disconnect();
  1206. return;
  1207. }
  1208. }
  1209. // Only retry so many times before failure.
  1210. if ( m_nRetryNumber >= GetConnectionRetryNumber() )
  1211. {
  1212. COM_ExplainDisconnection( true, "Connection failed after %i retries.\n", GetConnectionRetryNumber() );
  1213. // Host_Disconnect();
  1214. Disconnect();
  1215. return;
  1216. }
  1217. // Mark time of this attempt.
  1218. m_flConnectTime = net_time; // for retransmit requests
  1219. // Display appropriate message
  1220. if ( !StringHasPrefix( m_Remote.Get( 0 ).m_szRetryAddress, "localhost" ) )
  1221. {
  1222. CUtlString desc;
  1223. m_Remote.Describe( desc );
  1224. ConMsg ("%s %s...\n", m_nRetryNumber == 0 ? "Connecting to" : "Retrying", desc.String() );
  1225. }
  1226. #ifndef DEDICATED
  1227. #if ENGINE_CONNECT_VIA_MMS
  1228. if ( m_bWaitingForServerGameDetails )
  1229. // This is a special handler for when we need to fetch server game details
  1230. // before we can connect to the server
  1231. {
  1232. for ( int i = 0; i < m_Remote.Count(); ++i )
  1233. {
  1234. Remote_t &remote = m_Remote.Get( i );
  1235. ResendGameDetailsRequest( remote.m_adrRemote );
  1236. }
  1237. ++m_nRetryNumber;
  1238. return;
  1239. }
  1240. #endif
  1241. #endif
  1242. char payload[ 128 ];
  1243. Q_snprintf( payload, sizeof( payload ), "%cconnect0x%08X", A2S_GETCHALLENGE, m_DeferredConnection.m_nChallenge );
  1244. // Request another challenge value.
  1245. ISteamSocketMgr::ESteamCnxType cnxType = g_pSteamSocketMgr->GetCnxType();
  1246. bool bShouldSendSteamRetry =
  1247. ( m_ListenServerSteamID != 0ull ) &&
  1248. ( m_nRetryNumber > 0 ) &&
  1249. !( m_nRetryNumber & 0x1 );
  1250. if ( m_ListenServerSteamID != 0ull &&
  1251. cnxType == ISteamSocketMgr::ESCT_ALWAYS )
  1252. {
  1253. bShouldSendSteamRetry = true;
  1254. }
  1255. if ( IsX360() )
  1256. bShouldSendSteamRetry = false;
  1257. else if ( m_ListenServerSteamID ) // PORTAL2-specific: force Steam cnx for P2P (todo: need latest Steam P2P APIs)
  1258. bShouldSendSteamRetry = true;
  1259. if ( !bShouldSendSteamRetry )
  1260. {
  1261. for ( int i = 0; i < m_Remote.Count(); ++i )
  1262. {
  1263. const Remote_t &remote = m_Remote.Get( i );
  1264. const char *pszProtocol = "[unknown protocol]";
  1265. char szAddress[128];
  1266. V_strcpy_safe( szAddress, ns_address_render( remote.m_adrRemote ).String() );
  1267. switch ( remote.m_adrRemote.GetAddressType() )
  1268. {
  1269. case NSAT_PROXIED_CLIENT: // we're connecting to a server, not a client
  1270. default:
  1271. Assert( false );
  1272. break;
  1273. case NSAT_NETADR:
  1274. pszProtocol = "UDP";
  1275. if ( cl_hideserverip.GetInt()>0 )
  1276. V_sprintf_safe( szAddress, "<hidden>" );
  1277. break;
  1278. case NSAT_P2P:
  1279. pszProtocol = "SteamP2P";
  1280. break;
  1281. case NSAT_PROXIED_GAMESERVER:
  1282. #ifdef DEDICATED
  1283. Assert( false );
  1284. #else
  1285. // Make sure we have a ticket, and are setup to talk to this guy
  1286. if ( !NET_InitSteamDatagramProxiedGameserverConnection( remote.m_adrRemote ) )
  1287. continue;
  1288. pszProtocol = "SteamDatagram";
  1289. #endif
  1290. break;
  1291. }
  1292. if ( developer.GetInt() != 0 )
  1293. {
  1294. ConColorMsg( Color( 0, 255, 0, 255 ), "%.3f: Sending connect to %s address %s via %s\n", net_time, remote.m_szAlias.String(), szAddress, pszProtocol );
  1295. }
  1296. NET_OutOfBandDelayedPrintf( m_Socket, remote.m_adrRemote, GetPrivateIPDelayMsecs() * i, payload, Q_strlen( payload ) + 1 );
  1297. }
  1298. }
  1299. else if ( m_ListenServerSteamID )
  1300. {
  1301. Msg( "%.3f: Sending Steam connect to %s %llx\n", net_time, m_Remote.Get( 0 ).m_szRetryAddress.String(), m_ListenServerSteamID );
  1302. m_Remote.Get( 0 ).m_adrRemote = NET_InitiateSteamConnection( m_Socket, m_ListenServerSteamID, payload, Q_strlen( payload ) + 1 );
  1303. }
  1304. else
  1305. {
  1306. Warning( "%.3f: Steam connection to unknown SteamId (%s) failed!\n", net_time, m_Remote.Get( 0 ).m_szRetryAddress.String() );
  1307. }
  1308. ++m_nRetryNumber;
  1309. }
  1310. void CBaseClientState::ResendGameDetailsRequest( const ns_address &adr )
  1311. {
  1312. #ifndef DEDICATED
  1313. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1314. "Client::ResendGameDetailsRequest", "to", ns_address_render( adr ).String() ) );
  1315. #endif
  1316. }
  1317. static void Read_S2A_INFO_SRC( const ns_address &from, bf_read *msg )
  1318. {
  1319. char str[ 1024 ];
  1320. Msg( "Responder : %s\n", ns_address_render( from ).String() );
  1321. // read protocol version
  1322. Msg( "Protocol : %d\n", (int)msg->ReadByte() );
  1323. msg->ReadString( str, sizeof( str ) );
  1324. Msg( "Hostname : %s\n", str );
  1325. msg->ReadString( str, sizeof( str ) );
  1326. Msg( "Map : %s\n", str );
  1327. msg->ReadString( str, sizeof( str ) );
  1328. Msg( "Game : %s\n", str );
  1329. msg->ReadString( str, sizeof( str ) );
  1330. Msg( "Description : %s\n", str );
  1331. Msg( "AppID : %u\n", (unsigned int)msg->ReadShort() );
  1332. Msg( "Players : %u\n", (unsigned int)msg->ReadByte() );
  1333. Msg( "MaxPlayers : %u\n", (unsigned int)msg->ReadByte() );
  1334. Msg( "Bots : %u\n", (unsigned int)msg->ReadByte() );
  1335. char const *sType = "???";
  1336. switch ( msg->ReadByte() )
  1337. {
  1338. default:
  1339. break;
  1340. case 'd':
  1341. sType = "dedicated";
  1342. break;
  1343. case 'p':
  1344. sType = "proxy";
  1345. break;
  1346. case 'l':
  1347. sType = "listen";
  1348. break;
  1349. }
  1350. Msg( "Server Type : %s\n", sType );
  1351. char const *osType = "???";
  1352. switch ( msg->ReadByte() )
  1353. {
  1354. default:
  1355. break;
  1356. case 'l':
  1357. osType = "Linux";
  1358. break;
  1359. case 'w':
  1360. osType = "Windows";
  1361. break;
  1362. }
  1363. Msg( "OS Type : %s\n", osType );
  1364. Msg( "Password : %s\n", msg->ReadByte() > 0 ? "yes" : "no" );
  1365. Msg( "Secure : %s\n", msg->ReadByte() > 0 ? "yes" : "no" );
  1366. msg->ReadString( str, sizeof( str ) );
  1367. Msg( "Version : %s\n", str );
  1368. if ( msg->GetNumBytesLeft() <= 0 )
  1369. return;
  1370. unsigned char infoByte = msg->ReadByte();
  1371. if ( infoByte & S2A_EXTRA_DATA_HAS_GAME_PORT )
  1372. {
  1373. Msg( "Game Port : %u\n", (unsigned short)msg->ReadShort() );
  1374. }
  1375. if ( infoByte & S2A_EXTRA_DATA_HAS_SPECTATOR_DATA )
  1376. {
  1377. Msg( "Spectator Port: %u\n", (unsigned short)msg->ReadShort() );
  1378. msg->ReadString( str, sizeof( str ) );
  1379. Msg( "SpectatorName : %s\n", str );
  1380. }
  1381. if ( infoByte & S2A_EXTRA_DATA_HAS_GAMETAG_DATA )
  1382. {
  1383. msg->ReadString( str, sizeof( str ) );
  1384. Msg( "Public Tags : %s\n", str );
  1385. }
  1386. }
  1387. bool CBaseClientState::ProcessConnectionlessPacket( netpacket_t *packet )
  1388. {
  1389. VPROF( "ProcessConnectionlessPacket" );
  1390. Assert( packet );
  1391. // NOTE: msg is a reference to packet->message, so reading
  1392. // from "msg" will also advance read-pointer in "packet->message"!!!
  1393. // ... and vice-versa, hence passing "packet" to a nested function
  1394. // will make that function receive a modified message with advanced
  1395. // read pointer.
  1396. // [ this differs from server-side connectionless packet processing ]
  1397. bf_read &msg = packet->message; // handy shortcut
  1398. bf_read msgOriginal = packet->message;
  1399. int c = msg.ReadByte();
  1400. char string[MAX_ROUTABLE_PAYLOAD];
  1401. // FIXME: For some of these, we should confirm that the sender of
  1402. // the message is what we think the server is...
  1403. switch ( c )
  1404. {
  1405. case S2C_CONNECTION:
  1406. if ( ( m_nSignonState == SIGNONSTATE_CHALLENGE ) &&
  1407. ( packet->from.CompareAdr(m_DeferredConnection.m_adrServerAddress, true ) ) &&
  1408. ( msg.ReadByte() == '.' ) )
  1409. {
  1410. char chEncryptionKeyIndex[9] = {};
  1411. for ( int j = 0; j < 8; ++ j )
  1412. chEncryptionKeyIndex[j] = msg.ReadByte();
  1413. bool bConnectionExpectingEncryptionKey = ( m_DeferredConnection.m_nEncryptionKey != 0 ) ||
  1414. ( g_mapServersToCertificates.Find( ns_address_render( packet->from ).String() ) != UTL_INVAL_SYMBOL );
  1415. int nEncryptionKeyIndex = 0;
  1416. if ( ( 1 == sscanf( chEncryptionKeyIndex, "%08X", &nEncryptionKeyIndex ) ) &&
  1417. ( ( nEncryptionKeyIndex != 0 ) == bConnectionExpectingEncryptionKey ) )
  1418. {
  1419. // server accepted our connection request
  1420. FullConnect( packet->from, nEncryptionKeyIndex );
  1421. }
  1422. }
  1423. break;
  1424. case S2C_CHALLENGE:
  1425. // Response from getchallenge we sent to the server we are connecting to
  1426. if ( packet->from.IsLocalhost() || packet->from.IsLoopback() || !cl_failremoteconnections.GetBool() )
  1427. {
  1428. DeferredConnection_t &dc = m_DeferredConnection;
  1429. dc.m_bActive = false;
  1430. dc.m_adrServerAddress = packet->from;
  1431. dc.m_nChallenge = msg.ReadLong();
  1432. dc.m_nAuthprotocol = msg.ReadLong();
  1433. dc.m_unGSSteamID = 0;
  1434. dc.m_bGSSecure = false;
  1435. if ( dc.m_nAuthprotocol == PROTOCOL_STEAM )
  1436. {
  1437. if ( msg.ReadShort() != 0 )
  1438. {
  1439. Msg("Invalid Steam key size.\n");
  1440. Disconnect();
  1441. return false;
  1442. }
  1443. dc.m_unGSSteamID = msg.ReadLongLong();
  1444. dc.m_bGSSecure = msg.ReadByte() ? true : false;
  1445. }
  1446. else
  1447. {
  1448. msg.ReadShort();
  1449. dc.m_unGSSteamID = msg.ReadLongLong(); // still read out game server SteamID for token validation
  1450. msg.ReadByte();
  1451. }
  1452. if ( msg.IsOverflowed() )
  1453. {
  1454. Msg( "Invalid challenge packet.\n" );
  1455. Disconnect();
  1456. return false;
  1457. }
  1458. // The host can disable access to secure servers if you load unsigned code (mods, plugins, hacks)
  1459. if ( dc.m_bGSSecure && !Host_IsSecureServerAllowed() )
  1460. {
  1461. m_netadrReserveServer.RemoveAll();
  1462. m_nServerReservationCookie = 0;
  1463. m_pServerReservationCallback = NULL;
  1464. #if !defined(DEDICATED)
  1465. g_pMatchFramework->CloseSession();
  1466. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnClientInsecureBlocked", "reason", "connect" ) );
  1467. #endif
  1468. Disconnect();
  1469. return false;
  1470. }
  1471. char context[ 256 ] = { 0 };
  1472. msg.ReadString( context, sizeof( context ) );
  1473. if ( StringHasPrefix( context, "reserve" ) )
  1474. {
  1475. HandleReserveServerChallengeResponse( m_DeferredConnection.m_nChallenge );
  1476. }
  1477. else if ( StringHasPrefix( context, "connect" ) )
  1478. {
  1479. // Blow it off if we are not connected.
  1480. if ( m_nSignonState != SIGNONSTATE_CHALLENGE )
  1481. {
  1482. return false;
  1483. }
  1484. dc.m_bActive = true;
  1485. int nProto = msg.ReadLong();
  1486. m_nServerProtocolVersion = nProto;
  1487. if ( nProto > GetHostVersion() ) // server is running newer version
  1488. {
  1489. Msg( "Server is running a newer version, client version %d, server version %d\n", GetHostVersion(), nProto );
  1490. Disconnect();
  1491. return false;
  1492. }
  1493. if ( nProto < GetHostVersion() ) // server is running older version
  1494. {
  1495. Msg( "Server is running an older version, client version %d, server version %d\n", GetHostVersion(), nProto );
  1496. Disconnect();
  1497. return false;
  1498. }
  1499. msg.ReadString( dc.m_chLobbyType, ARRAYSIZE( dc.m_chLobbyType ) - 1 );
  1500. dc.m_bRequiresPassword = ( msg.ReadByte() != 0 );
  1501. dc.m_unLobbyID = msg.ReadLongLong();
  1502. dc.m_bDCFriendsReqd = (msg.ReadByte() != 0);
  1503. dc.m_bOfficialValveServer = ( msg.ReadByte() != 0 );
  1504. // Generate an encryption key for this challenge
  1505. bool bEncryptedChannel = ( msg.ReadByte() != 0 );
  1506. if ( bEncryptedChannel )
  1507. {
  1508. byte chKeyPub[1024] = {};
  1509. byte chKeySgn[1024] = {};
  1510. int cbKeyPub = msg.ReadLong();
  1511. msg.ReadBytes( chKeyPub, cbKeyPub );
  1512. int cbKeySgn = msg.ReadLong();
  1513. msg.ReadBytes( chKeySgn, cbKeySgn );
  1514. if ( msg.IsOverflowed() )
  1515. {
  1516. Msg( "Invalid challenge packet.\n" );
  1517. Disconnect();
  1518. return false;
  1519. }
  1520. // Verify server certificate signature
  1521. byte *pbAllocatedKey = NULL;
  1522. int nAllocatedCryptoBlockSize = 0;
  1523. if ( !NET_CryptVerifyServerCertificateAndAllocateSessionKey( dc.m_bOfficialValveServer, dc.m_adrServerAddress,
  1524. chKeyPub, cbKeyPub, chKeySgn, cbKeySgn,
  1525. &pbAllocatedKey, &nAllocatedCryptoBlockSize ) || !pbAllocatedKey || !nAllocatedCryptoBlockSize )
  1526. {
  1527. delete [] pbAllocatedKey;
  1528. Msg( "Bad challenge signature.\n" );
  1529. Disconnect();
  1530. return false;
  1531. }
  1532. static int s_nGeneratedEncryptionKey = 0;
  1533. ++s_nGeneratedEncryptionKey;
  1534. if ( !s_nGeneratedEncryptionKey )
  1535. ++ s_nGeneratedEncryptionKey;
  1536. m_mapGeneratedEncryptionKeys.InsertOrReplace( s_nGeneratedEncryptionKey, pbAllocatedKey );
  1537. dc.m_nEncryptionKey = s_nGeneratedEncryptionKey;
  1538. dc.m_nEncryptedSize = nAllocatedCryptoBlockSize;
  1539. }
  1540. else
  1541. {
  1542. dc.m_nEncryptionKey = 0;
  1543. dc.m_nEncryptedSize = 0;
  1544. }
  1545. Msg( "Server using '%s' lobbies, requiring pw %s, lobby id %llx\n",
  1546. dc.m_chLobbyType[0] ? dc.m_chLobbyType : "<none>",
  1547. dc.m_bRequiresPassword ? "yes" : "no",
  1548. dc.m_unLobbyID );
  1549. #if ENGINE_CONNECT_VIA_MMS
  1550. // Check if server is denying dc. If it sent us a -1 then we have to use our own reservation id.
  1551. // This will work if we joined the right lobby, otherwise it will fail
  1552. if ( dc.m_unLobbyID == (uint64)(-1) )
  1553. {
  1554. // GSidhu - Detect the case when we are trying to direct connect - ensure the reservation
  1555. // cookie we are holding is reset to 0 between session
  1556. //if ( m_nServerReservationCookie == 0 )
  1557. //{
  1558. // COM_ExplainDisconnection( true, "Connecting to a Competitive mode game on a Valve CS:GO server is not allowed, use matchmaking instead.\n" );
  1559. // Disconnect();
  1560. // break;
  1561. //}
  1562. // else
  1563. {
  1564. dc.m_unLobbyID = m_nServerReservationCookie;
  1565. }
  1566. }
  1567. RememberIPAddressForLobby( dc.m_unLobbyID, dc.m_adrServerAddress );
  1568. #endif
  1569. if ( !dc.m_chLobbyType[0] && !dc.m_unLobbyID && dc.m_bRequiresPassword &&
  1570. ( !password.GetString()[ 0 ] ) )
  1571. {
  1572. // Stop resending challenges while PW dialog is up
  1573. m_bWaitingForPassword = true;
  1574. // Show PW UI with current string
  1575. #ifndef DEDICATED
  1576. SCR_EndLoadingPlaque();
  1577. EngineVGui()->ShowPasswordUI( password.GetString() );
  1578. #endif
  1579. }
  1580. else if ( dc.m_chLobbyType[0] && !sv.IsActive() && !dc.m_unLobbyID &&
  1581. m_nServerReservationCookie )
  1582. {
  1583. // Server protocol violation - client has reserved this server, but server
  1584. // replies that it requires lobbies and doesn't yet have a lobby ID
  1585. Warning( "Server error - failed to handle reservation request.\n" );
  1586. Disconnect();
  1587. return false;
  1588. }
  1589. else if ( StringHasPrefix( context, "connect-retry" ) &&
  1590. dc.m_chLobbyType[0] && !sv.IsActive() && !dc.m_unLobbyID )
  1591. // Server tells us that we need to issue another "connect" with a valid
  1592. // challenge, then it will reserve itself for a brief period to
  1593. // let us create the required lobby
  1594. {
  1595. Msg( "Grace request retry for unreserved server...\n" );
  1596. CheckForResend( true ); // force a resend with the correct challenge nr
  1597. }
  1598. else if ( StringHasPrefix( context, "connect-matchmaking-only" ) )
  1599. // This response is sent by Valve CS:GO servers - we cannot
  1600. // direct-connect and need to go via matchmaking instead
  1601. {
  1602. COM_ExplainDisconnection( true, "You must use matchmaking to connect to this CS:GO server.\n" );
  1603. Disconnect();
  1604. break;
  1605. }
  1606. else if ( StringHasPrefix( context, "connect-lan-only" ) )
  1607. // This response is sent by anonymous community servers - we cannot
  1608. // direct-connect unless we are on the same LAN network
  1609. {
  1610. COM_ExplainDisconnection( true, "You cannot connect to this CS:GO server because it is restricted to LAN connections only.\n" );
  1611. Disconnect();
  1612. break;
  1613. }
  1614. else if ( !StringHasPrefix( context, "connect-granted" ) &&
  1615. dc.m_chLobbyType[0] && !sv.IsActive() && !dc.m_unLobbyID )
  1616. // Server requires lobbies, but is unreserved at the moment, so
  1617. // we should keep waiting for "connect-granted" response before we
  1618. // proceed and create a lobby
  1619. {
  1620. Msg( "Server did not approve grace request, retrying...\n" );
  1621. CheckForResend( true ); // force a resend with the correct challenge nr
  1622. }
  1623. else
  1624. {
  1625. if ( dc.m_chLobbyType[0] && !sv.IsActive() && !dc.m_unLobbyID )
  1626. {
  1627. Msg( "Server approved grace request...\n" );
  1628. }
  1629. HandleDeferredConnection();
  1630. }
  1631. }
  1632. }
  1633. break;
  1634. case A2A_PRINT:
  1635. if ( msg.ReadString( string, sizeof(string) ) )
  1636. {
  1637. ConMsg ( "%s\n", string );
  1638. }
  1639. break;
  1640. case S2C_CONNREJECT:
  1641. if ( m_nSignonState == SIGNONSTATE_CHALLENGE )
  1642. {
  1643. msg.ReadString( string, sizeof(string) );
  1644. // Check if the connection is rejected with a redirect address
  1645. if ( char const *szRedirectAddress = StringAfterPrefix( string, "ConnectRedirectAddress:" ) )
  1646. {
  1647. m_Remote.RemoveAll();
  1648. m_Remote.AddRemote( szRedirectAddress, "public" );
  1649. // For the check for resend timer to fire a connection / getchallenge request.
  1650. SetSignonState( SIGNONSTATE_CHALLENGE, -1, NULL );
  1651. // Force connection request to fire.
  1652. m_flConnectTime = -FLT_MAX;
  1653. m_nRetryNumber = 0;
  1654. // Retry for up to timeout seconds
  1655. m_nRetryMax = cl_resend_timeout.GetFloat() / cl_resend.GetFloat();
  1656. break;
  1657. }
  1658. // Force failure dialog to come up now.
  1659. COM_ExplainDisconnection( true, "%s", string );
  1660. Disconnect();
  1661. // Host_Disconnect();
  1662. }
  1663. break;
  1664. case A2A_PING:
  1665. NET_OutOfBandPrintf( m_Socket, packet->from, "%c00000000000000", A2A_ACK );
  1666. break;
  1667. case A2A_ACK:
  1668. ConMsg ("A2A_ACK from %s\n", ns_address_render( packet->from ).String() );
  1669. #if defined( _GAMECONSOLE )
  1670. // skip \r\n
  1671. msg.ReadByte();
  1672. msg.ReadByte();
  1673. const void *pvData = msg.GetBasePointer() + ( msg.GetNumBitsRead() >> 3 );
  1674. int numBytes = msg.GetNumBytesLeft();
  1675. KeyValues *notify = new KeyValues( "A2A_ACK" );
  1676. notify->SetPtr( "ptr", const_cast< void * >( pvData ) );
  1677. notify->SetInt( "size", numBytes );
  1678. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( notify );
  1679. #endif
  1680. break;
  1681. case A2A_CUSTOM:
  1682. break; // TODO fire local game event
  1683. case S2A_RESERVE_RESPONSE:
  1684. if ( msg.ReadLong() == GetHostVersion() )
  1685. {
  1686. ReservationResponseReply_t reply;
  1687. reply.m_adrFrom = packet->from;
  1688. reply.m_uiResponse = msg.ReadByte();
  1689. reply.m_bValveDS = msg.ReadOneBit() ? true : false;
  1690. reply.m_numGameSlots = msg.ReadLong();
  1691. HandleReservationResponse( reply );
  1692. }
  1693. break;
  1694. case S2A_RESERVE_CHECK_RESPONSE:
  1695. {
  1696. int32 hostVersion = msg.ReadLong();
  1697. uint32 token = msg.ReadLong();
  1698. for ( int i = 0; i < m_arrSvReservationCheck.Count(); ++ i )
  1699. {
  1700. CServerMsg_CheckReservation *pSv = m_arrSvReservationCheck[ i ];
  1701. if ( pSv && pSv->m_serverAdr.CompareAdr( packet->from ) )
  1702. {
  1703. pSv->ResponseReceived( packet->from, msg, hostVersion, token );
  1704. }
  1705. }
  1706. }
  1707. break;
  1708. case S2A_PING_RESPONSE:
  1709. {
  1710. int32 hostVersion = msg.ReadLong();
  1711. uint32 token = msg.ReadLong();
  1712. for ( int iSvPing = 0; iSvPing < m_arrSvPing.Count(); ++ iSvPing )
  1713. {
  1714. CServerMsg_Ping *pSvPing = m_arrSvPing[ iSvPing ];
  1715. if ( pSvPing && pSvPing->m_serverAdr.CompareAdr( packet->from ) )
  1716. {
  1717. pSvPing->ResponseReceived( packet->from, msg, hostVersion, token );
  1718. }
  1719. }
  1720. }
  1721. break;
  1722. #ifndef DEDICATED
  1723. case 0:
  1724. {
  1725. // Feed into matchmaking
  1726. packet->message = msgOriginal;
  1727. KeyValues *notify = new KeyValues( "OnNetLanConnectionlessPacket" );
  1728. notify->SetPtr( "rawpkt", packet );
  1729. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( notify );
  1730. }
  1731. return true;
  1732. #endif
  1733. #if defined( _GAMECONSOLE )
  1734. case M2A_SERVER_BATCH:
  1735. {
  1736. // skip \n
  1737. msg.ReadByte();
  1738. const void *pvData = msg.GetBasePointer() + ( msg.GetNumBitsRead() >> 3 );
  1739. int numBytes = msg.GetNumBytesLeft();
  1740. KeyValues *notify = new KeyValues( "M2A_SERVER_BATCH" );
  1741. notify->SetPtr( "ptr", const_cast< void * >( pvData ) );
  1742. notify->SetInt( "size", numBytes );
  1743. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( notify );
  1744. }
  1745. break;
  1746. case A2A_KV_CMD:
  1747. {
  1748. int nVersion = msg.ReadByte();
  1749. if ( nVersion == A2A_KV_VERSION )
  1750. {
  1751. int nHeader = msg.ReadLong();
  1752. int nReplyId = msg.ReadLong();
  1753. int nChallenge = msg.ReadLong();
  1754. int nExtra = msg.ReadLong();
  1755. int numBytes = msg.ReadLong();
  1756. KeyValues *kvData = NULL;
  1757. if ( numBytes > 0 && numBytes <= MAX_ROUTABLE_PAYLOAD )
  1758. {
  1759. void *pvBytes = stackalloc( numBytes );
  1760. if ( msg.ReadBytes( pvBytes, numBytes ) )
  1761. {
  1762. kvData = new KeyValues( "" );
  1763. CUtlBuffer buf( pvBytes, numBytes, CUtlBuffer::READ_ONLY );
  1764. buf.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
  1765. if ( !kvData->ReadAsBinary( buf ) )
  1766. {
  1767. kvData->deleteThis();
  1768. kvData = NULL;
  1769. }
  1770. }
  1771. }
  1772. KeyValues *notify = new KeyValues( "A2A_KV_CMD" );
  1773. if ( kvData )
  1774. notify->AddSubKey( kvData );
  1775. notify->SetInt( "version", nVersion );
  1776. notify->SetInt( "header", nHeader );
  1777. notify->SetInt( "replyid", nReplyId );
  1778. notify->SetInt( "challenge", nChallenge );
  1779. notify->SetInt( "extra", nExtra );
  1780. notify->SetInt( "size", numBytes );
  1781. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( notify );
  1782. }
  1783. }
  1784. break;
  1785. #endif
  1786. case S2A_INFO_SRC:
  1787. {
  1788. // Handle pingserver response
  1789. Read_S2A_INFO_SRC( packet->from, &msg );
  1790. }
  1791. return true;
  1792. // Unknown?
  1793. default:
  1794. // Otherwise, don't do anything.
  1795. ConDMsg( "Bad connectionless packet ( CL '%c') from %s.\n", c, ns_address_render( packet->from ).String() );
  1796. return false;
  1797. }
  1798. return true;
  1799. }
  1800. void CBaseClientState::OnEvent( KeyValues *pEvent )
  1801. {
  1802. #if ENGINE_CONNECT_VIA_MMS
  1803. char const *szEvent = pEvent->GetName();
  1804. if ( !Q_stricmp( "OnNetLanConnectionlessPacket", szEvent ) )
  1805. {
  1806. if ( KeyValues *pGameDetailsServer = pEvent->FindKey( "GameDetailsServer" ) )
  1807. {
  1808. char const *szDetailsAdr = pGameDetailsServer->GetString( "ConnectServerDetailsRequest/server" );
  1809. if ( !m_bWaitingForServerGameDetails ||
  1810. !szDetailsAdr || !*szDetailsAdr )
  1811. return;
  1812. int idxRemoteReconnect = -1;
  1813. for ( int i = 0; i < m_Remote.Count(); ++i )
  1814. {
  1815. const netadr_t &adr = m_Remote.Get( i ).m_adrRemote;
  1816. if ( !Q_stricmp( adr.ToString(), szDetailsAdr ) )
  1817. {
  1818. idxRemoteReconnect = i;
  1819. break;
  1820. }
  1821. }
  1822. if ( idxRemoteReconnect >= 0 )
  1823. {
  1824. // This is our direct connect probe response
  1825. m_bWaitingForServerGameDetails = false;
  1826. Msg( "Received game details information from %s...\n",
  1827. m_Remote.Get( idxRemoteReconnect ).m_szRetryAddress.String() );
  1828. Disconnect( false ); // disconnect the current attempt, will retry with reservation
  1829. //
  1830. // Prepare the settings
  1831. //
  1832. pGameDetailsServer->SetName( "settings" );
  1833. if ( KeyValues *kvLanSearch = pGameDetailsServer->FindKey( "ConnectServerDetailsRequest" ) )
  1834. {
  1835. // LanSearch is not needed for the sessions
  1836. pGameDetailsServer->RemoveSubKey( kvLanSearch );
  1837. kvLanSearch->deleteThis();
  1838. }
  1839. // Add the bypass lobby flag
  1840. KeyValues *optionsKey = pGameDetailsServer->FindKey( "options", true);
  1841. optionsKey->SetInt( "bypasslobby", 1 );
  1842. Disconnect( false );
  1843. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1844. "OnEngineLevelLoadingSession", "reason", "CreateSession" ) );
  1845. g_pMatchFramework->CreateSession( pGameDetailsServer );
  1846. }
  1847. }
  1848. }
  1849. #endif
  1850. }
  1851. void CBaseClientState::SetConnectionPassword( char const *pchCurrentPW )
  1852. {
  1853. if ( !pchCurrentPW || !*pchCurrentPW )
  1854. {
  1855. m_bWaitingForPassword = false;
  1856. m_bEnteredPassword = false;
  1857. Msg( "Connection to %s failed, server requires a password\n", ns_address_render( m_DeferredConnection.m_adrServerAddress ).String() );
  1858. Disconnect();
  1859. return;
  1860. }
  1861. #ifndef DEDICATED
  1862. SCR_BeginLoadingPlaque();
  1863. #endif
  1864. m_bWaitingForPassword = false;
  1865. m_bEnteredPassword = true;
  1866. password.SetValue( pchCurrentPW );
  1867. HandleDeferredConnection();
  1868. }
  1869. void CBaseClientState::RememberIPAddressForLobby( uint64 unLobbyID, const ns_address &adrRemote )
  1870. {
  1871. ConColorMsg( Color( 0, 255, 0, 255 ), "RememberIPAddressForLobby: lobby %llx from address %s\n", unLobbyID, ( cl_hideserverip.GetInt()>0 && adrRemote.IsType<netadr_t>() ) ? "<ip hidden>" : ns_address_render( adrRemote ).String() );
  1872. m_DirectConnectLobby.m_unLobbyID = unLobbyID;
  1873. m_DirectConnectLobby.m_adrRemote = adrRemote;
  1874. // Keep valid for 1 minute
  1875. m_DirectConnectLobby.m_flEndTime = realtime + 60.0f;
  1876. }
  1877. void CBaseClientState::HandleDeferredConnection()
  1878. {
  1879. DeferredConnection_t &dc = m_DeferredConnection;
  1880. if ( !dc.m_bActive )
  1881. return;
  1882. dc.m_bActive = false;
  1883. #ifndef DEDICATED
  1884. // If we started a listen server then we should connect no matter what
  1885. if ( ( dc.m_chLobbyType[0] || dc.m_unLobbyID ) && !sv.IsActive() )
  1886. {
  1887. #if ENGINE_CONNECT_VIA_MMS
  1888. if ( dc.m_unLobbyID )
  1889. {
  1890. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  1891. KeyValues *pSessionSysData = pMatchSession ? pMatchSession->GetSessionSystemData() : NULL;
  1892. uint64 xuidSessionReservation = pSessionSysData ? pSessionSysData->GetUint64( "xuidReserve" ) : 0ull;
  1893. if ( dc.m_bOfficialValveServer || ( xuidSessionReservation == dc.m_unLobbyID ) ) // Force the connection to official server, they aren't direct-connectable otherwise
  1894. {
  1895. SendConnectPacket ( dc.m_adrServerAddress, dc.m_nChallenge, dc.m_nAuthprotocol, dc.m_unGSSteamID, dc.m_bGSSecure );
  1896. }
  1897. else
  1898. {
  1899. KeyValues *pSettings = new KeyValues( "settings" );
  1900. KeyValues::AutoDelete autodelete( pSettings );
  1901. pSettings->SetString( "system/network", "LIVE" );
  1902. pSettings->SetString( "options/action", "joinsession" );
  1903. pSettings->SetUint64( "options/sessionid", dc.m_unLobbyID );
  1904. pSettings->SetBool( "options/dcFriendsRed", dc.m_bDCFriendsReqd );
  1905. if ( !dc.m_bOfficialValveServer )
  1906. pSettings->SetInt( "game/hosted", 1 );
  1907. pSettings->SetString( "options/server", dc.m_bOfficialValveServer ? "official" : "dedicated" );
  1908. Disconnect( true );
  1909. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1910. "OnEngineLevelLoadingSession", "reason", "MatchSession" ) );
  1911. g_pMatchFramework->MatchSession( pSettings );
  1912. }
  1913. }
  1914. else
  1915. {
  1916. // Server has no lobby but needs one, we need to enter through the UI.
  1917. Msg( "Retrying connection to %s, server requires lobby reservation but is unreserved.\n", dc.m_adrServerAddress.ToString() );
  1918. // Prevent resending the challenges to the server
  1919. m_bWaitingForServerGameDetails = true;
  1920. ResendGameDetailsRequest( dc.m_adrServerAddress );
  1921. }
  1922. #else
  1923. bool bCanSendConnectPacketRightNow = false;
  1924. // Always allow to connect if we have a reservation in m_nServerReservationCookie
  1925. if ( m_nServerReservationCookie )
  1926. bCanSendConnectPacketRightNow = true;
  1927. // Always allow to connect to listen server peer SteamID
  1928. if ( m_ListenServerSteamID )
  1929. bCanSendConnectPacketRightNow = true;
  1930. // Always allow to connect from dedicated or ValveDS or GOTV relay
  1931. if ( IsClientStateTv() || NET_IsDedicated() || sv.IsDedicated() || ( serverGameDLL && serverGameDLL->IsValveDS() ) )
  1932. bCanSendConnectPacketRightNow = true;
  1933. if ( !bCanSendConnectPacketRightNow && (
  1934. !dc.m_unGSSteamID || // Connecting to GOTV relay
  1935. ( CSteamID( dc.m_unGSSteamID ).GetEAccountType() == k_EAccountTypeInvalid ) // Connecting to sv_lan 1 server
  1936. ) )
  1937. {
  1938. static const bool s_bAllowLanWhitelist = !CommandLine()->FindParm( "-ignorelanwhitelist" );
  1939. bCanSendConnectPacketRightNow = // only allow for LAN server setup to bypass GC auth
  1940. dc.m_adrServerAddress.IsLocalhost() || dc.m_adrServerAddress.IsLoopback() // localhost/loopback
  1941. || ( s_bAllowLanWhitelist && dc.m_adrServerAddress.IsReservedAdr() ) // LAN RFC 1918
  1942. ;
  1943. }
  1944. // If we determined that client is good to go then just follow up with a real connect packet
  1945. if ( bCanSendConnectPacketRightNow )
  1946. {
  1947. SendConnectPacket( dc.m_adrServerAddress, dc.m_nChallenge, dc.m_nAuthprotocol, dc.m_unGSSteamID, dc.m_bGSSecure );
  1948. return;
  1949. }
  1950. // Check that if we are falling through we have a good game server Steam ID to ask GC about
  1951. if ( !dc.m_unGSSteamID || !CSteamID( dc.m_unGSSteamID ).BGameServerAccount() )
  1952. {
  1953. Disconnect( true ); // cannot retry this attempt - the GS SteamID is not good
  1954. COM_ExplainDisconnection( true, "You cannot connect to this CS:GO server because it is restricted to LAN connections only.\n" );
  1955. return;
  1956. }
  1957. //
  1958. // Otherwise we require that client obtained a GS cookie from GC
  1959. // which allows GC to deny connections to blacklisted game servers
  1960. //
  1961. uint64 uiReservationCookie = 0ull;
  1962. {
  1963. KeyValues *kvCreateSession = new KeyValues( "OnEngineLevelLoadingSession" );
  1964. kvCreateSession->SetString( "reason", "CreateSession" );
  1965. kvCreateSession->SetString( "adr", ns_address_render( dc.m_adrServerAddress ).String() );
  1966. kvCreateSession->SetUint64( "gsid", dc.m_unGSSteamID );
  1967. kvCreateSession->SetPtr( "ptr", &uiReservationCookie );
  1968. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvCreateSession );
  1969. }
  1970. if ( !uiReservationCookie )
  1971. {
  1972. Disconnect( true ); // disconnect the current attempt, will retry with GC reservation
  1973. {
  1974. KeyValues *kvCreateSession = new KeyValues( "OnEngineLevelLoadingSession" );
  1975. kvCreateSession->SetString( "reason", "CreateSession" );
  1976. kvCreateSession->SetString( "adr", ns_address_render( dc.m_adrServerAddress ).String() );
  1977. kvCreateSession->SetUint64( "gsid", dc.m_unGSSteamID );
  1978. // NO PTR HERE, FORCE COOKIE: kvCreateSession->SetPtr( "ptr", &uiReservationCookie );
  1979. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( kvCreateSession );
  1980. }
  1981. }
  1982. else
  1983. SendConnectPacket( dc.m_adrServerAddress, dc.m_nChallenge, dc.m_nAuthprotocol, dc.m_unGSSteamID, dc.m_bGSSecure );
  1984. #endif
  1985. }
  1986. else
  1987. #endif
  1988. {
  1989. SendConnectPacket ( dc.m_adrServerAddress, dc.m_nChallenge, dc.m_nAuthprotocol, dc.m_unGSSteamID, dc.m_bGSSecure );
  1990. }
  1991. }
  1992. bool CBaseClientState::NETMsg_Tick( const CNETMsg_Tick& msg )
  1993. {
  1994. VPROF( "ProcessTick" );
  1995. m_NetChannel->SetRemoteFramerate(
  1996. CNETMsg_Tick_t::FrametimeToFloat( msg.host_computationtime() ),
  1997. CNETMsg_Tick_t::FrametimeToFloat( msg.host_computationtime_std_deviation() ),
  1998. CNETMsg_Tick_t::FrametimeToFloat( msg.host_framestarttime_std_deviation() ) );
  1999. // Note: CClientState separates the client and server clock states and drifts
  2000. // the client's clock to match the server's, but right here, we keep the two clocks in sync.
  2001. SetClientTickCount( msg.tick() );
  2002. SetServerTickCount( msg.tick() );
  2003. if ( m_StringTableContainer )
  2004. {
  2005. m_StringTableContainer->SetTick( GetServerTickCount() );
  2006. }
  2007. return true;// (GetServerTickCount()>0);
  2008. }
  2009. void CBaseClientState::SendStringCmd(const char * command)
  2010. {
  2011. if ( m_NetChannel)
  2012. {
  2013. CNETMsg_StringCmd_t stringCmd( command );
  2014. m_NetChannel->SendNetMsg( stringCmd );
  2015. if ( strstr( command, "disconnect" ) )
  2016. {
  2017. // When client requests a disconnect this is last moment to
  2018. // push any data into secure network, forcefully transmit netchannel
  2019. m_NetChannel->Transmit();
  2020. }
  2021. }
  2022. }
  2023. bool CBaseClientState::NETMsg_StringCmd( const CNETMsg_StringCmd& msg )
  2024. {
  2025. VPROF( "ProcessStringCmd" );
  2026. return InternalProcessStringCmd( msg );
  2027. }
  2028. bool CBaseClientState::InternalProcessStringCmd( const CNETMsg_StringCmd& msg )
  2029. {
  2030. const char *command = msg.command().c_str();
  2031. // Don't restrict commands from the server in single player or if cl_restrict_stuffed_commands is 0.
  2032. if ( !m_bRestrictServerCommands || sv.IsActive() )
  2033. {
  2034. Cbuf_AddText ( Cbuf_GetCurrentPlayer(), command, kCommandSrcCode ); // FIXME: Should this be kCommandSrcNetServer and we push the m_bRestrictServerCommands into the server check?
  2035. return true;
  2036. }
  2037. CCommand args;
  2038. args.Tokenize( command );
  2039. if ( args.ArgC() <= 0 )
  2040. return true;
  2041. // Run the command, but make sure the command parser knows to only execute commands marked with FCVAR_SERVER_CAN_EXECUTE.
  2042. Cbuf_AddText( Cbuf_GetCurrentPlayer(), command, kCommandSrcNetServer );
  2043. return true;
  2044. }
  2045. #ifndef DEDICATED
  2046. class CScaleformAvatarImageProviderImpl : public IScaleformAvatarImageProvider
  2047. {
  2048. public:
  2049. // Scaleform low-level image needs rgba bits of the inventory image (if it's ready)
  2050. virtual bool GetImageInfo( uint64 xuid, ImageInfo_t *pImageInfo ) OVERRIDE
  2051. {
  2052. CSteamID steamID( xuid );
  2053. if ( !steamID.IsValid() || !steamID.BIndividualAccount() || !steamID.GetAccountID() )
  2054. return false;
  2055. if ( !GetBaseLocalClient().IsConnected() )
  2056. return false;
  2057. // Find the player with the given account ID
  2058. CBaseClientState::PlayerAvatarDataMap_t const &data = GetBaseLocalClient().m_mapPlayerAvatarData;
  2059. CBaseClientState::PlayerAvatarDataMap_t::IndexType_t const idxData = data.Find( steamID.GetAccountID() );
  2060. if ( idxData == data.InvalidIndex() )
  2061. return false;
  2062. const CNETMsg_PlayerAvatarData& msg = *data.Element( idxData );
  2063. pImageInfo->m_cbImageData = msg.rgb().size();
  2064. pImageInfo->m_pvImageData = msg.rgb().data();
  2065. return true;
  2066. }
  2067. } g_CScaleformAvatarImageProviderImpl;
  2068. #endif
  2069. bool CBaseClientState::NETMsg_PlayerAvatarData( const CNETMsg_PlayerAvatarData& msg )
  2070. {
  2071. PlayerAvatarDataMap_t::IndexType_t idxData = m_mapPlayerAvatarData.Find( msg.accountid() );
  2072. if ( idxData != m_mapPlayerAvatarData.InvalidIndex() )
  2073. {
  2074. delete m_mapPlayerAvatarData.Element( idxData );
  2075. m_mapPlayerAvatarData.RemoveAt( idxData );
  2076. }
  2077. CNETMsg_PlayerAvatarData_t *pClientDataCopy = new CNETMsg_PlayerAvatarData_t;
  2078. pClientDataCopy->CopyFrom( msg );
  2079. m_mapPlayerAvatarData.Insert( pClientDataCopy->accountid(), pClientDataCopy );
  2080. #ifndef DEDICATED
  2081. if ( g_pScaleformUI )
  2082. g_pScaleformUI->AvatarImageReload( uint64( pClientDataCopy->accountid() ), &g_CScaleformAvatarImageProviderImpl );
  2083. #endif
  2084. return true;
  2085. }
  2086. CNETMsg_PlayerAvatarData_t * CBaseClientState::AllocOwnPlayerAvatarData() const
  2087. {
  2088. #ifndef DEDICATED
  2089. // If the game server is not GOTV then upload our own avatar data
  2090. extern ConVar sv_reliableavatardata;
  2091. if ( ( this == &GetBaseLocalClient() )
  2092. && !GetBaseLocalClient().ishltv
  2093. && m_NetChannel && IsConnected()
  2094. && sv_reliableavatardata.GetBool() )
  2095. {
  2096. CSteamID steamID = Steam3Client().SteamUser()->GetSteamID();
  2097. int iAvatar = Steam3Client().SteamFriends()->GetMediumFriendAvatar( steamID );
  2098. if ( ( iAvatar != -1 ) && ( iAvatar != 0 ) )
  2099. {
  2100. uint32 wide = 0, tall = 0;
  2101. if ( Steam3Client().SteamUtils()->GetImageSize( iAvatar, &wide, &tall ) && ( wide == 64 ) && ( tall == 64 ) )
  2102. {
  2103. CUtlVector< byte > memAvatarData;
  2104. memAvatarData.SetCount( 64 * 64 * 4 );
  2105. memset( memAvatarData.Base(), 0xFF, memAvatarData.Count() );
  2106. Steam3Client().SteamUtils()->GetImageRGBA( iAvatar, memAvatarData.Base(), memAvatarData.Count() );
  2107. // trim alpha for size
  2108. for ( int y = 0; y < 64; ++y ) for ( int x = 0; x < 64; ++x )
  2109. {
  2110. V_memmove( memAvatarData.Base() + y * 64 * 3 + x * 3, memAvatarData.Base() + y * 64 * 4 + x * 4, 3 );
  2111. }
  2112. return new CNETMsg_PlayerAvatarData_t( steamID.GetAccountID(), memAvatarData.Base(), 64 * 64 * 3 );
  2113. }
  2114. }
  2115. // If we got here then we failed to obtain our own medium avatar, ping Steam rack and hope for an avatar changed callback
  2116. if ( !Steam3Client().SteamFriends()->RequestUserInformation( steamID, false ) )
  2117. {
  2118. static bool s_bReentrantGuard = false;
  2119. if ( !s_bReentrantGuard )
  2120. { // Allow one retry here
  2121. s_bReentrantGuard = true;
  2122. return AllocOwnPlayerAvatarData();
  2123. s_bReentrantGuard = false;
  2124. }
  2125. }
  2126. }
  2127. #endif
  2128. return NULL;
  2129. }
  2130. bool CBaseClientState::NETMsg_SetConVar( const CNETMsg_SetConVar& msg )
  2131. {
  2132. VPROF( "ProcessSetConVar" );
  2133. // Never process on local client, since the ConVar is directly linked here
  2134. if ( m_NetChannel->IsLoopback() )
  2135. return true;
  2136. for ( int i=0; i<msg.convars().cvars_size(); i++ )
  2137. {
  2138. const char *name = NetMsgGetCVarUsingDictionary( msg.convars().cvars(i) );
  2139. const char *value = msg.convars().cvars(i).value().c_str();
  2140. // De-constify
  2141. ConVarRef var( name );
  2142. if ( !var.IsValid() )
  2143. {
  2144. ConMsg( "SetConVar: No such cvar ( %s set to %s), skipping\n",
  2145. name, value );
  2146. continue;
  2147. }
  2148. // Make sure server is only setting replicated game ConVars
  2149. if ( !var.IsFlagSet( FCVAR_REPLICATED ) )
  2150. {
  2151. ConMsg( "SetConVar: Can't set server cvar %s to %s, not marked as FCVAR_REPLICATED on client\n",
  2152. name, value );
  2153. continue;
  2154. }
  2155. // Set value directly ( don't call through cv->DirectSet!!! )
  2156. if ( !sv.IsActive() )
  2157. {
  2158. var.SetValue( value );
  2159. DevMsg( "SetConVar: %s = \"%s\"\n", name, value );
  2160. }
  2161. }
  2162. return true;
  2163. }
  2164. bool CBaseClientState::NETMsg_SignonState( const CNETMsg_SignonState& msg )
  2165. {
  2166. VPROF( "ProcessSignonState" );
  2167. return SetSignonState( msg.signon_state(), msg.spawn_count(), &msg ) ;
  2168. }
  2169. bool CBaseClientState::SVCMsg_Print( const CSVCMsg_Print& msg )
  2170. {
  2171. VPROF( "SVCMsg_Print" );
  2172. ConMsg( "%s", msg.text().c_str() );
  2173. return true;
  2174. }
  2175. bool CBaseClientState::SVCMsg_Menu( const CSVCMsg_Menu& msg )
  2176. {
  2177. VPROF( "SVCMsg_Menu" );
  2178. #if !defined(DEDICATED)
  2179. PluginHelpers_Menu( msg );
  2180. #endif
  2181. return true;
  2182. }
  2183. bool CBaseClientState::SVCMsg_ServerInfo( const CSVCMsg_ServerInfo& msg )
  2184. {
  2185. VPROF( "SVCMsg_ServerInfo" );
  2186. #ifndef DEDICATED
  2187. EngineVGui()->UpdateProgressBar(PROGRESS_PROCESSSERVERINFO);
  2188. #endif
  2189. COM_TimestampedLog( " CBaseClient::SVCMsg_ServerInfo" );
  2190. if ( msg.protocol() != GetHostVersion() )
  2191. {
  2192. #if !defined( DEDICATED )
  2193. if ( demoplayer && demoplayer->IsPlayingBack() )
  2194. {
  2195. ConMsg ( "WARNING: Server demo version %i, client version %i.\n", msg.protocol(), GetHostVersion() );
  2196. }
  2197. else
  2198. #endif
  2199. {
  2200. ConMsg ( "WARNING: Server version %i, client version %i.\n", msg.protocol(), GetHostVersion() );
  2201. }
  2202. }
  2203. // Parse servercount (i.e., # of servers spawned since server .exe started)
  2204. // So that we can detect new server startup during download, etc.
  2205. m_nServerCount = msg.server_count();
  2206. m_nMaxClients = msg.max_clients();
  2207. m_nServerClasses = msg.max_classes();
  2208. m_nServerClassBits = Q_log2( m_nServerClasses ) + 1;
  2209. if ( m_nMaxClients < 1 || m_nMaxClients > ABSOLUTE_PLAYER_LIMIT )
  2210. {
  2211. ConMsg ("Bad maxclients (%u) from server.\n", m_nMaxClients);
  2212. return false;
  2213. }
  2214. if ( m_nServerClasses < 1 || m_nServerClasses > MAX_SERVER_CLASSES )
  2215. {
  2216. ConMsg ("Bad maxclasses (%u) from server.\n", m_nServerClasses);
  2217. return false;
  2218. }
  2219. #ifndef DEDICATED
  2220. if ( !sv.IsActive() &&
  2221. ( s_ClientBroadcastPlayer.IsPlayingBack() || // /*( demoplayer && demoplayer->IsPlayingBack() )*/
  2222. !( m_NetChannel->IsLoopback() || m_NetChannel->IsNull() || m_NetChannel->GetRemoteAddress().IsLocalhost() )
  2223. )
  2224. )
  2225. {
  2226. // reset server enforced cvars
  2227. g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED );
  2228. extern void RevertAllModifiedLocalState();
  2229. RevertAllModifiedLocalState();
  2230. }
  2231. #endif
  2232. // clear all baselines still around from last game
  2233. FreeEntityBaselines();
  2234. // force changed flag to being reset
  2235. g_GameEventManager.HasClientListenersChanged( true );
  2236. #ifndef DEDICATED
  2237. splitscreen->AddBaseUser( 0, msg.player_slot() + 1 );
  2238. #if defined( INCLUDE_SCALEFORM )
  2239. if ( g_pScaleformUI )
  2240. {
  2241. extern IScaleformSlotInitController *g_pIScaleformSlotInitControllerEngineImpl;
  2242. g_pScaleformUI->InitSlot( SF_SS_SLOT( 0 ), g_szDefaultScaleformClientMovieName, g_pIScaleformSlotInitControllerEngineImpl );
  2243. }
  2244. #endif
  2245. #endif
  2246. m_nPlayerSlot = msg.player_slot();
  2247. m_nViewEntity = msg.player_slot() + 1;
  2248. if ( msg.tick_interval() < MINIMUM_TICK_INTERVAL ||
  2249. msg.tick_interval() > MAXIMUM_TICK_INTERVAL )
  2250. {
  2251. ConMsg ("Interval_per_tick %f out of range [%f to %f]\n",
  2252. msg.tick_interval(), MINIMUM_TICK_INTERVAL, MAXIMUM_TICK_INTERVAL );
  2253. return false;
  2254. }
  2255. if ( !COM_CheckGameDirectory( msg.game_dir().c_str() ) )
  2256. {
  2257. return false;
  2258. }
  2259. Q_snprintf( m_szLevelName, sizeof( m_szLevelName ), "maps/%s%s.bsp", msg.map_name().c_str(), GetPlatformExt() );
  2260. Q_FixSlashes( m_szLevelName );
  2261. Q_strncpy( m_szLevelNameShort, msg.map_name().c_str(), sizeof( m_szLevelNameShort ) );
  2262. Q_strncpy( m_szMapGroupName, msg.map_group_name().c_str(), sizeof( m_szMapGroupName ) );
  2263. m_unUGCMapFileID = msg.ugc_map_id();
  2264. #if !defined(DEDICATED)
  2265. EngineVGui()->SetProgressLevelName( m_szLevelNameShort );
  2266. audiosourcecache->LevelInit( m_szLevelNameShort );
  2267. #endif
  2268. ConVarRef skyname( "sv_skyname" );
  2269. if ( skyname.IsValid() )
  2270. {
  2271. skyname.SetValue( msg.sky_name().c_str() );
  2272. }
  2273. m_nDeltaTick = -1; // no valid snapshot for this game yet
  2274. // fire a client side event about server data
  2275. IGameEvent *pEvent = g_GameEventManager.CreateEvent( "server_spawn" );
  2276. if ( pEvent )
  2277. {
  2278. pEvent->SetString( "hostname", msg.host_name().c_str() );
  2279. ns_address adr = m_NetChannel->GetRemoteAddress();
  2280. if ( adr.IsType<netadr_t>() )
  2281. {
  2282. pEvent->SetString( "address", CUtlNetAdrRender( adr.AsType<netadr_t>(), true ).String() );
  2283. pEvent->SetInt( "port", adr.GetPort() );
  2284. }
  2285. else
  2286. {
  2287. pEvent->SetString( "address", ns_address_render( adr ).String() );
  2288. }
  2289. pEvent->SetString( "game", msg.game_dir().c_str() );
  2290. pEvent->SetString( "mapname", msg.map_name().c_str() );
  2291. pEvent->SetInt( "maxplayers", msg.max_clients() );
  2292. pEvent->SetInt( "password", 0 ); // TODO
  2293. pEvent->SetString( "os", Q_strupr( va("%c", msg.c_os() ) ) );
  2294. pEvent->SetBool( "dedicated", msg.is_dedicated() );
  2295. pEvent->SetBool( "official", msg.is_official_valve_server() );
  2296. if ( m_ulGameServerSteamID != 0 )
  2297. {
  2298. pEvent->SetString( "steamid", CSteamID( m_ulGameServerSteamID ).Render() );
  2299. }
  2300. g_GameEventManager.FireEventClientSide( pEvent );
  2301. }
  2302. // Verify that the client doesn't play on the server with mismatching version
  2303. if ( m_nServerProtocolVersion && ( GetHostVersion() > m_nServerProtocolVersion ) )
  2304. {
  2305. if ( !msg.is_hltv() && !msg.is_redirecting_to_proxy_relay() )
  2306. {
  2307. // Newer client attempts to play on an older server which is not GOTV, bail here
  2308. Warning( "Failed to connect to a gameserver, client version %d, server version %d\n", GetHostVersion(), m_nServerProtocolVersion );
  2309. Disconnect();
  2310. return false;
  2311. }
  2312. }
  2313. // must be set BEFORE loading bsp which indicates above dependent code has completed
  2314. m_bServerInfoProcessed = true;
  2315. m_nServerInfoMsgProtocol = msg.protocol();
  2316. if ( !sv.IsActive() && ( !msg.is_hltv() || !msg.is_redirecting_to_proxy_relay() ) )
  2317. {
  2318. // For a non-local connection, the bsp needs to be loaded first, BEFORE any server string table
  2319. // callbacks occur. These occur after this function and before the SIGNONSTATE_NEW,
  2320. // causing unexpected out of order issues. The server material string table of precached
  2321. // materials has bsp dependencies (i.e. due to cubemap patching) so bsp load must be first.
  2322. // This also lets (fixes) the queued loader batch fast load the resources instead of the callbacks
  2323. // loading them via the slower synchronous method.
  2324. if ( IsGameConsole() && g_pQueuedLoader->IsMapLoading() )
  2325. {
  2326. Msg( "New CSVCMsg_ServerInfo message - loading map %s. Forcing current map load to end.\n", msg.map_name().c_str() );
  2327. g_pQueuedLoader->EndMapLoading( true );
  2328. }
  2329. SV_CheckForFlushMemory( m_szLastLevelNameShort, m_szLevelNameShort );
  2330. SV_FlushMemoryIfMarked();
  2331. // A map is about to be loaded into memory
  2332. HostState_Pre_LoadMapIntoMemory();
  2333. // CSGO custom map detection
  2334. bool bClientHasMap = true;
  2335. bool bIsRelay = false;
  2336. for ( CHltvServerIterator hltv; hltv; hltv.Next() )
  2337. {
  2338. if ( hltv->IsTVRelay() )
  2339. {
  2340. bIsRelay = true;
  2341. break;
  2342. }
  2343. }
  2344. if ( !bIsRelay ) // not a single one of the hltv servers is relay
  2345. {
  2346. char bspModelName[ MAX_PATH ];
  2347. Q_snprintf( bspModelName, sizeof( bspModelName ), "maps/%s.bsp", msg.map_name().c_str() );
  2348. #if !defined( _GAMECONSOLE ) && !defined( DEDICATED )
  2349. bool bCrcClientMapValid = false;
  2350. CRC32_t crcClientMap;
  2351. CRC32_Init( &crcClientMap );
  2352. // Compute CRC of client map on disk
  2353. {
  2354. FileHandle_t mapfile = g_pFileSystem->OpenEx( bspModelName, "rb", IsGameConsole() ? FSOPEN_NEVERINPACK : 0, "GAME" );
  2355. if ( mapfile != FILESYSTEM_INVALID_HANDLE )
  2356. {
  2357. g_pFileSystem->Close( mapfile );
  2358. bCrcClientMapValid = CRC_MapFile( &crcClientMap, bspModelName );
  2359. }
  2360. }
  2361. if ( demoplayer && demoplayer->IsPlayingBack() )
  2362. {
  2363. // We are playing a demo
  2364. // Can we use the map on disk directly?
  2365. if ( bCrcClientMapValid && msg.map_crc() && ( crcClientMap == msg.map_crc() ) )
  2366. {
  2367. // Seems like everything looks good on disk and we can proceed with map on disk
  2368. }
  2369. else
  2370. {
  2371. // We are trying to playback a demo, but CRC of the client map doesn't match
  2372. // the CRC of the server map.
  2373. // There are some known official maps that we can redirect into proper version
  2374. // on Steam Workshop.
  2375. char chMapName[ MAX_PATH ] = { 0 };
  2376. V_sprintf_safe( chMapName, "%s", msg.map_name().c_str() );
  2377. // Check if it's Valve official Workshop symbolic link
  2378. if ( char const *szPastWorkshop = StringAfterPrefix( chMapName, "workshop/" ) )
  2379. {
  2380. //uint64 uiWkshpId = Q_atoui64( szPastWorkshop );
  2381. bool bValveOfficialMap = false;
  2382. /** Removed for partner depot **/
  2383. if ( bValveOfficialMap )
  2384. {
  2385. if ( char const *szMapNameTrail = strchr( szPastWorkshop, '/' ) )
  2386. Q_memmove( chMapName, szMapNameTrail + 1, chMapName + Q_ARRAYSIZE( chMapName ) - szMapNameTrail - 1 );
  2387. }
  2388. }
  2389. // Now that workshop symlink has been resolved, verify whether
  2390. // it is one of Valve official maps and redirect to Workshop based on known CRC
  2391. uint64 uiKnownVersionWkshpId = 0;
  2392. uint32 uiRepackedWkshpCrc = 0;
  2393. /** Removed for partner depot **/
  2394. extern ConVar debug_map_crc;
  2395. if ( debug_map_crc.GetBool() && !uiKnownVersionWkshpId )
  2396. { // Force a debug error when debugging map CRC's
  2397. Warning( "debug_map_crc: Map version mismatch for %s CRC=%u, no fallback version specified in csgo_official_map_versions!\n",
  2398. chMapName, msg.map_crc() );
  2399. }
  2400. if ( uiKnownVersionWkshpId )
  2401. { // We have a fallback version specified for the CRC mismatch
  2402. Msg( "Map version fallback for %s CRC=%u: %llu (CRC=%u)\n", chMapName, msg.map_crc(), uiKnownVersionWkshpId, uiRepackedWkshpCrc );
  2403. Q_snprintf( m_szLevelName, sizeof( m_szLevelName ), "maps/workshop/%llu/%s.bsp", uiKnownVersionWkshpId, chMapName );
  2404. Q_FixSlashes( m_szLevelName );
  2405. Q_snprintf( m_szLevelNameShort, sizeof( m_szLevelNameShort ), "workshop/%llu/%s", uiKnownVersionWkshpId, chMapName );
  2406. Q_snprintf( bspModelName, sizeof( bspModelName ), "maps/workshop/%llu/%s.bsp", uiKnownVersionWkshpId, chMapName );
  2407. m_unUGCMapFileID = uiKnownVersionWkshpId;
  2408. // Dirty method: patch in a different map crc for derived class processing to pick up repacked crc value
  2409. const_cast< CSVCMsg_ServerInfo & >( msg ).set_map_crc( uiRepackedWkshpCrc );
  2410. // Check if we already have the workshop file for the fallback version downloaded
  2411. FileHandle_t mapfile = g_pFileSystem->OpenEx( bspModelName, "rb", IsGameConsole() ? FSOPEN_NEVERINPACK : 0, "GAME" );
  2412. if ( mapfile != FILESYSTEM_INVALID_HANDLE )
  2413. {
  2414. g_pFileSystem->Close( mapfile );
  2415. bCrcClientMapValid = true; // prevent CRC from resetting bClientHasMap further down in the code, compat versions never change so chances are the map is good right off the bat
  2416. }
  2417. else
  2418. bClientHasMap = false;
  2419. }
  2420. }
  2421. }
  2422. else
  2423. {
  2424. // We are not playing a demo
  2425. if ( bCrcClientMapValid && msg.map_crc() && ( crcClientMap != msg.map_crc() ) && ( m_unUGCMapFileID != 0 ) )
  2426. bClientHasMap = false; // If the crc doesn't match servers delay map load until after downloading new version from the workshop
  2427. }
  2428. if ( !bCrcClientMapValid )
  2429. bClientHasMap = false;
  2430. #endif
  2431. if ( bClientHasMap )
  2432. modelloader->GetModelForName( bspModelName, IModelLoader::FMODELLOADER_CLIENT );
  2433. } // not a relay
  2434. // If we connect to a dedicated server, we need to load up the dictionary file
  2435. CRC32_t crc = CRC32_ConvertFromUnsignedLong( msg.string_table_crc() );
  2436. if ( !g_pStringTableDictionary->OnLevelLoadStart( bClientHasMap ? m_szLevelNameShort : NULL, &crc ) )
  2437. {
  2438. // Allow us to continue with a mismatch string table
  2439. // this can occur with slighty different versisons
  2440. Warning( "***String table CRC mismatch, may need to rebuild bsp if model oddities occur!\n" );
  2441. }
  2442. // Client needs an opportunity to write all profile information
  2443. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  2444. "OnProfilesWriteOpportunity", "reason", "checkpoint"
  2445. ) );
  2446. }
  2447. COM_TimestampedLog( "CBaseClient::ProcessServerInfo(done)" );
  2448. return true;
  2449. }
  2450. bool CBaseClientState::SVCMsg_SendTable( const CSVCMsg_SendTable& msg )
  2451. {
  2452. VPROF( "SVCMsg_SendTable" );
  2453. if ( !RecvTable_RecvClassInfos( msg ) )
  2454. {
  2455. Host_EndGame(true, "ProcessSendTable: RecvTable_RecvClassInfos failed.\n" );
  2456. return false;
  2457. }
  2458. return true;
  2459. }
  2460. bool CBaseClientState::SVCMsg_ClassInfo( const CSVCMsg_ClassInfo& msg )
  2461. {
  2462. VPROF( "SVCMsg_ClassInfo" );
  2463. COM_TimestampedLog( " CBaseClient::SVCMsg_ClassInfo" );
  2464. if ( msg.create_on_client() )
  2465. {
  2466. ConMsg ( "Can't create class tables.\n");
  2467. Assert( 0 );
  2468. return false;
  2469. }
  2470. if( m_pServerClasses )
  2471. {
  2472. delete [] m_pServerClasses;
  2473. }
  2474. m_nServerClasses = msg.classes_size();
  2475. m_pServerClasses = new C_ServerClassInfo[ m_nServerClasses ];
  2476. if ( !m_pServerClasses )
  2477. {
  2478. Host_EndGame(true, "SVCMsg_ClassInfo: can't allocate %d C_ServerClassInfos.\n", m_nServerClasses);
  2479. return false;
  2480. }
  2481. // copy class names and class IDs from message to CClientState
  2482. for (int i=0; i<m_nServerClasses; i++)
  2483. {
  2484. const CSVCMsg_ClassInfo::class_t& svclass = msg.classes( i );
  2485. if( svclass.class_id() >= m_nServerClasses )
  2486. {
  2487. Host_EndGame(true, "SVCMsg_ClassInfo: invalid class index (%d).\n", svclass.class_id());
  2488. return false;
  2489. }
  2490. C_ServerClassInfo * svclassinfo = &m_pServerClasses[svclass.class_id()];
  2491. int len = Q_strlen(svclass.class_name().c_str()) + 1;
  2492. svclassinfo->m_ClassName = new char[ len ];
  2493. Q_strncpy( svclassinfo->m_ClassName, svclass.class_name().c_str(), len );
  2494. len = Q_strlen(svclass.data_table_name().c_str()) + 1;
  2495. svclassinfo->m_DatatableName = new char[ len ];
  2496. Q_strncpy( svclassinfo->m_DatatableName,svclass.data_table_name().c_str(), len );
  2497. }
  2498. COM_TimestampedLog( " CBaseClient::SVCMsg_ClassInfo(done)" );
  2499. return LinkClasses(); // link server and client classes
  2500. }
  2501. bool CBaseClientState::SVCMsg_SetPause( const CSVCMsg_SetPause& msg )
  2502. {
  2503. VPROF( "SVCMsg_SetPause" );
  2504. m_bPaused = msg.paused();
  2505. return true;
  2506. }
  2507. bool CBaseClientState::SVCMsg_CreateStringTable( const CSVCMsg_CreateStringTable &msg )
  2508. {
  2509. VPROF( "SVCMsg_CreateStringTable" );
  2510. #ifndef DEDICATED
  2511. EngineVGui()->UpdateProgressBar(PROGRESS_PROCESSSTRINGTABLE);
  2512. #endif
  2513. COM_TimestampedLog( " CBaseClient::ProcessCreateStringTable(%s)", msg.name().c_str() );
  2514. m_StringTableContainer->AllowCreation( true );
  2515. #ifndef SHARED_NET_STRING_TABLES
  2516. CNetworkStringTable *table = (CNetworkStringTable*)
  2517. m_StringTableContainer->CreateStringTable( msg.name().c_str(), msg.max_entries(), msg.user_data_size(), msg.user_data_size_bits(), msg.flags() );
  2518. Assert ( table );
  2519. table->SetTick( GetServerTickCount() ); // set creation tick
  2520. HookClientStringTable( msg.name().c_str() );
  2521. bf_read data( &msg.string_data()[0], msg.string_data().size() );
  2522. table->ParseUpdate( data, msg.num_entries() );
  2523. #endif
  2524. m_StringTableContainer->AllowCreation( false );
  2525. COM_TimestampedLog( " CBaseClient::ProcessCreateStringTable(%s)-done", msg.name().c_str() );
  2526. return true;
  2527. }
  2528. bool CBaseClientState::SVCMsg_UpdateStringTable( const CSVCMsg_UpdateStringTable &msg )
  2529. {
  2530. VPROF( "ProcessUpdateStringTable" );
  2531. #ifndef SHARED_NET_STRING_TABLES
  2532. //m_StringTableContainer is NULL on level transitions, Seems to be caused by a UpdateStringTable packet comming in before the ServerInfo packet
  2533. // I'm not sure this is safe, but at least we won't crash. The realy odd thing is this can happen on the server as well.//tmauer
  2534. if(m_StringTableContainer != NULL)
  2535. {
  2536. CNetworkStringTable *table = (CNetworkStringTable*)
  2537. m_StringTableContainer->GetTable( msg.table_id() );
  2538. bf_read data( &msg.string_data()[0], msg.string_data().size() );
  2539. table->ParseUpdate( data, msg.num_changed_entries() );
  2540. }
  2541. else
  2542. {
  2543. Warning("m_StringTableContainer is NULL in CBaseClientState::ProcessUpdateStringTable\n");
  2544. }
  2545. #endif
  2546. return true;
  2547. }
  2548. bool CBaseClientState::SVCMsg_SetView( const CSVCMsg_SetView& msg )
  2549. {
  2550. #if !defined( LINUX )
  2551. // dkorus: may need this for online split screen
  2552. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  2553. #endif
  2554. m_nViewEntity = msg.entity_index();
  2555. return true;
  2556. }
  2557. bool CBaseClientState::SVCMsg_PacketEntities( const CSVCMsg_PacketEntities &msg )
  2558. {
  2559. VPROF( "ProcessPacketEntities" );
  2560. // First update is the final signon stage where we actually receive an entity (i.e., the world at least)
  2561. if ( m_nSignonState < SIGNONSTATE_SPAWN )
  2562. {
  2563. ConMsg("Received packet entities while connecting!\n");
  2564. return false;
  2565. }
  2566. if ( m_nSignonState == SIGNONSTATE_SPAWN )
  2567. {
  2568. if ( !msg.is_delta() )
  2569. {
  2570. // We are done with signon sequence.
  2571. SetSignonState( SIGNONSTATE_FULL, m_nServerCount, NULL );
  2572. }
  2573. else
  2574. {
  2575. ConMsg("Received delta packet entities while spawing!\n");
  2576. return false;
  2577. }
  2578. }
  2579. // overwrite a -1 delta_tick only if packet was uncompressed
  2580. if ( (m_nDeltaTick >= 0) || !msg.is_delta() )
  2581. {
  2582. // we received this snapshot successfully, now this is our delta reference
  2583. m_nDeltaTick = GetServerTickCount();
  2584. }
  2585. return true;
  2586. }
  2587. //-----------------------------------------------------------------------------
  2588. // Purpose:
  2589. // Input : *pHead -
  2590. // *pClassName -
  2591. // Output : static ClientClass*
  2592. //-----------------------------------------------------------------------------
  2593. ClientClass* CBaseClientState::FindClientClass(const char *pClassName)
  2594. {
  2595. for(ClientClass *pCur=ClientDLL_GetAllClasses(); pCur; pCur=pCur->m_pNext)
  2596. {
  2597. if( Q_stricmp(pCur->m_pNetworkName, pClassName) == 0)
  2598. return pCur;
  2599. }
  2600. return NULL;
  2601. }
  2602. bool CBaseClientState::LinkClasses()
  2603. {
  2604. // Verify that we have received info about all classes.
  2605. for ( int i=0; i < m_nServerClasses; i++ )
  2606. {
  2607. if ( !m_pServerClasses[i].m_DatatableName )
  2608. {
  2609. Host_EndGame(true, "CL_ParseClassInfo_EndClasses: class %d not initialized.\n", i);
  2610. return false;
  2611. }
  2612. }
  2613. // Match the server classes to the client classes.
  2614. for ( int i=0; i < m_nServerClasses; i++ )
  2615. {
  2616. C_ServerClassInfo *pServerClass = &m_pServerClasses[i];
  2617. // (this can be null in which case we just use default behavior).
  2618. pServerClass->m_pClientClass = FindClientClass(pServerClass->m_ClassName);
  2619. if ( pServerClass->m_pClientClass )
  2620. {
  2621. // If the class names match, then their datatables must match too.
  2622. // It's ok if the client is missing a class that the server has. In that case,
  2623. // if the server actually tries to use it, the client will bomb out.
  2624. const char *pServerName = pServerClass->m_DatatableName;
  2625. const char *pClientName = pServerClass->m_pClientClass->m_pRecvTable->GetName();
  2626. if ( Q_stricmp( pServerName, pClientName ) != 0 )
  2627. {
  2628. Host_EndGame( true, "CL_ParseClassInfo_EndClasses: server and client classes for '%s' use different datatables (server: %s, client: %s)",
  2629. pServerClass->m_ClassName, pServerName, pClientName );
  2630. return false;
  2631. }
  2632. // copy class ID
  2633. pServerClass->m_pClientClass->m_ClassID = i;
  2634. }
  2635. else
  2636. {
  2637. Msg( "Client missing DT class %s\n", pServerClass->m_ClassName );
  2638. }
  2639. }
  2640. return true;
  2641. }
  2642. PackedEntity *CBaseClientState::GetEntityBaseline(int iBaseline, int nEntityIndex)
  2643. {
  2644. Assert( (iBaseline == 0) || (iBaseline == 1) );
  2645. return m_pEntityBaselines[iBaseline][nEntityIndex];
  2646. }
  2647. void CBaseClientState::FreeEntityBaselines()
  2648. {
  2649. for ( int i=0; i<2; i++ )
  2650. {
  2651. for ( int j=0; j<MAX_EDICTS; j++ )
  2652. if ( m_pEntityBaselines[i][j] )
  2653. {
  2654. delete m_pEntityBaselines[i][j];
  2655. m_pEntityBaselines[i][j] = NULL;
  2656. }
  2657. }
  2658. }
  2659. void CBaseClientState::SetEntityBaseline(int iBaseline, ClientClass *pClientClass, int index, SerializedEntityHandle_t handle)
  2660. {
  2661. Assert( index >= 0 && index < MAX_EDICTS );
  2662. Assert( pClientClass );
  2663. Assert( (iBaseline == 0) || (iBaseline == 1) );
  2664. PackedEntity *entitybl = m_pEntityBaselines[iBaseline][index];
  2665. if ( !entitybl )
  2666. {
  2667. entitybl = m_pEntityBaselines[iBaseline][index] = new PackedEntity();
  2668. }
  2669. entitybl->m_pClientClass = pClientClass;
  2670. entitybl->m_nEntityIndex = index;
  2671. entitybl->m_pServerClass = NULL;
  2672. // Copy out the data we just decoded.
  2673. entitybl->SetPackedData( handle );
  2674. }
  2675. void CBaseClientState::CopyEntityBaseline( int iFrom, int iTo )
  2676. {
  2677. Assert ( iFrom != iTo );
  2678. for ( int i=0; i<MAX_EDICTS; i++ )
  2679. {
  2680. PackedEntity *blfrom = m_pEntityBaselines[iFrom][i];
  2681. PackedEntity *blto = m_pEntityBaselines[iTo][i];
  2682. if( !blfrom )
  2683. {
  2684. // make sure blto doesn't exists
  2685. if ( blto )
  2686. {
  2687. // ups, we already had this entity but our ack got lost
  2688. // we have to remove it again to stay in sync
  2689. delete m_pEntityBaselines[iTo][i];
  2690. m_pEntityBaselines[iTo][i] = NULL;
  2691. }
  2692. continue;
  2693. }
  2694. if ( !blto )
  2695. {
  2696. // create new to baseline if none existed before
  2697. blto = m_pEntityBaselines[iTo][i] = new PackedEntity();
  2698. blto->m_pClientClass = NULL;
  2699. blto->m_pServerClass = NULL;
  2700. blto->m_ReferenceCount = 0;
  2701. }
  2702. Assert( blfrom->m_nEntityIndex == i );
  2703. blto->m_nEntityIndex = blfrom->m_nEntityIndex;
  2704. blto->m_pClientClass = blfrom->m_pClientClass;
  2705. blto->m_pServerClass = blfrom->m_pServerClass;
  2706. blto->CopyPackedData( blfrom->GetPackedData() );
  2707. }
  2708. }
  2709. ClientClass *CBaseClientState::GetClientClass( int index )
  2710. {
  2711. Assert( index < m_nServerClasses );
  2712. return m_pServerClasses[index].m_pClientClass;
  2713. }
  2714. void CBaseClientState::UpdateInstanceBaseline( int nStringNumber )
  2715. {
  2716. int nSlot = m_BaselineHandles.Find( nStringNumber );
  2717. if ( nSlot != m_BaselineHandles.InvalidIndex() )
  2718. {
  2719. // Release old
  2720. g_pSerializedEntities->ReleaseSerializedEntity( m_BaselineHandles[ nSlot ] );
  2721. m_BaselineHandles[ nSlot ] = SERIALIZED_ENTITY_HANDLE_INVALID;
  2722. }
  2723. else
  2724. {
  2725. m_BaselineHandles.Insert( nStringNumber, SERIALIZED_ENTITY_HANDLE_INVALID );
  2726. }
  2727. }
  2728. bool CBaseClientState::GetClassBaseline( int iClass, SerializedEntityHandle_t *pHandle )
  2729. {
  2730. ErrorIfNot(
  2731. iClass >= 0 && iClass < m_nServerClasses,
  2732. ("GetDynamicBaseline: invalid class index '%d'", iClass) );
  2733. // We lazily update these because if you connect to a server that's already got some dynamic baselines,
  2734. // you'll get the baselines BEFORE you get the class descriptions.
  2735. C_ServerClassInfo *pInfo = &m_pServerClasses[iClass];
  2736. INetworkStringTable *pBaselineTable = GetStringTable( INSTANCE_BASELINE_TABLENAME );
  2737. ErrorIfNot( pBaselineTable != NULL, ("GetDynamicBaseline: NULL baseline table" ) );
  2738. if ( pInfo->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  2739. {
  2740. // The key is the class index string.
  2741. char str[64];
  2742. Q_snprintf( str, sizeof( str ), "%d", iClass );
  2743. pInfo->m_InstanceBaselineIndex = pBaselineTable->FindStringIndex( str );
  2744. ErrorIfNot(
  2745. pInfo->m_InstanceBaselineIndex != INVALID_STRING_INDEX,
  2746. ("GetDynamicBaseline: FindStringIndex(%s-%s) failed.", str, pInfo->m_ClassName );
  2747. );
  2748. }
  2749. int slot = m_BaselineHandles.Find( pInfo->m_InstanceBaselineIndex );
  2750. Assert( slot != m_BaselineHandles.InvalidIndex() );
  2751. *pHandle = m_BaselineHandles[ slot ];
  2752. if ( *pHandle == SERIALIZED_ENTITY_HANDLE_INVALID )
  2753. {
  2754. SerializedEntityHandle_t handle = g_pSerializedEntities->AllocateSerializedEntity( __FILE__, __LINE__ );
  2755. int nLength = 0;
  2756. const void *pData = pBaselineTable->GetStringUserData( pInfo->m_InstanceBaselineIndex, &nLength );
  2757. bf_read readbuf( "UpdateInstanceBaseline", pData, nLength );
  2758. RecvTable_ReadFieldList( pInfo->m_pClientClass->m_pRecvTable, readbuf, handle, -1, false );
  2759. *pHandle = m_BaselineHandles[ slot ] = handle;
  2760. }
  2761. return *pHandle != SERIALIZED_ENTITY_HANDLE_INVALID;
  2762. }
  2763. bool CBaseClientState::SVCMsg_GameEventList( const CSVCMsg_GameEventList& msg )
  2764. {
  2765. VPROF( "SVCMsg_GameEventList" );
  2766. return g_GameEventManager.ParseEventList( msg );
  2767. }
  2768. bool CBaseClientState::SVCMsg_GetCvarValue( const CSVCMsg_GetCvarValue& msg )
  2769. {
  2770. VPROF( "SVCMsg_GetCvarValue" );
  2771. // Prepare the response.
  2772. CCLCMsg_RespondCvarValue_t returnMsg;
  2773. returnMsg.set_cookie( msg.cookie() );
  2774. returnMsg.set_name( msg.cvar_name().c_str() );
  2775. returnMsg.set_value( "" );
  2776. returnMsg.set_status_code( eQueryCvarValueStatus_CvarNotFound );
  2777. char tempValue[256];
  2778. // Does any ConCommand exist with this name?
  2779. const ConVar *pVar = g_pCVar->FindVar( msg.cvar_name().c_str() );
  2780. if ( pVar )
  2781. {
  2782. if ( pVar->IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
  2783. {
  2784. // The server isn't allowed to query this.
  2785. returnMsg.set_status_code( eQueryCvarValueStatus_CvarProtected );
  2786. }
  2787. else
  2788. {
  2789. returnMsg.set_status_code( eQueryCvarValueStatus_ValueIntact );
  2790. if ( pVar->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  2791. {
  2792. // The cvar won't store a string, so we have to come up with a string for it ourselves.
  2793. if ( fabs( pVar->GetFloat() - pVar->GetInt() ) < 0.001f )
  2794. {
  2795. Q_snprintf( tempValue, sizeof( tempValue ), "%d", pVar->GetInt() );
  2796. }
  2797. else
  2798. {
  2799. Q_snprintf( tempValue, sizeof( tempValue ), "%f", pVar->GetFloat() );
  2800. }
  2801. returnMsg.set_value( tempValue );
  2802. }
  2803. else
  2804. {
  2805. // The easy case..
  2806. returnMsg.set_value( pVar->GetString() );
  2807. }
  2808. }
  2809. }
  2810. else
  2811. {
  2812. if ( g_pCVar->FindCommand( msg.cvar_name().c_str() ) )
  2813. returnMsg.set_status_code( eQueryCvarValueStatus_NotACvar ); // It's a command, not a cvar.
  2814. else
  2815. returnMsg.set_status_code( eQueryCvarValueStatus_CvarNotFound );
  2816. }
  2817. // Send back.
  2818. m_NetChannel->SendNetMsg( returnMsg );
  2819. return true;
  2820. }
  2821. bool CBaseClientState::SVCMsg_SplitScreen( const CSVCMsg_SplitScreen& msg )
  2822. {
  2823. #ifndef DEDICATED
  2824. switch ( msg.type() )
  2825. {
  2826. default:
  2827. Assert( 0 );
  2828. break;
  2829. case MSG_SPLITSCREEN_ADDUSER:
  2830. {
  2831. splitscreen->AddSplitScreenUser( msg.slot(), msg.player_index() );
  2832. #if defined( INCLUDE_SCALEFORM )
  2833. extern IScaleformSlotInitController *g_pIScaleformSlotInitControllerEngineImpl;
  2834. g_pScaleformUI->InitSlot( SF_SS_SLOT( msg.slot() ), g_szDefaultScaleformClientMovieName, g_pIScaleformSlotInitControllerEngineImpl );
  2835. #endif
  2836. }
  2837. break;
  2838. case MSG_SPLITSCREEN_REMOVEUSER:
  2839. {
  2840. splitscreen->RemoveSplitScreenUser( msg.slot(), msg.player_index() );
  2841. #if defined( INCLUDE_SCALEFORM )
  2842. g_pScaleformUI->SlotRelease( SF_SS_SLOT( msg.slot() ) );
  2843. #endif
  2844. }
  2845. break;
  2846. }
  2847. #endif
  2848. return true;
  2849. }
  2850. bool CBaseClientState::ChangeSplitscreenUser( int nSplitScreenUserSlot )
  2851. {
  2852. #ifndef DEDICATED
  2853. Assert( splitscreen->IsValidSplitScreenSlot( nSplitScreenUserSlot ) );
  2854. if ( !splitscreen->IsValidSplitScreenSlot( nSplitScreenUserSlot ) )
  2855. return true;
  2856. // Msg( "Networking changing slot to %d\n", msg->m_nSlot );
  2857. splitscreen->SetActiveSplitScreenPlayerSlot( nSplitScreenUserSlot );
  2858. #endif
  2859. return true;
  2860. }
  2861. bool CBaseClientState::SVCMsg_CmdKeyValues( const CSVCMsg_CmdKeyValues& msg )
  2862. {
  2863. #ifndef DEDICATED
  2864. KeyValues *pMsgKeyValues = CmdKeyValuesHelper::SVCMsg_GetKeyValues( msg );
  2865. KeyValues::AutoDelete autodelete_pMsgKeyValues( pMsgKeyValues );
  2866. char const *szName = pMsgKeyValues->GetName();
  2867. if ( !V_strcmp( szName, "dsp_player" ) )
  2868. {
  2869. extern void dsp_player_set( int val );
  2870. dsp_player_set( pMsgKeyValues->GetInt() );
  2871. return true;
  2872. }
  2873. KeyValues *pEvent = new KeyValues( "Client::CmdKeyValues" );
  2874. pEvent->AddSubKey( autodelete_pMsgKeyValues.Detach() );
  2875. pEvent->SetInt( "slot", m_nSplitScreenSlot );
  2876. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pEvent );
  2877. #endif
  2878. return true;
  2879. }
  2880. bool CBaseClientState::SVCMsg_EncryptedData( const CSVCMsg_EncryptedData& msg )
  2881. {
  2882. #ifndef DEDICATED
  2883. // Decrypt the message and process embedded data
  2884. char const *szKey = "";
  2885. switch ( msg.key_type() )
  2886. {
  2887. case kEncryptedMessageKeyType_Private:
  2888. szKey = cl_decryptdata_key.GetString();
  2889. break;
  2890. case kEncryptedMessageKeyType_Public:
  2891. szKey = cl_decryptdata_key_pub.GetString();
  2892. break;
  2893. }
  2894. return CmdEncryptedDataMessageCodec::SVCMsg_EncryptedData_Process( msg, m_NetChannel, szKey );
  2895. #else
  2896. return true;
  2897. #endif
  2898. }
  2899. int CBaseClientState::GetViewEntity()
  2900. {
  2901. return m_nViewEntity;
  2902. }
  2903. bool CBaseClientState::ShouldUseDirectConnectAddress( const CAddressList &list ) const
  2904. {
  2905. // Expired?
  2906. if ( realtime > m_DirectConnectLobby.m_flEndTime )
  2907. return false;
  2908. // No server IP?
  2909. if ( m_DirectConnectLobby.m_adrRemote.IsType<netadr_t>() && !m_DirectConnectLobby.m_adrRemote.AsType<netadr_t>().GetIPHostByteOrder() )
  2910. return false;
  2911. // Either joining unreserved server or same lobby ID
  2912. if ( m_DirectConnectLobby.m_unLobbyID != 0ull && m_DirectConnectLobby.m_unLobbyID != m_nServerReservationCookie )
  2913. return false;
  2914. // Already in list
  2915. if ( list.IsAddressInList( m_DirectConnectLobby.m_adrRemote ) )
  2916. return false;
  2917. return true;
  2918. }
  2919. uint64 CBaseClientState::CAsyncOperation_ReserveServer::GetResult() {
  2920. if ( m_eState != AOS_SUCCEEDED )
  2921. return 0;
  2922. static char buf[64];
  2923. V_strcpy_safe( buf, ns_address_render( m_adr ).String() );
  2924. return reinterpret_cast<uintp>( buf );
  2925. }
  2926. //-----------------------------------------------------------------------------
  2927. // Purpose: Sends a message to game server to reserve it for members of our
  2928. // lobby for a short time to ensure everyone in lobby will be able to join.
  2929. // Server will send response accepting or denying reservation. It will only
  2930. // accept if it is empty and unreserved.
  2931. //-----------------------------------------------------------------------------
  2932. void CBaseClientState::ReserveServer( const ns_address &netAdrPublic, const ns_address &netAdrPrivate, uint64 nServerReservationCookie,
  2933. KeyValues *pKVGameSettings, IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation )
  2934. {
  2935. NET_SetMultiplayer( true );
  2936. // Should not already have a reservation in progress -- should only attempt one reservation at a time
  2937. Assert( !m_pServerReservationCallback );
  2938. if ( m_pServerReservationCallback )
  2939. {
  2940. ReservationResponseReply_t reply;
  2941. reply.m_adrFrom = m_netadrReserveServer.Get( 0 ).m_adrRemote;
  2942. HandleReservationResponse( reply );
  2943. }
  2944. if ( ppAsyncOperation )
  2945. {
  2946. m_pServerReservationOperation = new CAsyncOperation_ReserveServer( this );
  2947. *ppAsyncOperation = m_pServerReservationOperation;
  2948. }
  2949. m_pServerReservationCallback = pCallback;
  2950. m_nServerReservationCookie = nServerReservationCookie;
  2951. m_pKVGameSettings = pKVGameSettings->MakeCopy();
  2952. m_flReservationMsgSendTime = FLT_MIN;
  2953. m_nReservationMsgRetryNumber = 0;
  2954. m_bEnteredPassword = false;
  2955. m_netadrReserveServer.RemoveAll();
  2956. m_netadrReserveServer.AddRemote( ns_address_render( netAdrPublic ).String(), "public" );
  2957. if ( !netAdrPrivate.IsNull() ) // if we have a valid private address specified
  2958. m_netadrReserveServer.AddRemote( ns_address_render( netAdrPrivate ).String(), "private" );
  2959. if ( ShouldUseDirectConnectAddress( m_netadrReserveServer ) )
  2960. {
  2961. ConColorMsg( Color( 0, 255, 0, 255 ), "Adding direct connect address to reservation %s\n", ns_address_render( m_DirectConnectLobby.m_adrRemote ).String() );
  2962. m_netadrReserveServer.AddRemote( ns_address_render( m_DirectConnectLobby.m_adrRemote ).String(), "direct" );
  2963. }
  2964. // send the reservation message
  2965. SendReserveServerMsg();
  2966. }
  2967. bool CBaseClientState::CheckServerReservation( const ns_address &netAdrPublic, uint64 nServerReservationCookie, uint32 uiReservationStage,
  2968. IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation )
  2969. {
  2970. Assert( ppAsyncOperation );
  2971. if ( !ppAsyncOperation )
  2972. return false;
  2973. NET_SetMultiplayer( true );
  2974. CServerMsg_CheckReservation *pSvReservationCheck = new CServerMsg_CheckReservation( this, pCallback, netAdrPublic,
  2975. m_Socket, nServerReservationCookie, uiReservationStage );
  2976. *ppAsyncOperation = pSvReservationCheck;
  2977. m_arrSvReservationCheck.AddToTail( pSvReservationCheck );
  2978. return true;
  2979. }
  2980. bool CBaseClientState::ServerPing( const ns_address &netAdrPublic,
  2981. IMatchAsyncOperationCallback *pCallback, IMatchAsyncOperation **ppAsyncOperation )
  2982. {
  2983. Assert( ppAsyncOperation );
  2984. if ( !ppAsyncOperation )
  2985. return false;
  2986. NET_SetMultiplayer( true );
  2987. CServerMsg_Ping *pSvPing = new CServerMsg_Ping( this, pCallback, netAdrPublic, m_Socket );
  2988. *ppAsyncOperation = pSvPing;
  2989. m_arrSvPing.AddToTail( pSvPing );
  2990. return true;
  2991. }
  2992. //-----------------------------------------------------------------------------
  2993. // Purpose: Handles response from game server to reservation request
  2994. //-----------------------------------------------------------------------------
  2995. void CBaseClientState::HandleReservationResponse( const ReservationResponseReply_t &reply )
  2996. {
  2997. // Is this the address we expect? (It might not if this is a very delayed response
  2998. // from an earlier game server we tried to reserve on and subsequently gave up on.)
  2999. bool bAddressMatches = ( reply.m_adrFrom.IsLoopback() || m_netadrReserveServer.IsAddressInList( reply.m_adrFrom ) );
  3000. if ( !bAddressMatches )
  3001. return;
  3002. IMatchAsyncOperationCallback *pCallback = m_pServerReservationCallback;
  3003. if ( reply.m_uiResponse != 2 )
  3004. {
  3005. m_pServerReservationCallback = NULL;
  3006. if ( m_pKVGameSettings )
  3007. {
  3008. m_pKVGameSettings->deleteThis();
  3009. m_pKVGameSettings = NULL;
  3010. }
  3011. }
  3012. if ( !pCallback )
  3013. return;
  3014. // If we are expecting a response (have a callback to call) and the address matches what
  3015. // we expect, call the callback then clear it.
  3016. if ( m_pServerReservationOperation )
  3017. {
  3018. if ( reply.m_uiResponse == 2 )
  3019. {
  3020. // Reservation pending response, reset retry counter
  3021. m_nReservationMsgRetryNumber = 0;
  3022. DevMsg( "[MM] Server %s reservation pending response waiting...\n", ns_address_render( reply.m_adrFrom ).String() );
  3023. return;
  3024. }
  3025. m_pServerReservationOperation->m_eState = reply.m_uiResponse ? AOS_SUCCEEDED : AOS_FAILED;
  3026. m_pServerReservationOperation->m_adr = reply.m_adrFrom;
  3027. m_pServerReservationOperation->m_numGameSlotsForReservation = reply.m_numGameSlots;
  3028. }
  3029. pCallback->OnOperationFinished( m_pServerReservationOperation );
  3030. }
  3031. //-----------------------------------------------------------------------------
  3032. // Purpose: Resend a server reservation request if necessary
  3033. //-----------------------------------------------------------------------------
  3034. void CBaseClientState::CheckForReservationResend()
  3035. {
  3036. const float RESERVATION_RESEND_INTERVAL=3.0f;
  3037. const float MAX_RESERVATION_RETRIES=2;
  3038. if ( m_bWaitingForPassword )
  3039. return;
  3040. // do we have a reservation in progress?
  3041. if ( !m_pServerReservationCallback )
  3042. return;
  3043. // is it time to resend?
  3044. if ( ( net_time - m_flReservationMsgSendTime ) < RESERVATION_RESEND_INTERVAL )
  3045. return;
  3046. // fail if too many resends
  3047. if ( m_nReservationMsgRetryNumber >= MAX_RESERVATION_RETRIES )
  3048. {
  3049. CUtlString desc;
  3050. m_netadrReserveServer.Describe( desc );
  3051. Msg( "[MM] Attempt to reserve server %s failed; timed out after %d attempts\n", desc.String(), m_nReservationMsgRetryNumber + 1 );
  3052. ReservationResponseReply_t reply;
  3053. reply.m_adrFrom = m_netadrReserveServer.Get( 0 ).m_adrRemote;
  3054. HandleReservationResponse( reply );
  3055. return;
  3056. }
  3057. m_nReservationMsgRetryNumber++;
  3058. SendReserveServerMsg();
  3059. }
  3060. void CBaseClientState::SendReserveServerChallenge()
  3061. {
  3062. // Send to master asking for a challenge #
  3063. for ( int i = 0; i < m_netadrReserveServer.Count(); ++i )
  3064. {
  3065. Msg( "[MM] Sending reservation request to %s\n", ns_address_render( m_netadrReserveServer.Get( i ).m_adrRemote ).String() );
  3066. NET_OutOfBandDelayedPrintf( m_Socket, m_netadrReserveServer.Get( i ).m_adrRemote, GetPrivateIPDelayMsecs() * i, "%creserve0000000", A2S_GETCHALLENGE );
  3067. }
  3068. // Mark time of this attempt.
  3069. m_flReservationMsgSendTime = net_time; // for retransmit requests
  3070. }
  3071. void CBaseClientState::HandleReserveServerChallengeResponse( int nChallengeNr )
  3072. {
  3073. if ( !m_pServerReservationCallback )
  3074. return;
  3075. char buffer[MAX_OOB_KEYVALUES+128];
  3076. bf_write msg(buffer,sizeof(buffer));
  3077. msg.WriteLong( CONNECTIONLESS_HEADER );
  3078. msg.WriteByte( A2S_RESERVE );
  3079. msg.WriteLong( GetHostVersion() );
  3080. BuildReserveServerPayload( msg, nChallengeNr );
  3081. for ( int i = 0; i < m_netadrReserveServer.Count(); ++i )
  3082. {
  3083. NET_SendPacket( NULL, m_Socket, m_netadrReserveServer.Get( i ).m_adrRemote, msg.GetData(), msg.GetNumBytesWritten() );
  3084. }
  3085. }
  3086. //-----------------------------------------------------------------------------
  3087. // Purpose: encrypts an 8-byte sequence
  3088. //-----------------------------------------------------------------------------
  3089. inline void Encrypt8ByteSequence( IceKey& cipher, const unsigned char *plainText, unsigned char *cipherText)
  3090. {
  3091. cipher.encrypt(plainText, cipherText);
  3092. }
  3093. //-----------------------------------------------------------------------------
  3094. // Purpose:
  3095. //-----------------------------------------------------------------------------
  3096. void EncryptBuffer( IceKey& cipher, unsigned char *bufData, uint bufferSize)
  3097. {
  3098. unsigned char *cipherText = bufData;
  3099. unsigned char *plainText = bufData;
  3100. uint bytesEncrypted = 0;
  3101. while (bytesEncrypted < bufferSize)
  3102. {
  3103. // encrypt 8 byte section
  3104. Encrypt8ByteSequence( cipher, plainText, cipherText);
  3105. bytesEncrypted += 8;
  3106. cipherText += 8;
  3107. plainText += 8;
  3108. }
  3109. }
  3110. void CBaseClientState::BuildReserveServerPayload( bf_write &msg, int nChallengeNr )
  3111. {
  3112. char buffer[MAX_OOB_KEYVALUES+128];
  3113. bf_write payload(buffer,sizeof(buffer));
  3114. if ( !IsX360() )
  3115. {
  3116. // Magic # to ensure icey decrypt worked
  3117. payload.WriteLong( 0xfeedbeef );
  3118. }
  3119. // send the cookie that everyone in the joining party will provide to let them into the reserved server
  3120. payload.WriteLongLong( m_nServerReservationCookie );
  3121. int nSettingsLength = 0;
  3122. CUtlBuffer buf;
  3123. //this buffer needs to be endian compliant sot he X360 can talk correctly to the PC Dedicated server.
  3124. if( buf.IsBigEndian() )
  3125. {
  3126. buf.SetBigEndian( false );
  3127. }
  3128. if ( m_pKVGameSettings )
  3129. {
  3130. // if we have KeyValues with game settings, convert to binary blob
  3131. m_pKVGameSettings->WriteAsBinary( buf );
  3132. nSettingsLength = buf.TellPut();
  3133. // make sure it's not going to overflow one UDP packet
  3134. Assert( nSettingsLength <= MAX_OOB_KEYVALUES );
  3135. if ( nSettingsLength > MAX_OOB_KEYVALUES )
  3136. {
  3137. ReservationResponseReply_t reply;
  3138. reply.m_adrFrom = m_netadrReserveServer.Get( 0 ).m_adrRemote;
  3139. HandleReservationResponse( reply );
  3140. return;
  3141. }
  3142. }
  3143. // write # of bytes in game settings keyvalues
  3144. payload.WriteLong( nSettingsLength );
  3145. if ( nSettingsLength > 0 )
  3146. {
  3147. // write game setting keyvalues
  3148. payload.WriteBytes( buf.Base(), nSettingsLength );
  3149. }
  3150. if ( !IsX360() )
  3151. {
  3152. // Pad it to multiple of 8 bytes
  3153. while ( payload.GetNumBytesWritten() % 8 )
  3154. {
  3155. payload.WriteByte( 0 );
  3156. }
  3157. IceKey cipher(1); /* medium encryption level */
  3158. unsigned char ucEncryptionKey[8] = { 0 };
  3159. *( int * )&ucEncryptionKey[ 0 ] = LittleDWord( nChallengeNr ^ 0x5ef8ce12 );
  3160. *( int * )&ucEncryptionKey[ 4 ] = LittleDWord( nChallengeNr ^ 0xaa98e42c );
  3161. cipher.set( ucEncryptionKey );
  3162. EncryptBuffer( cipher, (byte *)payload.GetBasePointer(), payload.GetNumBytesWritten() );
  3163. msg.WriteLong( payload.GetNumBytesWritten() );
  3164. }
  3165. msg.WriteBytes( payload.GetBasePointer(), payload.GetNumBytesWritten() );
  3166. }
  3167. //-----------------------------------------------------------------------------
  3168. // Purpose: Sends a server reservation request
  3169. //-----------------------------------------------------------------------------
  3170. void CBaseClientState::SendReserveServerMsg()
  3171. {
  3172. if ( !IsX360() )
  3173. {
  3174. // The PC uses a more complicated challenge response system to prevent DDoS style attacks
  3175. SendReserveServerChallenge();
  3176. return;
  3177. }
  3178. Assert( m_pServerReservationCallback );
  3179. char buffer[MAX_OOB_KEYVALUES+128];
  3180. bf_write msg(buffer,sizeof(buffer));
  3181. msg.WriteLong( CONNECTIONLESS_HEADER );
  3182. msg.WriteByte( A2S_RESERVE );
  3183. msg.WriteLong( GetHostVersion() );
  3184. BuildReserveServerPayload( msg, 0 );
  3185. for ( int i = 0; i < m_netadrReserveServer.Count(); ++i )
  3186. {
  3187. NET_SendPacket( NULL, m_Socket, m_netadrReserveServer.Get( i ).m_adrRemote, msg.GetData(), msg.GetNumBytesWritten() );
  3188. }
  3189. // Mark time of this attempt.
  3190. m_flReservationMsgSendTime = net_time; // for retransmit requests
  3191. }
  3192. #ifndef DEDICATED
  3193. CSetActiveSplitScreenPlayerGuard::CSetActiveSplitScreenPlayerGuard( char const *pchContext, int nLine, int slot )
  3194. {
  3195. m_pchContext = pchContext;
  3196. m_nLine = nLine;
  3197. m_nSaveSlot = splitscreen->SetActiveSplitScreenPlayerSlot( slot );
  3198. m_bResolvable = splitscreen->SetLocalPlayerIsResolvable( pchContext, nLine, true );
  3199. }
  3200. CSetActiveSplitScreenPlayerGuard::~CSetActiveSplitScreenPlayerGuard()
  3201. {
  3202. splitscreen->SetActiveSplitScreenPlayerSlot( m_nSaveSlot );
  3203. splitscreen->SetLocalPlayerIsResolvable( m_pchContext, m_nLine, m_bResolvable );
  3204. }
  3205. #endif