Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1855 lines
51 KiB

  1. //========= Copyright 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 "vstdlib/random.h"
  9. #include <inetchannel.h>
  10. #include <netmessages.h>
  11. #include <proto_oob.h>
  12. #include <ctype.h>
  13. #include "cl_main.h"
  14. #include "net.h"
  15. #include "dt_recv_eng.h"
  16. #include "ents_shared.h"
  17. #include "net_synctags.h"
  18. #include "filesystem_engine.h"
  19. #include "host_cmd.h"
  20. #include "GameEventManager.h"
  21. #include "sv_rcon.h"
  22. #include "cl_rcon.h"
  23. #ifndef SWDS
  24. #include "vgui_baseui_interface.h"
  25. #include "cl_pluginhelpers.h"
  26. #include "vgui_askconnectpanel.h"
  27. #endif
  28. #include "sv_steamauth.h"
  29. #include "tier0/icommandline.h"
  30. #include "tier0/vcrmode.h"
  31. #include "snd_audio_source.h"
  32. #include "cl_steamauth.h"
  33. #include "server.h"
  34. #include "steam/steam_api.h"
  35. #include "matchmaking.h"
  36. #include "sv_plugin.h"
  37. #include "sys_dll.h"
  38. #include "host.h"
  39. #if defined( REPLAY_ENABLED )
  40. #include "replay_internal.h"
  41. #include "replayserver.h"
  42. #endif
  43. // memdbgon must be the last include file in a .cpp file!!!
  44. #include "tier0/memdbgon.h"
  45. #ifdef ENABLE_RPT
  46. void CL_NotifyRPTOfDisconnect( );
  47. #endif // ENABLE_RPT
  48. #if !defined( NO_STEAM )
  49. void UpdateNameFromSteamID( IConVar *pConVar, CSteamID *pSteamID )
  50. {
  51. #ifndef SWDS
  52. if ( !pConVar || !pSteamID || !Steam3Client().SteamFriends() )
  53. return;
  54. const char *pszName = Steam3Client().SteamFriends()->GetFriendPersonaName( *pSteamID );
  55. pConVar->SetValue( pszName );
  56. #endif // SWDS
  57. }
  58. void SetNameToSteamIDName( IConVar *pConVar )
  59. {
  60. #ifndef SWDS
  61. if ( Steam3Client().SteamUtils() && Steam3Client().SteamFriends() && Steam3Client().SteamUser() )
  62. {
  63. CSteamID steamID = Steam3Client().SteamUser()->GetSteamID();
  64. UpdateNameFromSteamID( pConVar, &steamID );
  65. }
  66. #endif // SWDS
  67. }
  68. #endif // NO_STEAM
  69. void CL_NameCvarChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
  70. {
  71. ConVarRef var( pConVar );
  72. static bool bPreventRent = false;
  73. if ( !bPreventRent )
  74. {
  75. bPreventRent = true;
  76. #if !defined( NO_STEAM )
  77. SetNameToSteamIDName( pConVar );
  78. #endif
  79. // Remove any evil characters (ex: zero width no-break space)
  80. char pchName[MAX_PLAYER_NAME_LENGTH];
  81. V_strcpy_safe( pchName, var.GetString() );
  82. Q_RemoveAllEvilCharacters( pchName );
  83. pConVar->SetValue( pchName );
  84. // store off the last known name, that isn't default, in the registry
  85. // this is a transition step so it can be used to display in friends
  86. if ( 0 != Q_stricmp( var.GetString(), var.GetDefault() )
  87. && 0 != Q_stricmp( var.GetString(), "player" ) )
  88. {
  89. Sys_SetRegKeyValue( "Software\\Valve\\Steam", "LastGameNameUsed", var.GetString() );
  90. }
  91. bPreventRent = false;
  92. }
  93. }
  94. #ifndef SWDS
  95. void askconnect_accept_f()
  96. {
  97. char szHostName[256];
  98. if ( IsAskConnectPanelActive( szHostName, sizeof( szHostName ) ) )
  99. {
  100. char szCommand[512];
  101. V_snprintf( szCommand, sizeof( szCommand ), "connect %s redirect", szHostName );
  102. Cbuf_AddText( szCommand );
  103. HideAskConnectPanel();
  104. }
  105. }
  106. ConCommand askconnect_accept( "askconnect_accept", askconnect_accept_f, "Accept a redirect request by the server.", FCVAR_DONTRECORD );
  107. #endif
  108. #ifndef SWDS
  109. extern IVEngineClient *engineClient;
  110. // ---------------------------------------------------------------------------------------- //
  111. static void SendClanTag( const char *pTag )
  112. {
  113. KeyValues *kv = new KeyValues( "ClanTagChanged" );
  114. kv->SetString( "tag", pTag );
  115. engineClient->ServerCmdKeyValues( kv );
  116. }
  117. #endif
  118. // ---------------------------------------------------------------------------------------- //
  119. void CL_ClanIdChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
  120. {
  121. #ifndef SWDS
  122. // Get the clan ID we're trying to select
  123. ConVarRef var( pConVar );
  124. uint32 newId = var.GetInt();
  125. if ( newId == 0 )
  126. {
  127. // Default value, equates to no tag
  128. SendClanTag( "" );
  129. return;
  130. }
  131. #if !defined( NO_STEAM )
  132. // Make sure this player is actually part of the desired clan
  133. ISteamFriends *pFriends = Steam3Client().SteamFriends();
  134. if ( pFriends )
  135. {
  136. int iGroupCount = pFriends->GetClanCount();
  137. for ( int k = 0; k < iGroupCount; ++ k )
  138. {
  139. CSteamID clanID = pFriends->GetClanByIndex( k );
  140. if ( clanID.GetAccountID() == newId )
  141. {
  142. // valid clan, accept the change
  143. CSteamID clanIDNew( newId, Steam3Client().SteamUtils()->GetConnectedUniverse(), k_EAccountTypeClan );
  144. SendClanTag( pFriends->GetClanTag( clanIDNew ) );
  145. return;
  146. }
  147. }
  148. }
  149. #endif // NO_STEAM
  150. // Couldn't validate the ID, so clear to the default (no tag)
  151. var.SetValue( 0 );
  152. #endif // !SWDS
  153. }
  154. ConVar cl_resend ( "cl_resend","6", FCVAR_NONE, "Delay in seconds before the client will resend the 'connect' attempt", true, CL_MIN_RESEND_TIME, true, CL_MAX_RESEND_TIME );
  155. ConVar cl_name ( "name","unnamed", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_PRINTABLEONLY | FCVAR_SERVER_CAN_EXECUTE, "Current user name", CL_NameCvarChanged );
  156. ConVar password ( "password", "", FCVAR_ARCHIVE | FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD, "Current server access password" );
  157. ConVar cl_interpolate( "cl_interpolate", "1.0", FCVAR_USERINFO | FCVAR_DEVELOPMENTONLY | FCVAR_NOT_CONNECTED, "Interpolate entities on the client." );
  158. ConVar cl_clanid( "cl_clanid", "0", FCVAR_ARCHIVE | FCVAR_USERINFO | FCVAR_HIDDEN, "Current clan ID for name decoration", CL_ClanIdChanged );
  159. ConVar cl_show_connectionless_packet_warnings( "cl_show_connectionless_packet_warnings", "0", FCVAR_NONE, "Show console messages about ignored connectionless packets on the client." );
  160. // ---------------------------------------------------------------------------------------- //
  161. // C_ServerClassInfo implementation.
  162. // ---------------------------------------------------------------------------------------- //
  163. C_ServerClassInfo::C_ServerClassInfo()
  164. {
  165. m_ClassName = NULL;
  166. m_DatatableName = NULL;
  167. m_InstanceBaselineIndex = INVALID_STRING_INDEX;
  168. }
  169. C_ServerClassInfo::~C_ServerClassInfo()
  170. {
  171. delete [] m_ClassName;
  172. delete [] m_DatatableName;
  173. }
  174. // Returns false if you should stop reading entities.
  175. inline static bool CL_DetermineUpdateType( CEntityReadInfo &u )
  176. {
  177. if ( !u.m_bIsEntity || ( u.m_nNewEntity > u.m_nOldEntity ) )
  178. {
  179. // If we're at the last entity, preserve whatever entities followed it in the old packet.
  180. // If newnum > oldnum, then the server skipped sending entities that it wants to leave the state alone for.
  181. if ( !u.m_pFrom || ( u.m_nOldEntity > u.m_pFrom->last_entity ) )
  182. {
  183. Assert( !u.m_bIsEntity );
  184. u.m_UpdateType = Finished;
  185. return false;
  186. }
  187. // Preserve entities until we reach newnum (ie: the server didn't send certain entities because
  188. // they haven't changed).
  189. u.m_UpdateType = PreserveEnt;
  190. }
  191. else
  192. {
  193. if( u.m_UpdateFlags & FHDR_ENTERPVS )
  194. {
  195. u.m_UpdateType = EnterPVS;
  196. }
  197. else if( u.m_UpdateFlags & FHDR_LEAVEPVS )
  198. {
  199. u.m_UpdateType = LeavePVS;
  200. }
  201. else
  202. {
  203. u.m_UpdateType = DeltaEnt;
  204. }
  205. }
  206. return true;
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose: When a delta command is received from the server
  210. // We need to grab the entity # out of it any the bit settings, too.
  211. // Returns -1 if there are no more entities.
  212. // Input : &bRemove -
  213. // &bIsNew -
  214. // Output : int
  215. //-----------------------------------------------------------------------------
  216. static inline void CL_ParseDeltaHeader( CEntityReadInfo &u )
  217. {
  218. u.m_UpdateFlags = FHDR_ZERO;
  219. #ifdef DEBUG_NETWORKING
  220. int startbit = u.m_pBuf->GetNumBitsRead();
  221. #endif
  222. SyncTag_Read( u.m_pBuf, "Hdr" );
  223. u.m_nNewEntity = u.m_nHeaderBase + 1 + u.m_pBuf->ReadUBitVar();
  224. u.m_nHeaderBase = u.m_nNewEntity;
  225. // leave pvs flag
  226. if ( u.m_pBuf->ReadOneBit() == 0 )
  227. {
  228. // enter pvs flag
  229. if ( u.m_pBuf->ReadOneBit() != 0 )
  230. {
  231. u.m_UpdateFlags |= FHDR_ENTERPVS;
  232. }
  233. }
  234. else
  235. {
  236. u.m_UpdateFlags |= FHDR_LEAVEPVS;
  237. // Force delete flag
  238. if ( u.m_pBuf->ReadOneBit() != 0 )
  239. {
  240. u.m_UpdateFlags |= FHDR_DELETE;
  241. }
  242. }
  243. // Output the bitstream...
  244. #ifdef DEBUG_NETWORKING
  245. int lastbit = u.m_pBuf->GetNumBitsRead();
  246. {
  247. void SpewBitStream( unsigned char* pMem, int bit, int lastbit );
  248. SpewBitStream( (byte *)u.m_pBuf->m_pData, startbit, lastbit );
  249. }
  250. #endif
  251. }
  252. CBaseClientState::CBaseClientState()
  253. {
  254. m_Socket = NS_CLIENT;
  255. m_pServerClasses = NULL;
  256. m_StringTableContainer = NULL;
  257. m_NetChannel = NULL;
  258. m_nSignonState = SIGNONSTATE_NONE;
  259. m_nChallengeNr = 0;
  260. m_flConnectTime = 0;
  261. m_nRetryNumber = 0;
  262. m_szRetryAddress[0] = 0;
  263. m_ulGameServerSteamID = 0;
  264. m_retryChallenge = 0;
  265. m_bRestrictServerCommands = true;
  266. m_bRestrictClientCommands = true;
  267. m_nServerCount = 0;
  268. m_nCurrentSequence = 0;
  269. m_nDeltaTick = 0;
  270. m_bPaused = 0;
  271. m_flPausedExpireTime = -1.f;
  272. m_nViewEntity = 0;
  273. m_nPlayerSlot = 0;
  274. m_szLevelFileName[0] = 0;
  275. m_szLevelBaseName[0] = 0;
  276. m_nMaxClients = 0;
  277. Q_memset( m_pEntityBaselines, 0, sizeof( m_pEntityBaselines ) );
  278. m_nServerClasses = 0;
  279. m_nServerClassBits = 0;
  280. m_szEncrytionKey[0] = 0;
  281. }
  282. CBaseClientState::~CBaseClientState()
  283. {
  284. }
  285. void CBaseClientState::Clear( void )
  286. {
  287. m_nServerCount = -1;
  288. m_nDeltaTick = -1;
  289. m_ClockDriftMgr.Clear();
  290. m_nCurrentSequence = 0;
  291. m_nServerClasses = 0;
  292. m_nServerClassBits = 0;
  293. m_nPlayerSlot = 0;
  294. m_szLevelFileName[0] = 0;
  295. m_szLevelBaseName[ 0 ] = 0;
  296. m_nMaxClients = 0;
  297. if ( m_pServerClasses )
  298. {
  299. delete[] m_pServerClasses;
  300. m_pServerClasses = NULL;
  301. }
  302. if ( m_StringTableContainer )
  303. {
  304. #ifndef SHARED_NET_STRING_TABLES
  305. m_StringTableContainer->RemoveAllTables();
  306. #endif
  307. m_StringTableContainer = NULL;
  308. }
  309. FreeEntityBaselines();
  310. RecvTable_Term( false );
  311. if ( m_NetChannel )
  312. m_NetChannel->Reset();
  313. m_bPaused = 0;
  314. m_flPausedExpireTime = -1.f;
  315. m_nViewEntity = 0;
  316. m_nChallengeNr = 0;
  317. m_flConnectTime = 0.0f;
  318. }
  319. void CBaseClientState::FileReceived( const char * fileName, unsigned int transferID )
  320. {
  321. ConMsg( "CBaseClientState::FileReceived: %s.\n", fileName );
  322. }
  323. void CBaseClientState::FileDenied(const char *fileName, unsigned int transferID )
  324. {
  325. ConMsg( "CBaseClientState::FileDenied: %s.\n", fileName );
  326. }
  327. void CBaseClientState::FileRequested(const char *fileName, unsigned int transferID )
  328. {
  329. ConMsg( "File '%s' requested from %s.\n", fileName, m_NetChannel->GetAddress() );
  330. m_NetChannel->SendFile( fileName, transferID ); // CBaseCLisntState always sends file
  331. }
  332. void CBaseClientState::FileSent(const char *fileName, unsigned int transferID )
  333. {
  334. ConMsg( "File '%s' sent.\n", fileName );
  335. }
  336. #define REGISTER_NET_MSG( name ) \
  337. NET_##name * p##name = new NET_##name(); \
  338. p##name->m_pMessageHandler = this; \
  339. chan->RegisterMessage( p##name ); \
  340. #define REGISTER_SVC_MSG( name ) \
  341. SVC_##name * p##name = new SVC_##name(); \
  342. p##name->m_pMessageHandler = this; \
  343. chan->RegisterMessage( p##name ); \
  344. void CBaseClientState::ConnectionStart(INetChannel *chan)
  345. {
  346. REGISTER_NET_MSG( Tick );
  347. REGISTER_NET_MSG( StringCmd );
  348. REGISTER_NET_MSG( SetConVar );
  349. REGISTER_NET_MSG( SignonState );
  350. REGISTER_SVC_MSG( Print );
  351. REGISTER_SVC_MSG( ServerInfo );
  352. REGISTER_SVC_MSG( SendTable );
  353. REGISTER_SVC_MSG( ClassInfo );
  354. REGISTER_SVC_MSG( SetPause );
  355. REGISTER_SVC_MSG( CreateStringTable );
  356. REGISTER_SVC_MSG( UpdateStringTable );
  357. REGISTER_SVC_MSG( VoiceInit );
  358. REGISTER_SVC_MSG( VoiceData );
  359. REGISTER_SVC_MSG( Sounds );
  360. REGISTER_SVC_MSG( SetView );
  361. REGISTER_SVC_MSG( FixAngle );
  362. REGISTER_SVC_MSG( CrosshairAngle );
  363. REGISTER_SVC_MSG( BSPDecal );
  364. REGISTER_SVC_MSG( GameEvent );
  365. REGISTER_SVC_MSG( UserMessage );
  366. REGISTER_SVC_MSG( EntityMessage );
  367. REGISTER_SVC_MSG( PacketEntities );
  368. REGISTER_SVC_MSG( TempEntities );
  369. REGISTER_SVC_MSG( Prefetch );
  370. REGISTER_SVC_MSG( Menu );
  371. REGISTER_SVC_MSG( GameEventList );
  372. REGISTER_SVC_MSG( GetCvarValue );
  373. REGISTER_SVC_MSG( CmdKeyValues );
  374. REGISTER_SVC_MSG( SetPauseTimed );
  375. }
  376. void CBaseClientState::ConnectionClosing( const char *reason )
  377. {
  378. ConMsg( "Disconnect: %s.\n", reason?reason:"unknown reason" );
  379. Disconnect( reason ? reason : "Connection closing", true );
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: A svc_signonnum has been received, perform a client side setup
  383. // Output : void CL_SignonReply
  384. //-----------------------------------------------------------------------------
  385. bool CBaseClientState::SetSignonState ( int state, int count )
  386. {
  387. // ConDMsg ("CL_SignonReply: %i\n", cl.signon);
  388. if ( state < SIGNONSTATE_NONE || state > SIGNONSTATE_CHANGELEVEL )
  389. {
  390. ConMsg ("Received signon %i when at %i\n", state, m_nSignonState );
  391. Assert( 0 );
  392. return false;
  393. }
  394. if ( (state > SIGNONSTATE_CONNECTED) && (state <= m_nSignonState) && !m_NetChannel->IsPlayback() )
  395. {
  396. ConMsg ("Received signon %i when at %i\n", state, m_nSignonState);
  397. Assert( 0 );
  398. return false;
  399. }
  400. if ( (count != m_nServerCount) && (count != -1) && (m_nServerCount != -1) && !m_NetChannel->IsPlayback() )
  401. {
  402. ConMsg ("Received wrong spawn count %i when at %i\n", count, m_nServerCount );
  403. Assert( 0 );
  404. return false;
  405. }
  406. if ( state == SIGNONSTATE_FULL )
  407. {
  408. #if defined( REPLAY_ENABLED ) && !defined( DEDICATED )
  409. if ( g_pClientReplayContext )
  410. {
  411. g_pClientReplayContext->OnSignonStateFull();
  412. }
  413. #endif
  414. if ( IsX360() &&
  415. g_pMatchmaking->PreventFullServerStartup() )
  416. {
  417. return true;
  418. }
  419. }
  420. m_nSignonState = state;
  421. return true;
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: called by CL_Connect and CL_CheckResend
  425. // If we are in ca_connecting state and we have gotten a challenge
  426. // response before the timeout, send another "connect" request.
  427. // Output : void CL_SendConnectPacket
  428. //-----------------------------------------------------------------------------
  429. void CBaseClientState::SendConnectPacket (int challengeNr, int authProtocol, uint64 unGSSteamID, bool bGSSecure )
  430. {
  431. COM_TimestampedLog( "SendConnectPacket" );
  432. netadr_t adr;
  433. char szServerName[MAX_OSPATH];
  434. const char *CDKey = "NOCDKEY";
  435. Q_strncpy(szServerName, m_szRetryAddress, MAX_OSPATH);
  436. if ( !NET_StringToAdr (szServerName, &adr) )
  437. {
  438. ConMsg ("Bad server address (%s)\n", szServerName );
  439. Disconnect( "Bad server address", true );
  440. // Host_Disconnect(); MOTODO
  441. return;
  442. }
  443. if ( adr.GetPort() == (unsigned short)0 )
  444. {
  445. adr.SetPort( PORT_SERVER );
  446. }
  447. ALIGN4 char msg_buffer[MAX_ROUTABLE_PAYLOAD] ALIGN4_POST;
  448. bf_write msg( msg_buffer, sizeof(msg_buffer) );
  449. msg.WriteLong( CONNECTIONLESS_HEADER );
  450. msg.WriteByte( C2S_CONNECT );
  451. msg.WriteLong( PROTOCOL_VERSION );
  452. msg.WriteLong( authProtocol );
  453. msg.WriteLong( challengeNr );
  454. msg.WriteLong( m_retryChallenge );
  455. msg.WriteString( GetClientName() ); // Name
  456. msg.WriteString( password.GetString() ); // password
  457. msg.WriteString( GetSteamInfIDVersionInfo().szVersionString ); // product version
  458. // msg.WriteByte( ( g_pServerPluginHandler->GetNumLoadedPlugins() > 0 ) ? 1 : 0 ); // have any client-side server plug-ins been loaded?
  459. switch ( authProtocol )
  460. {
  461. // Fall through, bogus protocol type, use CD key hash.
  462. case PROTOCOL_HASHEDCDKEY: CDKey = GetCDKeyHash();
  463. msg.WriteString( CDKey ); // cdkey
  464. break;
  465. case PROTOCOL_STEAM: if ( !PrepareSteamConnectResponse( unGSSteamID, bGSSecure, adr, msg ) )
  466. {
  467. return;
  468. }
  469. break;
  470. default: Host_Error( "Unexepected authentication protocol %i!\n", authProtocol );
  471. return;
  472. }
  473. // Mark time of this attempt for retransmit requests
  474. m_flConnectTime = net_time;
  475. // remember challengenr for TCP connection
  476. m_nChallengeNr = challengeNr;
  477. // Remember Steam ID, if any
  478. m_ulGameServerSteamID = unGSSteamID;
  479. // Send protocol and challenge value
  480. NET_SendPacket( NULL, m_Socket, adr, msg.GetData(), msg.GetNumBytesWritten() );
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: append steam specific data to a connection response
  484. //-----------------------------------------------------------------------------
  485. bool CBaseClientState::PrepareSteamConnectResponse( uint64 unGSSteamID, bool bGSSecure, const netadr_t &adr, bf_write &msg )
  486. {
  487. // X360TBD: Network - Steam Dedicated Server hack
  488. if ( IsX360() )
  489. {
  490. return true;
  491. }
  492. #if !defined( NO_STEAM ) && !defined( SWDS )
  493. if ( !Steam3Client().SteamUser() )
  494. {
  495. COM_ExplainDisconnection( true, "#GameUI_ServerRequireSteam" );
  496. Disconnect( "#GameUI_ServerRequireSteam", true );
  497. return false;
  498. }
  499. #endif
  500. netadr_t checkAdr = adr;
  501. if ( adr.GetType() == NA_LOOPBACK || adr.IsLocalhost() )
  502. {
  503. checkAdr.SetIP( net_local_adr.GetIPHostByteOrder() );
  504. }
  505. #ifndef SWDS
  506. // now append the steam3 cookie
  507. char steam3Cookie[ STEAM_KEYSIZE ];
  508. uint32 steam3CookieLen = 0;
  509. Steam3Client().GetAuthSessionTicket( steam3Cookie, sizeof(steam3Cookie), &steam3CookieLen, checkAdr.GetIPHostByteOrder(), checkAdr.GetPort(), unGSSteamID, bGSSecure );
  510. if ( steam3CookieLen == 0 )
  511. {
  512. COM_ExplainDisconnection( true, "#GameUI_ServerRequireSteam" );
  513. Disconnect( "#GameUI_ServerRequireSteam", true );
  514. return false;
  515. }
  516. msg.WriteShort( steam3CookieLen );
  517. if ( steam3CookieLen > 0 )
  518. msg.WriteBytes( steam3Cookie, steam3CookieLen );
  519. #endif
  520. return true;
  521. }
  522. // Tracks how we connected to the current server.
  523. static ConVar cl_connectmethod( "cl_connectmethod", "", FCVAR_USERINFO | FCVAR_HIDDEN, "Method by which we connected to the current server." );
  524. /* static */ bool CBaseClientState::ConnectMethodAllowsRedirects()
  525. {
  526. // Only HLTV should be allowed to redirect clients, but malicious servers can answer a connect
  527. // attempt as HLTV and then redirect elsewhere. A somewhat-more complete fix for this would
  528. // involve tracking our redirected status and refusing to interact with non-HLTV servers. For
  529. // now, however, we just blacklist server browser / matchmaking connect methods from allowing
  530. // redirects and allow it for other types.
  531. const char *pConnectMethod = cl_connectmethod.GetString();
  532. if ( V_strcmp( pConnectMethod, "serverbrowser_internet" ) == 0 ||
  533. V_strncmp( pConnectMethod, "quickpick", 9 ) == 0 ||
  534. V_strncmp( pConnectMethod, "quickplay", 9 ) == 0 ||
  535. V_strcmp( pConnectMethod, "matchmaking" ) == 0 ||
  536. V_strcmp( pConnectMethod, "coaching" ) == 0 )
  537. {
  538. return false;
  539. }
  540. return true;
  541. }
  542. void CBaseClientState::Connect(const char* adr, const char *pszSourceTag)
  543. {
  544. #if !defined( NO_STEAM )
  545. // Get our name from steam. Needs to be done before connecting
  546. // because we won't have triggered a check by changing our name.
  547. IConVar *pVar = g_pCVar->FindVar( "name" );
  548. if ( pVar )
  549. {
  550. SetNameToSteamIDName( pVar );
  551. }
  552. #endif
  553. Q_strncpy( m_szRetryAddress, adr, sizeof(m_szRetryAddress) );
  554. m_retryChallenge = (RandomInt(0,0x0FFF) << 16) | RandomInt(0,0xFFFF);
  555. m_ulGameServerSteamID = 0;
  556. m_sRetrySourceTag = pszSourceTag;
  557. cl_connectmethod.SetValue( m_sRetrySourceTag.String() );
  558. // For the check for resend timer to fire a connection / getchallenge request.
  559. SetSignonState( SIGNONSTATE_CHALLENGE, -1 );
  560. // Force connection request to fire.
  561. m_flConnectTime = -FLT_MAX;
  562. m_nRetryNumber = 0;
  563. }
  564. INetworkStringTable *CBaseClientState::GetStringTable( const char * name ) const
  565. {
  566. if ( !m_StringTableContainer )
  567. {
  568. Assert( m_StringTableContainer );
  569. return NULL;
  570. }
  571. return m_StringTableContainer->FindTable( name );
  572. }
  573. void CBaseClientState::ForceFullUpdate( void )
  574. {
  575. if ( m_nDeltaTick == -1 )
  576. return;
  577. FreeEntityBaselines();
  578. m_nDeltaTick = -1;
  579. DevMsg( "Requesting full game update...\n");
  580. }
  581. void CBaseClientState::FullConnect( netadr_t &adr )
  582. {
  583. // Initiate the network channel
  584. COM_TimestampedLog( "CBaseClientState::FullConnect" );
  585. m_NetChannel = NET_CreateNetChannel( m_Socket, &adr, "CLIENT", this );
  586. Assert( m_NetChannel );
  587. m_NetChannel->StartStreaming( m_nChallengeNr ); // open TCP stream
  588. // Bump connection time to now so we don't resend a connection
  589. // Request
  590. m_flConnectTime = net_time;
  591. // We'll request a full delta from the baseline
  592. m_nDeltaTick = -1;
  593. // We can send a cmd right away
  594. m_flNextCmdTime = net_time;
  595. // Mark client as connected
  596. SetSignonState( SIGNONSTATE_CONNECTED, -1 );
  597. #if !defined(SWDS)
  598. RCONClient().SetAddress( m_NetChannel->GetRemoteAddress() );
  599. #endif
  600. // Fire an event when we get our connection
  601. IGameEvent *event = g_GameEventManager.CreateEvent( "client_connected" );
  602. if ( event )
  603. {
  604. event->SetString( "address", m_NetChannel->GetRemoteAddress().ToString( true ) );
  605. event->SetInt( "ip", m_NetChannel->GetRemoteAddress().GetIPNetworkByteOrder() ); // <<< Network byte order?
  606. event->SetInt( "port", m_NetChannel->GetRemoteAddress().GetPort() );
  607. g_GameEventManager.FireEventClientSide( event );
  608. }
  609. }
  610. void CBaseClientState::ConnectionCrashed(const char *reason)
  611. {
  612. DebuggerBreakIfDebugging_StagingOnly();
  613. ConMsg( "Connection lost: %s.\n", reason?reason:"unknown reason" );
  614. Disconnect( reason ? reason : "Connection crashed", true );
  615. }
  616. void CBaseClientState::Disconnect( const char *pszReason, bool bShowMainMenu )
  617. {
  618. m_flConnectTime = -FLT_MAX;
  619. m_nRetryNumber = 0;
  620. m_ulGameServerSteamID = 0;
  621. if ( m_nSignonState == SIGNONSTATE_NONE )
  622. return;
  623. #if !defined( SWDS ) && defined( ENABLE_RPT )
  624. CL_NotifyRPTOfDisconnect( );
  625. #endif
  626. m_nSignonState = SIGNONSTATE_NONE;
  627. netadr_t adr;
  628. if ( m_NetChannel )
  629. {
  630. adr = m_NetChannel->GetRemoteAddress();
  631. }
  632. else
  633. {
  634. NET_StringToAdr (m_szRetryAddress, &adr);
  635. }
  636. #ifndef SWDS
  637. netadr_t checkAdr = adr;
  638. if ( adr.GetType() == NA_LOOPBACK || adr.IsLocalhost() )
  639. {
  640. checkAdr.SetIP( net_local_adr.GetIPHostByteOrder() );
  641. }
  642. Steam3Client().CancelAuthTicket();
  643. #endif
  644. if ( m_NetChannel )
  645. {
  646. m_NetChannel->Shutdown( ( pszReason && *pszReason ) ? pszReason : "Disconnect by user." );
  647. m_NetChannel = NULL;
  648. }
  649. }
  650. void CBaseClientState::RunFrame (void)
  651. {
  652. VPROF("CBaseClientState::RunFrame");
  653. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  654. if ( (m_nSignonState > SIGNONSTATE_NEW) && m_NetChannel && g_GameEventManager.HasClientListenersChanged() )
  655. {
  656. // assemble a list of all events we listening to and tell the server
  657. CLC_ListenEvents msg;
  658. g_GameEventManager.WriteListenEventList( &msg );
  659. m_NetChannel->SendNetMsg( msg );
  660. }
  661. if ( m_nSignonState == SIGNONSTATE_CHALLENGE )
  662. {
  663. CheckForResend();
  664. }
  665. }
  666. /*
  667. =================
  668. CL_CheckForResend
  669. Resend a connect message if the last one has timed out
  670. =================
  671. */
  672. void CBaseClientState::CheckForResend (void)
  673. {
  674. // resend if we haven't gotten a reply yet
  675. // We only resend during the connection process.
  676. if ( m_nSignonState != SIGNONSTATE_CHALLENGE )
  677. return;
  678. // Wait at least the resend # of seconds.
  679. if ( ( net_time - m_flConnectTime ) < cl_resend.GetFloat())
  680. return;
  681. netadr_t adr;
  682. if (!NET_StringToAdr (m_szRetryAddress, &adr))
  683. {
  684. ConMsg ("Bad server address (%s)\n", m_szRetryAddress);
  685. //Host_Disconnect();
  686. Disconnect( "Bad server address", true );
  687. return;
  688. }
  689. if (adr.GetPort() == 0)
  690. {
  691. adr.SetPort( PORT_SERVER );
  692. }
  693. // Only retry so many times before failure.
  694. if ( m_nRetryNumber >= GetConnectionRetryNumber() )
  695. {
  696. COM_ExplainDisconnection( true, "Connection failed after %i retries.\n", CL_CONNECTION_RETRIES );
  697. // Host_Disconnect();
  698. Disconnect( "Connection failed", true );
  699. return;
  700. }
  701. // Mark time of this attempt.
  702. m_flConnectTime = net_time; // for retransmit requests
  703. // Display appropriate message
  704. if ( Q_strncmp(m_szRetryAddress, "localhost", 9) )
  705. {
  706. if ( m_nRetryNumber == 0 )
  707. ConMsg ("Connecting to %s...\n", m_szRetryAddress);
  708. else
  709. ConMsg ("Retrying %s...\n", m_szRetryAddress);
  710. }
  711. // Fire an event when we attempt connection
  712. if ( m_nRetryNumber == 0 )
  713. {
  714. IGameEvent *event = g_GameEventManager.CreateEvent( "client_beginconnect" );
  715. if ( event )
  716. {
  717. event->SetString( "address", m_szRetryAddress);
  718. event->SetInt( "ip", adr.GetIPNetworkByteOrder() ); // <<< Network byte order?
  719. event->SetInt( "port", adr.GetPort() );
  720. //event->SetInt( "retry_number", m_nRetryNumber );
  721. event->SetString( "source", m_sRetrySourceTag );
  722. g_GameEventManager.FireEventClientSide( event );
  723. }
  724. }
  725. m_nRetryNumber++;
  726. // Request another challenge value.
  727. {
  728. ALIGN4 char msg_buffer[MAX_ROUTABLE_PAYLOAD] ALIGN4_POST;
  729. bf_write msg( msg_buffer, sizeof(msg_buffer) );
  730. msg.WriteLong( CONNECTIONLESS_HEADER );
  731. msg.WriteByte( A2S_GETCHALLENGE );
  732. msg.WriteLong( m_retryChallenge );
  733. msg.WriteString( "0000000000" ); // pad out
  734. NET_SendPacket( NULL, m_Socket, adr, msg.GetData(), msg.GetNumBytesWritten() );
  735. }
  736. }
  737. bool CBaseClientState::ProcessConnectionlessPacket( netpacket_t *packet )
  738. {
  739. VPROF( "ProcessConnectionlessPacket" );
  740. Assert( packet );
  741. bf_read &msg = packet->message; // handy shortcut
  742. int c = msg.ReadByte();
  743. // ignoring a specific packet that DOTA2 broadcasts
  744. if ( c == C2C_MOD )
  745. return false;
  746. char string[MAX_ROUTABLE_PAYLOAD];
  747. netadr_t adrServerConnectingTo;
  748. NET_StringToAdr ( m_szRetryAddress, &adrServerConnectingTo );
  749. if ( ( packet->from.GetType() != NA_LOOPBACK ) && ( packet->from.GetIPNetworkByteOrder() != adrServerConnectingTo.GetIPNetworkByteOrder() ) )
  750. {
  751. if ( cl_show_connectionless_packet_warnings.GetBool() )
  752. {
  753. ConDMsg ( "Discarding connectionless packet ( CL '%c' ) from %s.\n", c, packet->from.ToString() );
  754. }
  755. return false;
  756. }
  757. switch ( c )
  758. {
  759. case S2C_CONNECTION: if ( m_nSignonState == SIGNONSTATE_CHALLENGE )
  760. {
  761. int myChallenge = msg.ReadLong();
  762. if ( myChallenge != m_retryChallenge )
  763. {
  764. Msg( "Server connection did not have the correct challenge, ignoring.\n" );
  765. return false;
  766. }
  767. // server accepted our connection request
  768. FullConnect( packet->from );
  769. }
  770. break;
  771. case S2C_CHALLENGE: // Response from getchallenge we sent to the server we are connecting to
  772. // Blow it off if we are not connected.
  773. if ( m_nSignonState == SIGNONSTATE_CHALLENGE )
  774. {
  775. int magicVersion = msg.ReadLong();
  776. if ( magicVersion != S2C_MAGICVERSION )
  777. {
  778. COM_ExplainDisconnection( true, "#GameUI_ServerConnectOutOfDate" );
  779. Disconnect( "#GameUI_ServerConnectOutOfDate", true );
  780. return false;
  781. }
  782. int challenge = msg.ReadLong();
  783. int myChallenge = msg.ReadLong();
  784. if ( myChallenge != m_retryChallenge )
  785. {
  786. Msg( "Server challenge did not have the correct challenge, ignoring.\n" );
  787. return false;
  788. }
  789. int authprotocol = msg.ReadLong();
  790. uint64 unGSSteamID = 0;
  791. bool bGSSecure = false;
  792. if ( authprotocol == PROTOCOL_STEAM )
  793. {
  794. if ( msg.ReadShort() != 0 )
  795. {
  796. Msg( "Invalid Steam key size.\n" );
  797. Disconnect( "Invalid Steam key size", true );
  798. return false;
  799. }
  800. if ( msg.GetNumBytesLeft() > sizeof(unGSSteamID) )
  801. {
  802. if ( !msg.ReadBytes( &unGSSteamID, sizeof(unGSSteamID) ) )
  803. {
  804. Msg( "Invalid GS Steam ID.\n" );
  805. Disconnect( "Invalid GS Steam ID", true );
  806. return false;
  807. }
  808. bGSSecure = ( msg.ReadByte() == 1 );
  809. }
  810. // The host can disable access to secure servers if you load unsigned code (mods, plugins, hacks)
  811. if ( bGSSecure && !Host_IsSecureServerAllowed() )
  812. {
  813. COM_ExplainDisconnection( true, "#GameUI_ServerInsecure" );
  814. Disconnect( "#GameUI_ServerInsecure", true );
  815. return false;
  816. }
  817. }
  818. SendConnectPacket( challenge, authprotocol, unGSSteamID, bGSSecure );
  819. }
  820. break;
  821. case S2C_CONNREJECT: if ( m_nSignonState == SIGNONSTATE_CHALLENGE ) // Spoofed?
  822. {
  823. int myChallenge = msg.ReadLong();
  824. if ( myChallenge != m_retryChallenge )
  825. {
  826. Msg( "Received connection rejection that didn't match my challenge, ignoring.\n" );
  827. return false;
  828. }
  829. msg.ReadString( string, sizeof(string) );
  830. // Force failure dialog to come up now.
  831. COM_ExplainDisconnection( true, "%s", string );
  832. Disconnect( string, true );
  833. // Host_Disconnect();
  834. }
  835. break;
  836. // Unknown?
  837. default:
  838. // Otherwise, don't do anything.
  839. if ( cl_show_connectionless_packet_warnings.GetBool() )
  840. {
  841. ConDMsg ( "Bad connectionless packet ( CL '%c' ) from %s.\n", c, packet->from.ToString() );
  842. }
  843. return false;
  844. }
  845. return true;
  846. }
  847. bool CBaseClientState::ProcessTick( NET_Tick *msg )
  848. {
  849. VPROF( "ProcessTick" );
  850. m_NetChannel->SetRemoteFramerate( msg->m_flHostFrameTime, msg->m_flHostFrameTimeStdDeviation );
  851. // Note: CClientState separates the client and server clock states and drifts
  852. // the client's clock to match the server's, but right here, we keep the two clocks in sync.
  853. SetClientTickCount( msg->m_nTick );
  854. SetServerTickCount( msg->m_nTick );
  855. if ( m_StringTableContainer )
  856. {
  857. m_StringTableContainer->SetTick( GetServerTickCount() );
  858. }
  859. return (GetServerTickCount()>0);
  860. }
  861. void CBaseClientState::SendStringCmd(const char * command)
  862. {
  863. if ( m_NetChannel)
  864. {
  865. NET_StringCmd stringCmd( command );
  866. m_NetChannel->SendNetMsg( stringCmd );
  867. }
  868. }
  869. bool CBaseClientState::ProcessStringCmd( NET_StringCmd *msg )
  870. {
  871. VPROF( "ProcessStringCmd" );
  872. // Don't restrict commands from the server in single player or if cl_restrict_stuffed_commands is 0.
  873. if ( !m_bRestrictServerCommands || sv.IsActive() )
  874. {
  875. Cbuf_AddText ( msg->m_szCommand );
  876. return true;
  877. }
  878. // Check that we can add the two execution markers
  879. if ( !Cbuf_HasRoomForExecutionMarkers( 2 ) )
  880. {
  881. AssertMsg( false, "CBaseClientState::ProcessStringCmd called but there is no room for the execution markers. Ignoring command." );
  882. return true;
  883. }
  884. // Run the command, but make sure the command parser knows to only execute commands marked with FCVAR_SERVER_CAN_EXECUTE.
  885. Cbuf_AddTextWithMarkers( eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE, msg->m_szCommand, eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE );
  886. return true;
  887. }
  888. bool CBaseClientState::ProcessSetConVar( NET_SetConVar *msg )
  889. {
  890. VPROF( "ProcessSetConVar" );
  891. // Never process on local client, since the ConVar is directly linked here
  892. if ( m_NetChannel->IsLoopback() )
  893. return true;
  894. for ( int i=0; i<msg->m_ConVars.Count(); i++ )
  895. {
  896. const char *name = msg->m_ConVars[i].name;
  897. const char *value = msg->m_ConVars[i].value;
  898. // De-constify
  899. ConVarRef var( name );
  900. if ( !var.IsValid() )
  901. {
  902. ConMsg( "SetConVar: No such cvar ( %s set to %s), skipping\n",
  903. name, value );
  904. continue;
  905. }
  906. // Make sure server is only setting replicated game ConVars
  907. if ( !var.IsFlagSet( FCVAR_REPLICATED ) )
  908. {
  909. ConMsg( "SetConVar: Can't set server cvar %s to %s, not marked as FCVAR_REPLICATED on client\n",
  910. name, value );
  911. continue;
  912. }
  913. // Set value directly ( don't call through cv->DirectSet!!! )
  914. if ( !sv.IsActive() )
  915. {
  916. var.SetValue( value );
  917. DevMsg( "SetConVar: %s = \"%s\"\n", name, value );
  918. }
  919. }
  920. return true;
  921. }
  922. bool CBaseClientState::ProcessSignonState( NET_SignonState *msg )
  923. {
  924. VPROF( "ProcessSignonState" );
  925. return SetSignonState( msg->m_nSignonState, msg->m_nSpawnCount ) ;
  926. }
  927. bool CBaseClientState::ProcessPrint( SVC_Print *msg )
  928. {
  929. VPROF( "ProcessPrint" );
  930. ConMsg( "%s", msg->m_szText );
  931. return true;
  932. }
  933. bool CBaseClientState::ProcessMenu( SVC_Menu *msg )
  934. {
  935. VPROF( "ProcessMenu" );
  936. #if !defined(SWDS)
  937. PluginHelpers_Menu( msg );
  938. #endif
  939. return true;
  940. }
  941. bool CBaseClientState::ProcessServerInfo( SVC_ServerInfo *msg )
  942. {
  943. VPROF( "ProcessServerInfo" );
  944. #ifndef SWDS
  945. EngineVGui()->UpdateProgressBar(PROGRESS_PROCESSSERVERINFO);
  946. #endif
  947. COM_TimestampedLog( " CBaseClient::ProcessServerInfo" );
  948. if ( msg->m_nProtocol != PROTOCOL_VERSION
  949. #if defined( DEMO_BACKWARDCOMPATABILITY ) && (! defined( SWDS ) )
  950. && !( demoplayer->IsPlayingBack() && msg->m_nProtocol >= PROTOCOL_VERSION_12 )
  951. #endif
  952. )
  953. {
  954. ConMsg ( "Server returned version %i, expected %i.\n", msg->m_nProtocol, PROTOCOL_VERSION );
  955. return false;
  956. }
  957. // Parse servercount (i.e., # of servers spawned since server .exe started)
  958. // So that we can detect new server startup during download, etc.
  959. m_nServerCount = msg->m_nServerCount;
  960. m_nMaxClients = msg->m_nMaxClients;
  961. m_nServerClasses = msg->m_nMaxClasses;
  962. m_nServerClassBits = Q_log2( m_nServerClasses ) + 1;
  963. if ( m_nMaxClients < 1 || m_nMaxClients > ABSOLUTE_PLAYER_LIMIT )
  964. {
  965. ConMsg ("Bad maxclients (%u) from server.\n", m_nMaxClients);
  966. return false;
  967. }
  968. if ( m_nServerClasses < 1 || m_nServerClasses > MAX_SERVER_CLASSES )
  969. {
  970. ConMsg ("Bad maxclasses (%u) from server.\n", m_nServerClasses);
  971. return false;
  972. }
  973. #ifndef SWDS
  974. if ( !sv.IsActive() &&
  975. !( m_NetChannel->IsLoopback() || m_NetChannel->IsNull() ) )
  976. {
  977. // if you are joing a remote server it MUST be multipler and have maxplayer set to more than 1
  978. // this prevents a spoofed ServerInfo packet from making the client think its in singleplayer
  979. // and turning off a bunch of security checks
  980. if ( m_nMaxClients <= 1 )
  981. {
  982. ConMsg ("Bad maxclients (%u) from server.\n", m_nMaxClients);
  983. return false;
  984. }
  985. // reset server enforced cvars
  986. g_pCVar->RevertFlaggedConVars( FCVAR_REPLICATED );
  987. // Cheats were disabled; revert all cheat cvars to their default values.
  988. // This must be done heading into multiplayer games because people can play
  989. // demos etc and set cheat cvars with sv_cheats 0.
  990. g_pCVar->RevertFlaggedConVars( FCVAR_CHEAT );
  991. DevMsg( "FCVAR_CHEAT cvars reverted to defaults.\n" );
  992. }
  993. #endif
  994. // clear all baselines still around from last game
  995. FreeEntityBaselines();
  996. // force changed flag to being reset
  997. g_GameEventManager.HasClientListenersChanged( true );
  998. m_nPlayerSlot = msg->m_nPlayerSlot;
  999. m_nViewEntity = m_nPlayerSlot + 1;
  1000. if ( msg->m_fTickInterval < MINIMUM_TICK_INTERVAL ||
  1001. msg->m_fTickInterval > MAXIMUM_TICK_INTERVAL )
  1002. {
  1003. ConMsg ("Interval_per_tick %f out of range [%f to %f]\n",
  1004. msg->m_fTickInterval, MINIMUM_TICK_INTERVAL, MAXIMUM_TICK_INTERVAL );
  1005. return false;
  1006. }
  1007. if ( !COM_CheckGameDirectory( msg->m_szGameDir ) )
  1008. {
  1009. return false;
  1010. }
  1011. Q_strncpy( m_szLevelBaseName, msg->m_szMapName, sizeof( m_szLevelBaseName ) );
  1012. #if !defined(SWDS)
  1013. audiosourcecache->LevelInit( m_szLevelBaseName );
  1014. #endif
  1015. ConVarRef skyname( "sv_skyname" );
  1016. if ( skyname.IsValid() )
  1017. {
  1018. skyname.SetValue( msg->m_szSkyName );
  1019. }
  1020. m_nDeltaTick = -1; // no valid snapshot for this game yet
  1021. // fire a client side event about server data
  1022. IGameEvent *event = g_GameEventManager.CreateEvent( "server_spawn" );
  1023. if ( event )
  1024. {
  1025. event->SetString( "hostname", msg->m_szHostName );
  1026. event->SetString( "address", m_NetChannel->GetRemoteAddress().ToString( true ) );
  1027. event->SetInt( "ip", m_NetChannel->GetRemoteAddress().GetIPNetworkByteOrder() ); // <<< Network byte order?
  1028. event->SetInt( "port", m_NetChannel->GetRemoteAddress().GetPort() );
  1029. event->SetString( "game", msg->m_szGameDir );
  1030. event->SetString( "mapname", msg->m_szMapName );
  1031. event->SetInt( "maxplayers", msg->m_nMaxClients );
  1032. event->SetInt( "password", 0 ); // TODO
  1033. event->SetString( "os", va("%c", toupper( msg->m_cOS ) ) );
  1034. event->SetInt( "dedicated", msg->m_bIsDedicated ? 1 : 0 );
  1035. if ( m_ulGameServerSteamID != 0 )
  1036. {
  1037. event->SetString( "steamid", CSteamID(m_ulGameServerSteamID).Render() );
  1038. }
  1039. g_GameEventManager.FireEventClientSide( event );
  1040. }
  1041. // Set default filename, but this is finalized by ClientState later, so it should not be depended on yet. See PrepareLevelResources call
  1042. Host_DefaultMapFileName( msg->m_szMapName, m_szLevelFileName, sizeof( m_szLevelFileName ) );
  1043. COM_TimestampedLog( " CBaseClient::ProcessServerInfo(done)" );
  1044. return true;
  1045. }
  1046. bool CBaseClientState::ProcessSendTable( SVC_SendTable *msg )
  1047. {
  1048. VPROF( "ProcessSendTable" );
  1049. if ( !RecvTable_RecvClassInfos( &msg->m_DataIn, msg->m_bNeedsDecoder ) )
  1050. {
  1051. Host_EndGame(true, "ProcessSendTable: RecvTable_RecvClassInfos failed.\n" );
  1052. return false;
  1053. }
  1054. return true;
  1055. }
  1056. bool CBaseClientState::ProcessClassInfo( SVC_ClassInfo *msg )
  1057. {
  1058. VPROF( "ProcessClassInfo" );
  1059. COM_TimestampedLog( " CBaseClient::ProcessClassInfo" );
  1060. if ( msg->m_bCreateOnClient )
  1061. {
  1062. ConMsg ( "Can't create class tables.\n");
  1063. Assert( 0 );
  1064. return false;
  1065. }
  1066. if( m_pServerClasses )
  1067. {
  1068. delete [] m_pServerClasses;
  1069. }
  1070. m_nServerClasses = msg->m_Classes.Count();
  1071. m_pServerClasses = new C_ServerClassInfo[m_nServerClasses];
  1072. if ( !m_pServerClasses )
  1073. {
  1074. Host_EndGame(true, "ProcessClassInfo: can't allocate %d C_ServerClassInfos.\n", m_nServerClasses);
  1075. return false;
  1076. }
  1077. // copy class names and class IDs from message to CClientState
  1078. for (int i=0; i<m_nServerClasses; i++)
  1079. {
  1080. SVC_ClassInfo::class_t * svclass = &msg->m_Classes[ i ];
  1081. if( svclass->classID >= m_nServerClasses )
  1082. {
  1083. Host_EndGame(true, "ProcessClassInfo: invalid class index (%d).\n", svclass->classID);
  1084. return false;
  1085. }
  1086. C_ServerClassInfo * svclassinfo = &m_pServerClasses[svclass->classID];
  1087. int len = Q_strlen(svclass->classname) + 1;
  1088. svclassinfo->m_ClassName = new char[ len ];
  1089. Q_strncpy( svclassinfo->m_ClassName, svclass->classname, len );
  1090. len = Q_strlen(svclass->datatablename) + 1;
  1091. svclassinfo->m_DatatableName = new char[ len ];
  1092. Q_strncpy( svclassinfo->m_DatatableName,svclass->datatablename, len );
  1093. }
  1094. COM_TimestampedLog( " CBaseClient::ProcessClassInfo(done)" );
  1095. return LinkClasses(); // link server and client classes
  1096. }
  1097. bool CBaseClientState::ProcessSetPause( SVC_SetPause *msg )
  1098. {
  1099. VPROF( "ProcessSetPause" );
  1100. m_bPaused = msg->m_bPaused;
  1101. return true;
  1102. }
  1103. bool CBaseClientState::ProcessSetPauseTimed( SVC_SetPauseTimed *msg )
  1104. {
  1105. VPROF( "ProcessSetPauseTimed" );
  1106. m_bPaused = msg->m_bPaused;
  1107. m_flPausedExpireTime = msg->m_flExpireTime;
  1108. return true;
  1109. }
  1110. bool CBaseClientState::ProcessCreateStringTable( SVC_CreateStringTable *msg )
  1111. {
  1112. VPROF( "ProcessCreateStringTable" );
  1113. #ifndef SWDS
  1114. EngineVGui()->UpdateProgressBar(PROGRESS_PROCESSSTRINGTABLE);
  1115. #endif
  1116. COM_TimestampedLog( " CBaseClient::ProcessCreateStringTable(%s)", msg->m_szTableName );
  1117. m_StringTableContainer->AllowCreation( true );
  1118. int startbit = msg->m_DataIn.GetNumBitsRead();
  1119. #ifndef SHARED_NET_STRING_TABLES
  1120. CNetworkStringTable *table = (CNetworkStringTable*)
  1121. m_StringTableContainer->CreateStringTableEx( msg->m_szTableName, msg->m_nMaxEntries, msg->m_nUserDataSize, msg->m_nUserDataSizeBits, msg->m_bIsFilenames );
  1122. Assert ( table );
  1123. table->SetTick( GetServerTickCount() ); // set creation tick
  1124. HookClientStringTable( msg->m_szTableName );
  1125. if ( msg->m_bDataCompressed )
  1126. {
  1127. unsigned int msgUncompressedSize = msg->m_DataIn.ReadLong();
  1128. unsigned int msgCompressedSize = msg->m_DataIn.ReadLong();
  1129. unsigned int uncompressedSize = msgUncompressedSize;
  1130. /// XXX(JohnS): 11/08/2016 - The PAD_NUMBER() call below was overflowing on UINT32_MAX-3 values. Enforcing
  1131. // resource-usage limits at this level is a lost cause without a massive overhaul, but clamp these
  1132. // to somewhat reasonable ranges to prevent overflows with less-audited code in the engine.
  1133. bool bSuccess = false;
  1134. if ( msg->m_DataIn.TotalBytesAvailable() > 0 &&
  1135. msgCompressedSize <= (unsigned int)msg->m_DataIn.TotalBytesAvailable() &&
  1136. msgCompressedSize < UINT_MAX/2 &&
  1137. msgUncompressedSize < UINT_MAX/2 )
  1138. {
  1139. // allocate buffer for uncompressed data, align to 4 bytes boundary
  1140. char *uncompressedBuffer = new char[PAD_NUMBER( msgUncompressedSize, 4 )];
  1141. char *compressedBuffer = new char[PAD_NUMBER( msgCompressedSize, 4 )];
  1142. msg->m_DataIn.ReadBits( compressedBuffer, msgCompressedSize * 8 );
  1143. // uncompress data
  1144. bSuccess = COM_BufferToBufferDecompress( uncompressedBuffer, &uncompressedSize, compressedBuffer, msgCompressedSize );
  1145. bSuccess &= ( uncompressedSize == msgUncompressedSize );
  1146. if ( bSuccess )
  1147. {
  1148. bf_read data( uncompressedBuffer, uncompressedSize );
  1149. table->ParseUpdate( data, msg->m_nNumEntries );
  1150. }
  1151. delete[] uncompressedBuffer;
  1152. delete[] compressedBuffer;
  1153. }
  1154. if ( !bSuccess )
  1155. {
  1156. Assert( false );
  1157. Warning("Malformed message in CBaseClientState::ProcessCreateStringTable\n");
  1158. }
  1159. }
  1160. else
  1161. {
  1162. table->ParseUpdate( msg->m_DataIn, msg->m_nNumEntries );
  1163. }
  1164. #endif
  1165. m_StringTableContainer->AllowCreation( false );
  1166. int endbit = msg->m_DataIn.GetNumBitsRead();
  1167. COM_TimestampedLog( " CBaseClient::ProcessCreateStringTable(%s)-done", msg->m_szTableName );
  1168. return ( endbit - startbit ) == msg->m_nLength;
  1169. }
  1170. bool CBaseClientState::ProcessUpdateStringTable( SVC_UpdateStringTable *msg )
  1171. {
  1172. VPROF( "ProcessUpdateStringTable" );
  1173. int startbit = msg->m_DataIn.GetNumBitsRead();
  1174. #ifndef SHARED_NET_STRING_TABLES
  1175. //m_StringTableContainer is NULL on level transitions, Seems to be caused by a UpdateStringTable packet comming in before the ServerInfo packet
  1176. // 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
  1177. if(m_StringTableContainer != NULL)
  1178. {
  1179. CNetworkStringTable *table = (CNetworkStringTable*)
  1180. m_StringTableContainer->GetTable( msg->m_nTableID );
  1181. table->ParseUpdate( msg->m_DataIn, msg->m_nChangedEntries );
  1182. }
  1183. else
  1184. {
  1185. Warning("m_StringTableContainer is NULL in CBaseClientState::ProcessUpdateStringTable\n");
  1186. }
  1187. #endif
  1188. int endbit = msg->m_DataIn.GetNumBitsRead();
  1189. return ( endbit - startbit ) == msg->m_nLength;
  1190. }
  1191. bool CBaseClientState::ProcessSetView( SVC_SetView *msg )
  1192. {
  1193. VPROF( "ProcessSetView" );
  1194. m_nViewEntity = msg->m_nEntityIndex;
  1195. return true;
  1196. }
  1197. bool CBaseClientState::ProcessPacketEntities( SVC_PacketEntities *msg )
  1198. {
  1199. VPROF( "ProcessPacketEntities" );
  1200. // First update is the final signon stage where we actually receive an entity (i.e., the world at least)
  1201. if ( m_nSignonState < SIGNONSTATE_SPAWN )
  1202. {
  1203. ConMsg("Received packet entities while connecting!\n");
  1204. return false;
  1205. }
  1206. if ( m_nSignonState == SIGNONSTATE_SPAWN )
  1207. {
  1208. if ( !msg->m_bIsDelta )
  1209. {
  1210. // We are done with signon sequence.
  1211. SetSignonState( SIGNONSTATE_FULL, m_nServerCount );
  1212. }
  1213. else
  1214. {
  1215. ConMsg("Received delta packet entities while spawing!\n");
  1216. return false;
  1217. }
  1218. }
  1219. // overwrite a -1 delta_tick only if packet was uncompressed
  1220. if ( (m_nDeltaTick >= 0) || !msg->m_bIsDelta )
  1221. {
  1222. // we received this snapshot successfully, now this is our delta reference
  1223. m_nDeltaTick = GetServerTickCount();
  1224. }
  1225. return true;
  1226. }
  1227. void CBaseClientState::ReadPacketEntities( CEntityReadInfo &u )
  1228. {
  1229. VPROF( "ReadPacketEntities" );
  1230. // Loop until there are no more entities to read
  1231. u.NextOldEntity();
  1232. while ( u.m_UpdateType < Finished )
  1233. {
  1234. u.m_nHeaderCount--;
  1235. u.m_bIsEntity = ( u.m_nHeaderCount >= 0 ) ? true : false;
  1236. if ( u.m_bIsEntity )
  1237. {
  1238. CL_ParseDeltaHeader( u );
  1239. }
  1240. u.m_UpdateType = PreserveEnt;
  1241. while( u.m_UpdateType == PreserveEnt )
  1242. {
  1243. // Figure out what kind of an update this is.
  1244. if( CL_DetermineUpdateType( u ) )
  1245. {
  1246. switch( u.m_UpdateType )
  1247. {
  1248. case EnterPVS: ReadEnterPVS( u );
  1249. break;
  1250. case LeavePVS: ReadLeavePVS( u );
  1251. break;
  1252. case DeltaEnt: ReadDeltaEnt( u );
  1253. break;
  1254. case PreserveEnt: ReadPreserveEnt( u );
  1255. break;
  1256. default: DevMsg(1, "ReadPacketEntities: unknown updatetype %i\n", u.m_UpdateType );
  1257. break;
  1258. }
  1259. }
  1260. }
  1261. }
  1262. // Now process explicit deletes
  1263. if ( u.m_bAsDelta && u.m_UpdateType == Finished )
  1264. {
  1265. ReadDeletions( u );
  1266. }
  1267. // Something didn't parse...
  1268. if ( u.m_pBuf->IsOverflowed() )
  1269. {
  1270. Host_Error ( "CL_ParsePacketEntities: buffer read overflow\n" );
  1271. }
  1272. // If we get an uncompressed packet, then the server is waiting for us to ack the validsequence
  1273. // that we got the uncompressed packet on. So we stop reading packets here and force ourselves to
  1274. // send the clc_move on the next frame.
  1275. if ( !u.m_bAsDelta )
  1276. {
  1277. m_flNextCmdTime = 0.0; // answer ASAP to confirm full update tick
  1278. }
  1279. }
  1280. //-----------------------------------------------------------------------------
  1281. // Purpose:
  1282. // Input : *pHead -
  1283. // *pClassName -
  1284. // Output : static ClientClass*
  1285. //-----------------------------------------------------------------------------
  1286. ClientClass* CBaseClientState::FindClientClass(const char *pClassName)
  1287. {
  1288. if ( !pClassName )
  1289. return NULL;
  1290. for(ClientClass *pCur=ClientDLL_GetAllClasses(); pCur; pCur=pCur->m_pNext)
  1291. {
  1292. if( Q_stricmp(pCur->m_pNetworkName, pClassName) == 0)
  1293. return pCur;
  1294. }
  1295. return NULL;
  1296. }
  1297. bool CBaseClientState::LinkClasses()
  1298. {
  1299. // // Verify that we have received info about all classes.
  1300. // for ( int i=0; i < m_nServerClasses; i++ )
  1301. // {
  1302. // if ( !m_pServerClasses[i].m_DatatableName )
  1303. // {
  1304. // Host_EndGame(true, "CL_ParseClassInfo_EndClasses: class %d not initialized.\n", i);
  1305. // return false;
  1306. // }
  1307. // }
  1308. // Match the server classes to the client classes.
  1309. for ( int i=0; i < m_nServerClasses; i++ )
  1310. {
  1311. C_ServerClassInfo *pServerClass = &m_pServerClasses[i];
  1312. if ( !pServerClass->m_DatatableName )
  1313. continue;
  1314. // (this can be null in which case we just use default behavior).
  1315. pServerClass->m_pClientClass = FindClientClass(pServerClass->m_ClassName);
  1316. if ( pServerClass->m_pClientClass )
  1317. {
  1318. // If the class names match, then their datatables must match too.
  1319. // It's ok if the client is missing a class that the server has. In that case,
  1320. // if the server actually tries to use it, the client will bomb out.
  1321. const char *pServerName = pServerClass->m_DatatableName;
  1322. const char *pClientName = pServerClass->m_pClientClass->m_pRecvTable->GetName();
  1323. if ( Q_stricmp( pServerName, pClientName ) != 0 )
  1324. {
  1325. Host_EndGame( true, "CL_ParseClassInfo_EndClasses: server and client classes for '%s' use different datatables (server: %s, client: %s)",
  1326. pServerClass->m_ClassName, pServerName, pClientName );
  1327. return false;
  1328. }
  1329. // copy class ID
  1330. pServerClass->m_pClientClass->m_ClassID = i;
  1331. }
  1332. else
  1333. {
  1334. Msg( "Client missing DT class %s\n", pServerClass->m_ClassName );
  1335. }
  1336. }
  1337. return true;
  1338. }
  1339. PackedEntity *CBaseClientState::GetEntityBaseline(int iBaseline, int nEntityIndex)
  1340. {
  1341. Assert( (iBaseline == 0) || (iBaseline == 1) );
  1342. return m_pEntityBaselines[iBaseline][nEntityIndex];
  1343. }
  1344. void CBaseClientState::FreeEntityBaselines()
  1345. {
  1346. for ( int i=0; i<2; i++ )
  1347. {
  1348. for ( int j=0; j<MAX_EDICTS; j++ )
  1349. if ( m_pEntityBaselines[i][j] )
  1350. {
  1351. delete m_pEntityBaselines[i][j];
  1352. m_pEntityBaselines[i][j] = NULL;
  1353. }
  1354. }
  1355. }
  1356. void CBaseClientState::SetEntityBaseline(int iBaseline, ClientClass *pClientClass, int index, char *packedData, int length)
  1357. {
  1358. VPROF( "CBaseClientState::SetEntityBaseline" );
  1359. Assert( index >= 0 && index < MAX_EDICTS );
  1360. Assert( pClientClass );
  1361. Assert( (iBaseline == 0) || (iBaseline == 1) );
  1362. PackedEntity *entitybl = m_pEntityBaselines[iBaseline][index];
  1363. if ( !entitybl )
  1364. {
  1365. entitybl = m_pEntityBaselines[iBaseline][index] = new PackedEntity();
  1366. }
  1367. entitybl->m_pClientClass = pClientClass;
  1368. entitybl->m_nEntityIndex = index;
  1369. entitybl->m_pServerClass = NULL;
  1370. // Copy out the data we just decoded.
  1371. entitybl->AllocAndCopyPadded( packedData, length );
  1372. }
  1373. void CBaseClientState::CopyEntityBaseline( int iFrom, int iTo )
  1374. {
  1375. Assert ( iFrom != iTo );
  1376. for ( int i=0; i<MAX_EDICTS; i++ )
  1377. {
  1378. PackedEntity *blfrom = m_pEntityBaselines[iFrom][i];
  1379. PackedEntity *blto = m_pEntityBaselines[iTo][i];
  1380. if( !blfrom )
  1381. {
  1382. // make sure blto doesn't exists
  1383. if ( blto )
  1384. {
  1385. // ups, we already had this entity but our ack got lost
  1386. // we have to remove it again to stay in sync
  1387. delete m_pEntityBaselines[iTo][i];
  1388. m_pEntityBaselines[iTo][i] = NULL;
  1389. }
  1390. continue;
  1391. }
  1392. if ( !blto )
  1393. {
  1394. // create new to baseline if none existed before
  1395. blto = m_pEntityBaselines[iTo][i] = new PackedEntity();
  1396. blto->m_pClientClass = NULL;
  1397. blto->m_pServerClass = NULL;
  1398. blto->m_ReferenceCount = 0;
  1399. }
  1400. Assert( blfrom->m_nEntityIndex == i );
  1401. Assert( !blfrom->IsCompressed() );
  1402. blto->m_nEntityIndex = blfrom->m_nEntityIndex;
  1403. blto->m_pClientClass = blfrom->m_pClientClass;
  1404. blto->m_pServerClass = blfrom->m_pServerClass;
  1405. blto->AllocAndCopyPadded( blfrom->GetData(), blfrom->GetNumBytes() );
  1406. }
  1407. }
  1408. ClientClass *CBaseClientState::GetClientClass( int index )
  1409. {
  1410. Assert( index < m_nServerClasses );
  1411. return m_pServerClasses[index].m_pClientClass;
  1412. }
  1413. bool CBaseClientState::GetClassBaseline( int iClass, void const **pData, int *pDatalen )
  1414. {
  1415. ErrorIfNot(
  1416. iClass >= 0 && iClass < m_nServerClasses,
  1417. ("GetDynamicBaseline: invalid class index '%d'", iClass) );
  1418. // We lazily update these because if you connect to a server that's already got some dynamic baselines,
  1419. // you'll get the baselines BEFORE you get the class descriptions.
  1420. C_ServerClassInfo *pInfo = &m_pServerClasses[iClass];
  1421. INetworkStringTable *pBaselineTable = GetStringTable( INSTANCE_BASELINE_TABLENAME );
  1422. ErrorIfNot( pBaselineTable != NULL, ("GetDynamicBaseline: NULL baseline table" ) );
  1423. if ( pInfo->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  1424. {
  1425. // The key is the class index string.
  1426. char str[64];
  1427. Q_snprintf( str, sizeof( str ), "%d", iClass );
  1428. pInfo->m_InstanceBaselineIndex = pBaselineTable->FindStringIndex( str );
  1429. if ( pInfo->m_InstanceBaselineIndex == INVALID_STRING_INDEX )
  1430. {
  1431. for (int i = 0; i < pBaselineTable->GetNumStrings(); ++i )
  1432. {
  1433. DevMsg( "%i: %s\n", i, pBaselineTable->GetString( i ) );
  1434. }
  1435. // Gets a callstack, whereas ErrorIfNot(), does not.
  1436. Assert( 0 );
  1437. }
  1438. ErrorIfNot(
  1439. pInfo->m_InstanceBaselineIndex != INVALID_STRING_INDEX,
  1440. ("GetDynamicBaseline: FindStringIndex(%s-%s) failed.", str, pInfo->m_ClassName );
  1441. );
  1442. }
  1443. *pData = pBaselineTable->GetStringUserData( pInfo->m_InstanceBaselineIndex, pDatalen );
  1444. return *pData != NULL;
  1445. }
  1446. bool CBaseClientState::ProcessGameEventList( SVC_GameEventList *msg )
  1447. {
  1448. VPROF( "ProcessGameEventList" );
  1449. return g_GameEventManager.ParseEventList( msg );
  1450. }
  1451. bool CBaseClientState::ProcessGetCvarValue( SVC_GetCvarValue *msg )
  1452. {
  1453. VPROF( "ProcessGetCvarValue" );
  1454. // Prepare the response.
  1455. CLC_RespondCvarValue returnMsg;
  1456. returnMsg.m_iCookie = msg->m_iCookie;
  1457. returnMsg.m_szCvarName = msg->m_szCvarName;
  1458. returnMsg.m_szCvarValue = "";
  1459. returnMsg.m_eStatusCode = eQueryCvarValueStatus_CvarNotFound;
  1460. char tempValue[256];
  1461. // Does any ConCommand exist with this name?
  1462. const ConVar *pVar = g_pCVar->FindVar( msg->m_szCvarName );
  1463. if ( pVar )
  1464. {
  1465. if ( pVar->IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
  1466. {
  1467. // The server isn't allowed to query this.
  1468. returnMsg.m_eStatusCode = eQueryCvarValueStatus_CvarProtected;
  1469. }
  1470. else
  1471. {
  1472. returnMsg.m_eStatusCode = eQueryCvarValueStatus_ValueIntact;
  1473. if ( pVar->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  1474. {
  1475. // The cvar won't store a string, so we have to come up with a string for it ourselves.
  1476. if ( fabs( pVar->GetFloat() - pVar->GetInt() ) < 0.001f )
  1477. {
  1478. Q_snprintf( tempValue, sizeof( tempValue ), "%d", pVar->GetInt() );
  1479. }
  1480. else
  1481. {
  1482. Q_snprintf( tempValue, sizeof( tempValue ), "%f", pVar->GetFloat() );
  1483. }
  1484. returnMsg.m_szCvarValue = tempValue;
  1485. }
  1486. else
  1487. {
  1488. // The easy case..
  1489. returnMsg.m_szCvarValue = pVar->GetString();
  1490. }
  1491. }
  1492. }
  1493. else
  1494. {
  1495. if ( g_pCVar->FindCommand( msg->m_szCvarName ) )
  1496. returnMsg.m_eStatusCode = eQueryCvarValueStatus_NotACvar; // It's a command, not a cvar.
  1497. else
  1498. returnMsg.m_eStatusCode = eQueryCvarValueStatus_CvarNotFound;
  1499. }
  1500. // Send back.
  1501. m_NetChannel->SendNetMsg( returnMsg );
  1502. return true;
  1503. }
  1504. // Returns dem file protocol version, or, if not playing a demo, just returns PROTOCOL_VERSION
  1505. int CBaseClientState::GetDemoProtocolVersion() const
  1506. {
  1507. #ifndef SWDS // why is demo play undefined? it shuold be fine on a dedicated server
  1508. if ( demoplayer->IsPlayingBack() )
  1509. {
  1510. return demoplayer->GetProtocolVersion();
  1511. }
  1512. #endif
  1513. return PROTOCOL_VERSION;
  1514. }
  1515. bool CBaseClientState::ProcessCmdKeyValues( SVC_CmdKeyValues *msg )
  1516. {
  1517. return true;
  1518. }
  1519. bool CBaseClientState::IsClientConnectionViaMatchMaking( void )
  1520. {
  1521. return ( V_strnistr( cl_connectmethod.GetString(), "quickplay", 9 ) || V_strnistr( cl_connectmethod.GetString(), "matchmaking", 11 ) );
  1522. }