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.

615 lines
17 KiB

  1. //===== Copyright (c) Valve Corporation, All rights reserved. ======//
  2. //
  3. // hltvclient.cpp: implementation of the CHLTVClient class.
  4. //
  5. // $NoKeywords: $
  6. //
  7. //==================================================================//
  8. #include <tier0/vprof.h>
  9. #include "hltvclient.h"
  10. #include "netmessages.h"
  11. #include "hltvserver.h"
  12. #include "framesnapshot.h"
  13. #include "networkstringtable.h"
  14. #include "dt_send_eng.h"
  15. #include "GameEventManager.h"
  16. #include "cmd.h"
  17. #include "ihltvdirector.h"
  18. #include "host.h"
  19. #include "sv_steamauth.h"
  20. #include "fmtstr.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. static ConVar tv_maxrate( "tv_maxrate", STRINGIFY( DEFAULT_RATE ), FCVAR_RELEASE, "Max GOTV spectator bandwidth rate allowed, 0 == unlimited" );
  24. static ConVar tv_relaypassword( "tv_relaypassword", "", FCVAR_NOTIFY | FCVAR_PROTECTED | FCVAR_DONTRECORD | FCVAR_RELEASE, "GOTV password for relay proxies" );
  25. static ConVar tv_chattimelimit( "tv_chattimelimit", "8", FCVAR_RELEASE, "Limits spectators to chat only every n seconds" );
  26. static ConVar tv_chatgroupsize( "tv_chatgroupsize", "0", FCVAR_RELEASE, "Set the default chat group size" );
  27. extern ConVar replay_debug;
  28. //////////////////////////////////////////////////////////////////////
  29. // Construction/Destruction
  30. //////////////////////////////////////////////////////////////////////
  31. CHLTVClient::CHLTVClient(int slot, CBaseServer *pServer)
  32. {
  33. Clear();
  34. m_nClientSlot = slot;
  35. m_Server = pServer;
  36. m_pHLTV = dynamic_cast<CHLTVServer*>(pServer);
  37. Assert( g_pHltvServer[ m_pHLTV->GetInstanceIndex() ] == pServer );
  38. m_nEntityIndex = slot < 0 ? slot : m_pHLTV->GetHLTVSlot() + 1;
  39. m_nLastSendTick = 0;
  40. m_fLastSendTime = 0.0f;
  41. m_flLastChatTime = 0.0f;
  42. m_bNoChat = false;
  43. if ( tv_chatgroupsize.GetInt() > 0 )
  44. {
  45. Q_snprintf( m_szChatGroup, sizeof(m_szChatGroup), "group%d", slot%tv_chatgroupsize.GetInt() );
  46. }
  47. else
  48. {
  49. Q_strncpy( m_szChatGroup, "all", sizeof(m_szChatGroup) );
  50. }
  51. }
  52. CHLTVClient::~CHLTVClient()
  53. {
  54. }
  55. bool CHLTVClient::SendSignonData( void )
  56. {
  57. // check class table CRCs
  58. if ( m_nSendtableCRC != SendTable_GetCRC() )
  59. {
  60. Disconnect( "Server uses different class tables" );
  61. return false;
  62. }
  63. else
  64. {
  65. // use your class infos, CRC is correct
  66. CSVCMsg_ClassInfo_t classmsg;
  67. classmsg.set_create_on_client( true );
  68. m_NetChannel->SendNetMsg( classmsg );
  69. }
  70. return CBaseClient::SendSignonData();
  71. }
  72. bool CHLTVClient::ProcessSignonStateMsg(int state, int spawncount)
  73. {
  74. if ( !CBaseClient::ProcessSignonStateMsg( state, spawncount ) )
  75. return false;
  76. if ( state == SIGNONSTATE_FULL )
  77. {
  78. // Send all the delayed avatar data to the fully connected client
  79. if ( INetChannel *pMyNetChannel = GetNetChannel() )
  80. {
  81. FOR_EACH_MAP_FAST( m_pHLTV->m_mapPlayerAvatarData, iData )
  82. {
  83. pMyNetChannel->EnqueueVeryLargeAsyncTransfer( *m_pHLTV->m_mapPlayerAvatarData.Element( iData ) );
  84. }
  85. }
  86. }
  87. return true;
  88. }
  89. bool CHLTVClient::CLCMsg_ClientInfo( const CCLCMsg_ClientInfo& msg )
  90. {
  91. if ( !CBaseClient::CLCMsg_ClientInfo( msg ) )
  92. return false;
  93. return true;
  94. }
  95. bool CHLTVClient::CLCMsg_Move( const CCLCMsg_Move& msg )
  96. {
  97. // HLTV clients can't move
  98. return true;
  99. }
  100. bool CHLTVClient::CLCMsg_ListenEvents( const CCLCMsg_ListenEvents& msg )
  101. {
  102. // HLTV clients can't subscribe to events, we just send them
  103. return true;
  104. }
  105. bool CHLTVClient::CLCMsg_RespondCvarValue( const CCLCMsg_RespondCvarValue& msg )
  106. {
  107. return true;
  108. }
  109. bool CHLTVClient::CLCMsg_FileCRCCheck( const CCLCMsg_FileCRCCheck& msg )
  110. {
  111. return true;
  112. }
  113. bool CHLTVClient::CLCMsg_VoiceData(const CCLCMsg_VoiceData& msg)
  114. {
  115. // HLTV clients can't speak
  116. return true;
  117. }
  118. void CHLTVClient::ConnectionClosing(const char *reason)
  119. {
  120. Disconnect ( (reason!=NULL)?reason:"Connection closing" );
  121. }
  122. void CHLTVClient::ConnectionCrashed(const char *reason)
  123. {
  124. Disconnect ( (reason!=NULL)?reason:"Connection lost" );
  125. }
  126. void CHLTVClient::PacketStart(int incoming_sequence, int outgoing_acknowledged)
  127. {
  128. // During connection, only respond if client sends a packet
  129. m_bReceivedPacket = true;
  130. }
  131. void CHLTVClient::PacketEnd()
  132. {
  133. }
  134. void CHLTVClient::FileRequested(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  135. {
  136. DevMsg( "CHLTVClient::FileRequested: %s.\n", fileName );
  137. m_NetChannel->DenyFile( fileName, transferID, bIsReplayDemoFile );
  138. }
  139. void CHLTVClient::FileDenied(const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  140. {
  141. DevMsg( "CHLTVClient::FileDenied: %s.\n", fileName );
  142. }
  143. void CHLTVClient::FileReceived( const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  144. {
  145. DevMsg( "CHLTVClient::FileReceived: %s.\n", fileName );
  146. }
  147. void CHLTVClient::FileSent( const char *fileName, unsigned int transferID, bool bIsReplayDemoFile /* = false */ )
  148. {
  149. DevMsg( "CHLTVClient::FileSent: %s.\n", fileName );
  150. }
  151. CClientFrame *CHLTVClient::GetDeltaFrame( int nTick )
  152. {
  153. return m_pHLTV->GetDeltaFrame( nTick );
  154. }
  155. bool CHLTVClient::ExecuteStringCommand( const char *pCommandString )
  156. {
  157. // first let the baseclass handle it
  158. if ( CBaseClient::ExecuteStringCommand( pCommandString ) )
  159. return true;
  160. if ( !pCommandString || !pCommandString[0] )
  161. return true;
  162. CCommand args;
  163. if ( !args.Tokenize( pCommandString, kCommandSrcNetServer ) )
  164. return true;
  165. const char *cmd = args[ 0 ];
  166. if ( !Q_stricmp( cmd, "spec_next" ) ||
  167. !Q_stricmp( cmd, "spec_prev" ) ||
  168. !Q_stricmp( cmd, "spec_mode" ) ||
  169. !Q_stricmp( cmd, "spec_goto" ) ||
  170. !Q_stricmp( cmd, "spec_lerpto" ) )
  171. {
  172. ClientPrintf("Camera settings can't be changed during a live broadcast.\n");
  173. return true;
  174. }
  175. if ( !Q_stricmp( cmd, "say" ) && args.ArgC() > 1 )
  176. {
  177. // if tv_chattimelimit = 0, chat is turned off
  178. if ( tv_chattimelimit.GetFloat() <= 0 )
  179. return true;
  180. if ( (m_flLastChatTime + tv_chattimelimit.GetFloat()) > net_time )
  181. return true;
  182. m_flLastChatTime = net_time;
  183. // Check if chat is non-empty string
  184. bool bValidText = false;
  185. for ( char const *szChatMsg = args[1]; szChatMsg && *szChatMsg; ++ szChatMsg )
  186. {
  187. if ( !V_isspace( *szChatMsg ) )
  188. {
  189. bValidText = true;
  190. break;
  191. }
  192. }
  193. if ( !bValidText )
  194. return true;
  195. char chattext[128];
  196. V_sprintf_safe( chattext, "%s : %s", GetClientName(), args[1] );
  197. m_pHLTV->BroadcastLocalChat( chattext, m_szChatGroup );
  198. return true;
  199. }
  200. else if ( !Q_strcmp( cmd, "tv_chatgroup" ) )
  201. {
  202. if ( args.ArgC() > 1 )
  203. {
  204. Q_strncpy( m_szChatGroup, args[1], sizeof(m_szChatGroup) );
  205. }
  206. else
  207. {
  208. ClientPrintf("Your current chat group is \"%s\"\n", m_szChatGroup );
  209. }
  210. return true;
  211. }
  212. else if ( !Q_strcmp( cmd, "status" ) )
  213. {
  214. int slots, proxies, clients;
  215. char gd[MAX_OSPATH];
  216. Q_FileBase( com_gamedir, gd, sizeof( gd ) );
  217. if ( m_pHLTV->IsMasterProxy() )
  218. {
  219. ClientPrintf("GOTV Master \"%s\", delay %.0f\n",
  220. m_pHLTV->GetName(), m_pHLTV->GetDirector()->GetDelay() );
  221. }
  222. else // if ( m_Server->IsRelayProxy() )
  223. {
  224. if ( m_pHLTV->GetRelayAddress() )
  225. {
  226. ClientPrintf("GOTV Relay \"%s\", connected.\n",
  227. m_pHLTV->GetName() );
  228. }
  229. else
  230. {
  231. ClientPrintf("GOTV Relay \"%s\", not connect.\n", m_pHLTV->GetName() );
  232. }
  233. }
  234. ClientPrintf("IP %s:%i, Online %s, Version %i (%s)\n",
  235. net_local_adr.ToString( true ), m_pHLTV->GetUDPPort(),
  236. COM_FormatSeconds( m_pHLTV->GetOnlineTime() ), build_number(),
  237. #ifdef _WIN32
  238. "Win32" );
  239. #else
  240. "Linux" );
  241. #endif
  242. ClientPrintf("Game Time %s, Mod \"%s\", Map \"%s\", Players %i\n", COM_FormatSeconds( m_pHLTV->GetTime() ),
  243. gd, m_pHLTV->GetMapName(), m_pHLTV->GetNumPlayers() );
  244. m_pHLTV->GetLocalStats( proxies, slots, clients );
  245. ClientPrintf("Local Slots %i, Spectators %i, Proxies %i\n",
  246. slots, clients-proxies, proxies );
  247. m_pHLTV->GetGlobalStats( proxies, slots, clients);
  248. ClientPrintf("Total Slots %i, Spectators %i, Proxies %i\n",
  249. slots, clients-proxies, proxies);
  250. m_pHLTV->GetExternalStats( slots, clients );
  251. if ( slots > 0 )
  252. {
  253. if ( clients > 0 )
  254. ClientPrintf( "Streaming spectators %i, linked to Steam %i\n", slots, clients );
  255. else
  256. ClientPrintf( "Streaming spectators %i\n", slots );
  257. }
  258. }
  259. else
  260. {
  261. DevMsg( "CHLTVClient::ExecuteStringCommand: Unknown command %s.\n", pCommandString );
  262. }
  263. return true;
  264. }
  265. bool CHLTVClient::ShouldSendMessages( void )
  266. {
  267. if ( !IsActive() )
  268. {
  269. // during signon behave like normal client
  270. return CBaseClient::ShouldSendMessages();
  271. }
  272. // HLTV clients use snapshot rate used by HLTV server, not given by HLTV client
  273. // if the reliable message overflowed, drop the client
  274. if ( m_NetChannel->IsOverflowed() )
  275. {
  276. m_NetChannel->Reset();
  277. Disconnect( CFmtStr( "%s overflowed reliable buffer", m_Name ) );
  278. return false;
  279. }
  280. // send a packet if server has a new tick we didn't already send
  281. bool bSendMessage = ( m_nLastSendTick != m_Server->m_nTickCount );
  282. // send a packet at least every 2 seconds
  283. if ( !bSendMessage && (m_fLastSendTime + 2.0f) < net_time )
  284. {
  285. bSendMessage = true; // force sending a message even if server didn't update
  286. }
  287. if ( bSendMessage && !m_NetChannel->CanPacket() )
  288. {
  289. // we would like to send a message, but bandwidth isn't available yet
  290. // in HLTV we don't send choke information, doesn't matter
  291. bSendMessage = false;
  292. }
  293. return bSendMessage;
  294. }
  295. void CHLTVClient::SpawnPlayer( void )
  296. {
  297. // set view entity
  298. CSVCMsg_SetView_t setView;
  299. setView.set_entity_index( m_pHLTV->m_nViewEntity );
  300. SendNetMsg( setView );
  301. m_pHLTV->BroadcastLocalTitle( this );
  302. m_flLastChatTime = net_time;
  303. CBaseClient::SpawnPlayer();
  304. }
  305. void CHLTVClient::SetRate(int nRate, bool bForce )
  306. {
  307. if ( !bForce )
  308. {
  309. if ( m_bIsHLTV )
  310. {
  311. // allow higher bandwidth rates for HLTV proxies
  312. nRate = clamp( nRate, MIN_RATE, MAX_RATE );
  313. }
  314. else if ( tv_maxrate.GetInt() > 0 )
  315. {
  316. // restrict rate for normal clients to hltv_maxrate
  317. nRate = clamp( nRate, MIN_RATE, tv_maxrate.GetInt() );
  318. }
  319. }
  320. CBaseClient::SetRate( nRate, bForce );
  321. }
  322. void CHLTVClient::SetUpdateRate( float fUpdateRate, bool bForce)
  323. {
  324. // for HLTV clients ignore update rate settings, speed is tv_snapshotrate
  325. m_fSnapshotInterval = 1.0f / m_pHLTV->GetSnapshotRate();
  326. }
  327. bool CHLTVClient::NETMsg_SetConVar(const CNETMsg_SetConVar& msg)
  328. {
  329. if ( !CBaseClient::NETMsg_SetConVar( msg ) )
  330. return false;
  331. // if this is the first time we get user settings, check password etc
  332. if ( GetSignonState() == SIGNONSTATE_CONNECTED )
  333. {
  334. // Note: the master client of HLTV server will replace the rate ConVars for us. It's necessary so that demo recorder can take those frames from the master client and write them with values already modified
  335. m_bIsHLTV = m_ConVars->GetInt( "tv_relay", 0 ) != 0;
  336. if ( m_bIsHLTV )
  337. {
  338. // The connecting client is a TV relay
  339. // Check if this relay address is whitelisted by IP range mask and bypasses all checks
  340. extern bool IsHltvRelayProxyWhitelisted( ns_address const &adr );
  341. if ( IsHltvRelayProxyWhitelisted( m_NetChannel->GetRemoteAddress() ) )
  342. {
  343. Msg( "Accepted GOTV relay proxy from whitelisted IP address: %s\n", m_NetChannel->GetAddress() );
  344. }
  345. // if the connecting client is a TV relay, check the password
  346. else if ( !m_pHLTV->CheckHltvPasswordMatch( m_szPassword, m_pHLTV->GetHltvRelayPassword(), CSteamID() ) )
  347. {
  348. Disconnect("Bad relay password");
  349. return false;
  350. }
  351. }
  352. else
  353. {
  354. // if client is a normal spectator, check if we can to forward him to other relays
  355. if ( m_pHLTV->DispatchToRelay( this ) )
  356. {
  357. return false;
  358. }
  359. // if we are not dispatching the client to other relay and we are the master server then validate
  360. // the number of non-proxy clients
  361. extern ConVar tv_maxclients_relayreserved;
  362. if ( tv_maxclients_relayreserved.GetInt() )
  363. {
  364. int numActualNonProxyAccounts = 0;
  365. for (int i=0; i < m_pHLTV->GetClientCount(); i++ )
  366. {
  367. CBaseClient *pProxy = static_cast< CBaseClient * >( m_pHLTV->GetClient( i ) );
  368. // check if this is a proxy
  369. if ( !pProxy->IsConnected() || pProxy->IsHLTV() || (this == pProxy) )
  370. continue;
  371. ++ numActualNonProxyAccounts;
  372. }
  373. if ( numActualNonProxyAccounts > m_pHLTV->GetMaxClients() - tv_maxclients_relayreserved.GetInt() )
  374. {
  375. this->Disconnect( "No GOTV relays available" );
  376. return false;
  377. }
  378. }
  379. // if client stays here, check the normal password
  380. // additionally if the first variable is client accountid then use that to validate personalized password
  381. CSteamID steamUserAccountID;
  382. if ( Steam3Server().SteamGameServerUtils() &&
  383. ( msg.convars().cvars_size() > 1 ) &&
  384. !Q_strcmp( NetMsgGetCVarUsingDictionary( msg.convars().cvars( 0 ) ), "accountid" ) )
  385. steamUserAccountID = CSteamID( Q_atoi( msg.convars().cvars( 0 ).value().c_str() ), Steam3Server().SteamGameServerUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
  386. if ( !m_pHLTV->CheckHltvPasswordMatch( m_szPassword, m_pHLTV->GetPassword(), steamUserAccountID ) )
  387. {
  388. Disconnect("Bad spectator password");
  389. return false;
  390. }
  391. // check if server is LAN only
  392. if ( !m_pHLTV->CheckIPRestrictions( m_NetChannel->GetRemoteAddress(), PROTOCOL_HASHEDCDKEY ) )
  393. {
  394. Disconnect( "GOTV server is restricted to local spectators (class C).\n" );
  395. return false;
  396. }
  397. }
  398. }
  399. return true;
  400. }
  401. void CHLTVClient::UpdateUserSettings()
  402. {
  403. // set voice loopback
  404. m_bNoChat = m_ConVars->GetInt( "tv_nochat", 0 ) != 0;
  405. CBaseClient::UpdateUserSettings();
  406. }
  407. bool CHLTVClient::SendSnapshot( CClientFrame * pFrame )
  408. {
  409. VPROF_BUDGET( "CHLTVClient::SendSnapshot", "HLTV" );
  410. byte buf[NET_MAX_PAYLOAD];
  411. bf_write msg( "CHLTVClient::SendSnapshot", buf, sizeof(buf) );
  412. // if we send a full snapshot (no delta-compression) before, wait until client
  413. // received and acknowledge that update. don't spam client with full updates
  414. if ( m_pLastSnapshot == pFrame->GetSnapshot() )
  415. {
  416. // never send the same snapshot twice
  417. m_NetChannel->Transmit();
  418. return false;
  419. }
  420. if ( m_nForceWaitForTick > 0 )
  421. {
  422. // just continue transmitting reliable data
  423. Assert( !m_bFakePlayer ); // Should never happen
  424. m_NetChannel->Transmit();
  425. return false;
  426. }
  427. CClientFrame *pDeltaFrame = GetDeltaFrame( m_nDeltaTick ); // NULL if delta_tick is not found
  428. CHLTVFrame *pLastFrame = (CHLTVFrame*) GetDeltaFrame( m_nLastSendTick );
  429. if ( pLastFrame )
  430. {
  431. // start first frame after last send
  432. pLastFrame = (CHLTVFrame*) pLastFrame->m_pNext;
  433. }
  434. // add all reliable messages between ]lastframe,currentframe]
  435. // add all tempent & sound messages between ]lastframe,currentframe]
  436. while ( pLastFrame && pLastFrame->tick_count <= pFrame->tick_count )
  437. {
  438. m_NetChannel->SendData( pLastFrame->m_Messages[HLTV_BUFFER_RELIABLE], true );
  439. if ( pDeltaFrame )
  440. {
  441. // if we send entities delta compressed, also send unreliable data
  442. m_NetChannel->SendData( pLastFrame->m_Messages[HLTV_BUFFER_UNRELIABLE], false );
  443. m_NetChannel->SendData( pLastFrame->m_Messages[ HLTV_BUFFER_VOICE ], false ); // we separate voice, even though it's simply more unreliable data, because we don't send it in replay
  444. }
  445. pLastFrame = (CHLTVFrame*) pLastFrame->m_pNext;
  446. }
  447. // now create client snapshot packet
  448. // send tick time
  449. CNETMsg_Tick_t tickmsg( pFrame->tick_count, host_frameendtime_computationduration, host_frametime_stddeviation, host_framestarttime_stddeviation );
  450. tickmsg.WriteToBuffer( msg );
  451. // Update shared client/server string tables. Must be done before sending entities
  452. m_Server->m_StringTables->WriteUpdateMessage( NULL, GetMaxAckTickCount(), msg );
  453. // TODO delta cache whole snapshots, not just packet entities. then use net_Align
  454. // send entity update, delta compressed if deltaFrame != NULL
  455. {
  456. CSVCMsg_PacketEntities_t packetmsg;
  457. m_Server->WriteDeltaEntities( this, pFrame, pDeltaFrame, packetmsg );
  458. packetmsg.WriteToBuffer( msg );
  459. }
  460. // write message to packet and check for overflow
  461. if ( msg.IsOverflowed() )
  462. {
  463. if ( !pDeltaFrame )
  464. {
  465. // if this is a reliable snapshot, drop the client
  466. Disconnect( "ERROR! Reliable snapshot overflow." );
  467. return false;
  468. }
  469. else
  470. {
  471. // unreliable snapshots may be dropped
  472. ConMsg ("WARNING: msg overflowed for %s\n", m_Name);
  473. msg.Reset();
  474. }
  475. }
  476. // remember this snapshot
  477. m_pLastSnapshot = pFrame->GetSnapshot();
  478. m_nLastSendTick = pFrame->tick_count;
  479. // Don't send the datagram to fakeplayers
  480. if ( m_bFakePlayer )
  481. {
  482. m_nDeltaTick = pFrame->tick_count;
  483. return true;
  484. }
  485. bool bSendOK;
  486. // is this is a full entity update (no delta) ?
  487. if ( !pDeltaFrame )
  488. {
  489. if ( replay_debug.GetInt() >= 10 )
  490. Msg( "HLTV send full frame %d bytes\n", ( msg.m_iCurBit + 7 ) / 8 );
  491. // transmit snapshot as reliable data chunk
  492. bSendOK = m_NetChannel->SendData( msg );
  493. bSendOK = bSendOK && m_NetChannel->Transmit();
  494. // remember this tickcount we send the reliable snapshot
  495. // so we can continue sending other updates if this has been acknowledged
  496. m_nForceWaitForTick = pFrame->tick_count;
  497. }
  498. else
  499. {
  500. if ( replay_debug.GetInt() >= 10 )
  501. Msg( "HLTV send datagram %d bytes\n", ( msg.m_iCurBit + 7 ) / 8 );
  502. // just send it as unreliable snapshot
  503. bSendOK = m_NetChannel->SendDatagram( &msg ) > 0;
  504. }
  505. if ( !bSendOK )
  506. {
  507. Disconnect( "ERROR! Couldn't send snapshot." );
  508. return false;
  509. }
  510. return true;
  511. }