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.

4059 lines
117 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // hltvserver.cpp: implementation of the CHLTVServer class.
  9. //
  10. //////////////////////////////////////////////////////////////////////
  11. #include <server_class.h>
  12. #include <inetmessage.h>
  13. #include <tier0/vprof.h>
  14. #include <keyvalues.h>
  15. #include <edict.h>
  16. #include <eiface.h>
  17. #include <PlayerState.h>
  18. #include <ihltvdirector.h>
  19. #include <time.h>
  20. #include "hltvserver.h"
  21. #include "sv_client.h"
  22. #include "hltvclient.h"
  23. #include "server.h"
  24. #include "sv_main.h"
  25. #include "framesnapshot.h"
  26. #include "networkstringtable.h"
  27. #include "cmodel_engine.h"
  28. #include "dt_recv_eng.h"
  29. #include "cdll_engine_int.h"
  30. #include "GameEventManager.h"
  31. #include "host.h"
  32. #include "dt_common_eng.h"
  33. #include "baseautocompletefilelist.h"
  34. #include "sv_steamauth.h"
  35. #include "tier0/icommandline.h"
  36. #include "mathlib/IceKey.H"
  37. #include "tier1/fmtstr.h"
  38. #include "serializedentity.h"
  39. #include "changeframelist.h"
  40. #include "ihltv.h"
  41. // memdbgon must be the last include file in a .cpp file!!!
  42. #include "tier0/memdbgon.h"
  43. extern ConVar tv_snapshotrate, tv_snapshotrate1;
  44. extern CNetworkStringTableContainer *networkStringTableContainerClient;
  45. //we do not want our snapshot frames to be mixed in with the default snapshot frames
  46. static const uint32 knHLTVSnapshotSet = CFrameSnapshotManager::knDefaultSnapshotSet + 1;
  47. //////////////////////////////////////////////////////////////////////
  48. // Construction/Destruction
  49. //////////////////////////////////////////////////////////////////////
  50. CHLTVServer *g_pHltvServer[ HLTV_SERVER_MAX_COUNT ] = { NULL, NULL };
  51. bool IsHltvActive()
  52. {
  53. CActiveHltvServerIterator hltv;
  54. if ( hltv )
  55. return true; // found at least one HLTV active
  56. else
  57. return false;
  58. }
  59. static void tv_title_changed_f( IConVar *var, const char *pOldString, float flOldValue )
  60. {
  61. for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() )
  62. {
  63. hltv->BroadcastLocalTitle(); // broadcast hltv_title to the respective clients of this HLTV server
  64. }
  65. }
  66. static void tv_name_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  67. {
  68. Steam3Server().NotifyOfServerNameChange();
  69. }
  70. ConVar tv_maxclients( "tv_maxclients", "128", FCVAR_RELEASE, "Maximum client number on GOTV server.",
  71. true, 0, true, 255 );
  72. ConVar tv_maxclients_relayreserved( "tv_maxclients_relayreserved", "0", FCVAR_RELEASE, "Reserves a certain number of GOTV client slots for relays.",
  73. true, 0, true, 255 );
  74. ConVar tv_autorecord( "tv_autorecord", "0", FCVAR_RELEASE, "Automatically records all games as GOTV demos." );
  75. void OnTvBroadcast( IConVar *var, const char *pOldValue, float flOldValue );
  76. ConVar tv_broadcast( "tv_broadcast", "0", FCVAR_RELEASE, "Automatically broadcasts all games as GOTV demos through Steam.", OnTvBroadcast );
  77. ConVar tv_broadcast1( "tv_broadcast1", "0", FCVAR_RELEASE, "Automatically broadcasts all games as GOTV[1] demos through Steam.", OnTvBroadcast );
  78. ConVar tv_name( "tv_name", "GOTV", FCVAR_RELEASE, "GOTV host name", tv_name_changed_f );
  79. static ConVar tv_password( "tv_password", "", FCVAR_NOTIFY | FCVAR_PROTECTED | FCVAR_DONTRECORD | FCVAR_RELEASE, "GOTV password for all clients" );
  80. static ConVar tv_advertise_watchable( "tv_advertise_watchable", "0", FCVAR_NOTIFY | FCVAR_PROTECTED | FCVAR_DONTRECORD | FCVAR_RELEASE, "GOTV advertises the match as watchable via game UI, clients watching via UI will not need to type password" );
  81. static ConVar tv_overridemaster( "tv_overridemaster", "0", FCVAR_RELEASE, "Overrides the GOTV master root address." );
  82. static ConVar tv_dispatchmode( "tv_dispatchmode", "1", FCVAR_RELEASE, "Dispatch clients to relay proxies: 0=never, 1=if appropriate, 2=always" );
  83. static ConVar tv_dispatchweight( "tv_dispatchweight", "1.25", FCVAR_RELEASE, "Dispatch clients to relay proxies based on load, 1.25 will prefer for every 4 local clients to put 5 clients on every connected relay" );
  84. ConVar tv_transmitall( "tv_transmitall", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Transmit all entities (not only director view)" );
  85. ConVar tv_debug( "tv_debug", "0", FCVAR_RELEASE, "GOTV debug info." );
  86. ConVar tv_title( "tv_title", "GOTV", FCVAR_RELEASE, "Set title for GOTV spectator UI", tv_title_changed_f );
  87. static ConVar tv_deltacache( "tv_deltacache", "2", FCVAR_RELEASE, "Enable delta entity bit stream cache" );
  88. static ConVar tv_relayvoice( "tv_relayvoice", "1", FCVAR_RELEASE, "Relay voice data: 0=off, 1=on" );
  89. static ConVar tv_encryptdata_key( "tv_encryptdata_key", "", FCVAR_RELEASE, "When set to a valid key communication messages will be encrypted for GOTV" );
  90. static ConVar tv_encryptdata_key_pub( "tv_encryptdata_key_pub", "", FCVAR_RELEASE, "When set to a valid key public communication messages will be encrypted for GOTV" );
  91. static ConVar tv_window_size( "tv_window_size", "16.0", FCVAR_NONE, "Specifies the number of seconds worth of frames that the tv replay system should keep in memory. Increasing this greatly increases the amount of memory consumed by the TV system" );
  92. static ConVar tv_enable_delta_frames( "tv_enable_delta_frames", "1", FCVAR_RELEASE, "Indicates whether or not the tv should use delta frames for storage of intermediate frames. This takes more CPU but significantly less memory." );
  93. extern ConVar spec_replay_enable;
  94. extern ConVar spec_replay_message_time;
  95. ConVar spec_replay_leadup_time( "spec_replay_leadup_time", "5.3438", FCVAR_RELEASE | FCVAR_REPLICATED, "Replay time in seconds before the highlighted event" );
  96. ConVar tv_broadcast_url( "tv_broadcast_url", "http://localhost:8080", FCVAR_RELEASE, "URL of the broadcast relay" );
  97. CHLTVServer::SHLTVDeltaFrame_t::SHLTVDeltaFrame_t() :
  98. m_pRelativeFrame( NULL ),
  99. m_pSourceFrame( NULL ),
  100. m_pClientFrame( NULL ),
  101. m_nNumValidEntities( 0 ),
  102. m_nTotalEntities( 0 ),
  103. m_pEntities( NULL ),
  104. m_pNewerDeltaFrame( NULL )
  105. {}
  106. CHLTVServer::SHLTVDeltaFrame_t::~SHLTVDeltaFrame_t()
  107. {
  108. delete [] m_pCopyEntities;
  109. m_pCopyEntities = NULL;
  110. //delete the client frame
  111. delete m_pClientFrame;
  112. m_pClientFrame = NULL;
  113. //delete our entity linked list
  114. while( m_pEntities )
  115. {
  116. SHLTVDeltaEntity_t* pDel = m_pEntities;
  117. m_pEntities = m_pEntities->m_pNext;
  118. delete pDel;
  119. }
  120. if( m_pRelativeFrame )
  121. m_pRelativeFrame->ReleaseReference();
  122. if( m_pSourceFrame )
  123. m_pSourceFrame->ReleaseReference();
  124. }
  125. CHLTVServer::SHLTVDeltaEntity_t::SHLTVDeltaEntity_t() :
  126. m_SerializedEntity( SERIALIZED_ENTITY_HANDLE_INVALID ),
  127. m_pServerClass( NULL ),
  128. m_pNewRecipients( NULL ),
  129. m_pNext( NULL )
  130. {
  131. }
  132. CHLTVServer::SHLTVDeltaEntity_t::~SHLTVDeltaEntity_t()
  133. {
  134. //free any recipients if we allocated them
  135. delete [] m_pNewRecipients;
  136. if( (m_SerializedEntity != knNoPackedData) && ( m_SerializedEntity != SERIALIZED_ENTITY_HANDLE_INVALID ) )
  137. delete ( CSerializedEntity* )m_SerializedEntity;
  138. }
  139. CDeltaEntityCache::CDeltaEntityCache()
  140. {
  141. Q_memset( m_Cache, 0, sizeof(m_Cache) );
  142. m_nTick = 0;
  143. m_nMaxEntities = 0;
  144. m_nCacheSize = 0;
  145. }
  146. CDeltaEntityCache::~CDeltaEntityCache()
  147. {
  148. Flush();
  149. }
  150. void CDeltaEntityCache::Flush()
  151. {
  152. if ( m_nMaxEntities != 0 )
  153. {
  154. // at least one entity was set
  155. for ( int i=0; i<m_nMaxEntities; i++ )
  156. {
  157. if ( m_Cache[i] != NULL )
  158. {
  159. free( m_Cache[i] );
  160. m_Cache[i] = NULL;
  161. }
  162. }
  163. m_nMaxEntities = 0;
  164. }
  165. m_nCacheSize = 0;
  166. }
  167. void CDeltaEntityCache::SetTick( int nTick, int nMaxEntities )
  168. {
  169. if ( nTick == m_nTick )
  170. return;
  171. Flush();
  172. m_nCacheSize = tv_deltacache.GetInt() * 1024;
  173. if ( m_nCacheSize <= 0 )
  174. return;
  175. m_nMaxEntities = MIN(nMaxEntities,MAX_EDICTS);
  176. m_nTick = nTick;
  177. }
  178. unsigned char* CDeltaEntityCache::FindDeltaBits( int nEntityIndex, int nDeltaTick, int &nBits )
  179. {
  180. nBits = -1;
  181. if ( nEntityIndex < 0 || nEntityIndex >= m_nMaxEntities )
  182. return NULL;
  183. DeltaEntityEntry_s *pEntry = m_Cache[nEntityIndex];
  184. while ( pEntry )
  185. {
  186. if ( pEntry->nDeltaTick == nDeltaTick )
  187. {
  188. nBits = pEntry->nBits;
  189. return (unsigned char*)(pEntry) + sizeof(DeltaEntityEntry_s);
  190. }
  191. else
  192. {
  193. // keep searching entry list
  194. pEntry = pEntry->pNext;
  195. }
  196. }
  197. return NULL;
  198. }
  199. void CDeltaEntityCache::AddDeltaBits( int nEntityIndex, int nDeltaTick, int nBits, bf_write *pBuffer )
  200. {
  201. if ( nEntityIndex < 0 || nEntityIndex >= m_nMaxEntities || m_nCacheSize <= 0 )
  202. return;
  203. int nBufferSize = PAD_NUMBER( Bits2Bytes(nBits), 4);
  204. DeltaEntityEntry_s *pEntry = m_Cache[nEntityIndex];
  205. if ( pEntry == NULL )
  206. {
  207. if ( (int)(nBufferSize+sizeof(DeltaEntityEntry_s)) > m_nCacheSize )
  208. return; // way too big, don't even create an entry
  209. pEntry = m_Cache[nEntityIndex] = (DeltaEntityEntry_s *) malloc( m_nCacheSize );
  210. }
  211. else
  212. {
  213. char *pEnd = (char*)(pEntry) + m_nCacheSize; // end marker
  214. while( pEntry->pNext )
  215. {
  216. pEntry = pEntry->pNext;
  217. }
  218. int entrySize = sizeof(DeltaEntityEntry_s) + PAD_NUMBER( Bits2Bytes(pEntry->nBits), 4);
  219. DeltaEntityEntry_s *pNew = (DeltaEntityEntry_s*)((char*)(pEntry) + entrySize);
  220. if ( ((char*)(pNew) + sizeof(DeltaEntityEntry_s) + nBufferSize) > pEnd )
  221. return; // data wouldn't fit into cache anymore, don't add new entries
  222. pEntry->pNext = pEntry = pNew;
  223. }
  224. pEntry->pNext = NULL; // link to next
  225. pEntry->nDeltaTick = nDeltaTick;
  226. pEntry->nBits = nBits;
  227. if ( nBits > 0 )
  228. {
  229. bf_read inBuffer;
  230. inBuffer.StartReading( pBuffer->GetData(), pBuffer->m_nDataBytes, pBuffer->GetNumBitsWritten() );
  231. bf_write outBuffer( (char*)(pEntry) + sizeof(DeltaEntityEntry_s), nBufferSize );
  232. outBuffer.WriteBitsFromBuffer( &inBuffer, nBits );
  233. }
  234. }
  235. static RecvTable* FindRecvTable( const char *pName, RecvTable **pRecvTables, int nRecvTables )
  236. {
  237. for ( int i=0; i< nRecvTables; i++ )
  238. {
  239. if ( !Q_strcmp( pName, pRecvTables[i]->GetName() ) )
  240. return pRecvTables[i];
  241. }
  242. return NULL;
  243. }
  244. static RecvTable* AddRecvTableR( SendTable *sendt, RecvTable **pRecvTables, int &nRecvTables )
  245. {
  246. RecvTable *recvt = FindRecvTable( sendt->m_pNetTableName, pRecvTables, nRecvTables );
  247. if ( recvt )
  248. return recvt; // already in list
  249. if ( sendt->m_nProps > 0 )
  250. {
  251. RecvProp *receiveProps = new RecvProp[sendt->m_nProps];
  252. for ( int i=0; i < sendt->m_nProps; i++ )
  253. {
  254. // copy property data
  255. SendProp * sp = sendt->GetProp( i );
  256. RecvProp * rp = &receiveProps[i];
  257. rp->m_pVarName = sp->m_pVarName;
  258. rp->m_RecvType = sp->m_Type;
  259. if ( sp->IsExcludeProp() )
  260. {
  261. // if prop is excluded, give different name
  262. rp->m_pVarName = "IsExcludedProp";
  263. }
  264. if ( sp->IsInsideArray() )
  265. {
  266. rp->SetInsideArray();
  267. rp->m_pVarName = "InsideArrayProp"; // give different name
  268. }
  269. if ( sp->GetType() == DPT_Array )
  270. {
  271. Assert ( sp->GetArrayProp() == sendt->GetProp( i-1 ) );
  272. Assert( receiveProps[i-1].IsInsideArray() );
  273. rp->SetArrayProp( &receiveProps[i-1] );
  274. rp->InitArray( sp->m_nElements, sp->m_ElementStride );
  275. }
  276. if ( sp->GetType() == DPT_DataTable )
  277. {
  278. // recursive create
  279. Assert ( sp->GetDataTable() );
  280. RecvTable *subTable = AddRecvTableR( sp->GetDataTable(), pRecvTables, nRecvTables );
  281. rp->SetDataTable( subTable );
  282. }
  283. }
  284. recvt = new RecvTable( receiveProps, sendt->m_nProps, sendt->m_pNetTableName );
  285. }
  286. else
  287. {
  288. // table with no properties
  289. recvt = new RecvTable( NULL, 0, sendt->m_pNetTableName );
  290. }
  291. pRecvTables[nRecvTables] = recvt;
  292. nRecvTables++;
  293. return recvt;
  294. }
  295. void CHLTVServer::FreeClientRecvTables()
  296. {
  297. for ( int i=0; i< m_nRecvTables; i++ )
  298. {
  299. RecvTable *rt = m_pRecvTables[i];
  300. // delete recv table props
  301. if ( rt->m_pProps )
  302. {
  303. Assert( rt->m_nProps > 0 );
  304. delete [] rt->m_pProps;
  305. }
  306. // delete the table itself
  307. delete rt;
  308. }
  309. Q_memset( m_pRecvTables, 0, sizeof( m_pRecvTables ) );
  310. m_nRecvTables = 0;
  311. }
  312. // creates client receive tables from server send tables
  313. void CHLTVServer::InitClientRecvTables()
  314. {
  315. ServerClass* pCur = NULL;
  316. if ( ClientDLL_GetAllClasses() != NULL )
  317. return; //already initialized
  318. // first create all SendTables
  319. for ( pCur = serverGameDLL->GetAllServerClasses(); pCur; pCur=pCur->m_pNext )
  320. {
  321. // create receive table from send table.
  322. AddRecvTableR( pCur->m_pTable, m_pRecvTables, m_nRecvTables );
  323. ErrorIfNot(
  324. m_nRecvTables < ARRAYSIZE( m_pRecvTables ),
  325. ("AddRecvTableR: overflowed MAX_DATATABLES")
  326. );
  327. }
  328. // now register client classes
  329. for ( pCur = serverGameDLL->GetAllServerClasses(); pCur; pCur=pCur->m_pNext )
  330. {
  331. ErrorIfNot(
  332. m_nRecvTables < ARRAYSIZE( m_pRecvTables ),
  333. ("ClientDLL_InitRecvTableMgr: overflowed MAX_DATATABLES")
  334. );
  335. // find top receive table for class
  336. RecvTable * recvt = FindRecvTable( pCur->m_pTable->GetName(), m_pRecvTables, m_nRecvTables );
  337. Assert ( recvt );
  338. // register class, constructor addes clientClass to g_pClientClassHead list
  339. ClientClass * clientclass = new ClientClass( pCur->m_pNetworkName, NULL, NULL, recvt );
  340. if ( !clientclass )
  341. {
  342. Msg("HLTV_InitRecvTableMgr: failed to allocate client class %s.\n", pCur->m_pNetworkName );
  343. return;
  344. }
  345. }
  346. RecvTable_Init( m_pRecvTables, m_nRecvTables );
  347. }
  348. CHLTVFrame::CHLTVFrame()
  349. {
  350. }
  351. CHLTVFrame::~CHLTVFrame()
  352. {
  353. FreeBuffers();
  354. }
  355. void CHLTVFrame::Reset( void )
  356. {
  357. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  358. {
  359. m_Messages[i].Reset();
  360. }
  361. }
  362. bool CHLTVFrame::HasData( void )
  363. {
  364. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  365. {
  366. if ( m_Messages[i].GetNumBitsWritten() > 0 )
  367. return true;
  368. }
  369. return false;
  370. }
  371. void CHLTVFrame::CopyHLTVData( const CHLTVFrame &frame )
  372. {
  373. // copy reliable messages
  374. int bits = frame.m_Messages[HLTV_BUFFER_RELIABLE].GetNumBitsWritten();
  375. if ( bits > 0 )
  376. {
  377. int bytes = PAD_NUMBER( Bits2Bytes(bits), 4 );
  378. m_Messages[HLTV_BUFFER_RELIABLE].StartWriting( new char[ bytes ], bytes, bits );
  379. Q_memcpy( m_Messages[HLTV_BUFFER_RELIABLE].GetData(), frame.m_Messages[HLTV_BUFFER_RELIABLE].GetData(), bytes );
  380. }
  381. // copy unreliable messages
  382. bits = frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetNumBitsWritten();
  383. bits += frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetNumBitsWritten();
  384. bits += frame.m_Messages[HLTV_BUFFER_SOUNDS].GetNumBitsWritten();
  385. if ( bits > 0 )
  386. {
  387. // collapse all unreliable buffers in one
  388. int bytes = PAD_NUMBER( Bits2Bytes(bits), 4 );
  389. m_Messages[HLTV_BUFFER_UNRELIABLE].StartWriting( new char[ bytes ], bytes );
  390. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetData(), frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetNumBitsWritten() );
  391. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetData(), frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetNumBitsWritten() );
  392. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_SOUNDS].GetData(), frame.m_Messages[HLTV_BUFFER_SOUNDS].GetNumBitsWritten() );
  393. }
  394. if ( tv_relayvoice.GetBool() )
  395. {
  396. int nVoiceBits = frame.m_Messages[ HLTV_BUFFER_VOICE ].GetNumBitsWritten();
  397. if ( nVoiceBits > 0 )
  398. {
  399. int nVoiceBytes = PAD_NUMBER( Bits2Bytes( nVoiceBits ), 4 );
  400. m_Messages[ HLTV_BUFFER_VOICE ].StartWriting( new char[ nVoiceBytes ], nVoiceBytes );
  401. m_Messages[ HLTV_BUFFER_VOICE ].WriteBits( frame.m_Messages[ HLTV_BUFFER_VOICE ].GetData(), frame.m_Messages[ HLTV_BUFFER_VOICE ].GetNumBitsWritten() );
  402. }
  403. }
  404. }
  405. uint CHLTVFrame::GetMemSize()const
  406. {
  407. uint nSize = sizeof( *this );
  408. for ( int i = 0; i < ARRAYSIZE( m_Messages ); ++i )
  409. nSize += m_Messages[ i ].GetMaxNumBits() / 8;
  410. return nSize;
  411. }
  412. void CHLTVFrame::AllocBuffers( void )
  413. {
  414. // allocate buffers for input frame
  415. for ( int i=0; i < HLTV_BUFFER_MAX; i++ )
  416. {
  417. Assert( m_Messages[i].GetBasePointer() == NULL );
  418. m_Messages[i].StartWriting( new char[NET_MAX_PAYLOAD], NET_MAX_PAYLOAD);
  419. }
  420. }
  421. void CHLTVFrame::FreeBuffers( void )
  422. {
  423. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  424. {
  425. bf_write &msg = m_Messages[i];
  426. if ( msg.GetBasePointer() )
  427. {
  428. delete[] msg.GetBasePointer();
  429. msg.StartWriting( NULL, 0 );
  430. }
  431. }
  432. }
  433. CHLTVServer::CHLTVServer( uint nInstanceIndex, float flSnapshotRate )
  434. : m_DemoRecorder( this )
  435. , m_Broadcast( this )
  436. , m_ClientState( this )
  437. , m_nInstanceIndex( nInstanceIndex )
  438. , m_flSnapshotRate( flSnapshotRate )
  439. {
  440. m_flTickInterval = 0.03;
  441. m_MasterClient = NULL;
  442. m_Server = NULL;
  443. m_Director = NULL;
  444. m_nFirstTick = -1;
  445. m_nLastTick = 0;
  446. m_CurrentFrame = NULL;
  447. m_nViewEntity = 0;
  448. m_nPlayerSlot = 0;
  449. m_bSignonState = false;
  450. m_flStartTime = 0;
  451. m_flFPS = 0;
  452. m_nGameServerMaxClients = 0;
  453. m_fNextSendUpdateTime = 0;
  454. Q_memset( m_pRecvTables, 0, sizeof( m_pRecvTables ) );
  455. m_nRecvTables = 0;
  456. m_vPVSOrigin.Init();
  457. m_nStartTick = 0;
  458. m_bPlayingBack = false;
  459. m_bPlaybackPaused = false;
  460. m_flPlaybackRateModifier = 0;
  461. m_nSkipToTick = 0;
  462. m_bMasterOnlyMode = false;
  463. Assert( m_ClientState.m_pHLTV == this ); // constructor of ClientState should've set thie HLTV.
  464. m_nGlobalSlots = 0;
  465. m_nGlobalClients = 0;
  466. m_nGlobalProxies = 0;
  467. m_nExternalTotalViewers = 0;
  468. m_nExternalLinkedViewers = 0;
  469. m_nDebugID = EVENT_DEBUG_ID_INIT;
  470. m_pOldestDeltaFrame = NULL;
  471. m_pNewestDeltaFrame = NULL;
  472. m_pLastSourceSnapshot = NULL;
  473. m_pLastTargetSnapshot = NULL;
  474. }
  475. CHLTVServer::~CHLTVServer()
  476. {
  477. m_nDebugID = EVENT_DEBUG_ID_SHUTDOWN;
  478. if ( m_nRecvTables > 0 )
  479. {
  480. RecvTable_Term();
  481. FreeClientRecvTables();
  482. }
  483. // make sure everything was destroyed
  484. Assert( m_CurrentFrame == NULL );
  485. Assert( CountClientFrames() == 0 );
  486. }
  487. void CHLTVServer::SetMaxClients( int number )
  488. {
  489. // allow max clients 0 in HLTV
  490. m_nMaxclients = clamp( number, 0, ABSOLUTE_PLAYER_LIMIT );
  491. }
  492. void CHLTVServer::StartMaster(CGameClient *client)
  493. {
  494. m_nExternalTotalViewers = 0;
  495. m_nExternalLinkedViewers = 0;
  496. Clear(); // clear old settings & buffers
  497. if ( !client )
  498. {
  499. ConMsg("GOTV client not found.\n");
  500. return;
  501. }
  502. m_Director = serverGameDirector;
  503. if ( !m_Director )
  504. {
  505. ConMsg("Mod doesn't support GOTV. No director module found.\n");
  506. return;
  507. }
  508. m_MasterClient = client;
  509. m_MasterClient->m_bIsHLTV = true;
  510. m_MasterClient->m_pHltvSlaveServer = this; // Master client needs to know which server (with which tickrate) it's sending packets
  511. #if defined( REPLAY_ENABLED )
  512. m_MasterClient->m_bIsReplay = false;
  513. #endif
  514. // let game.dll know that we are the HLTV client
  515. Assert( serverGameClients );
  516. CPlayerState *player = serverGameClients->GetPlayerState( m_MasterClient->edict );
  517. player->hltv = true;
  518. m_Server = (CGameServer*)m_MasterClient->GetServer();
  519. // set default user settings
  520. m_MasterClient->m_ConVars->SetString( "name", tv_name.GetString() );
  521. m_MasterClient->m_ConVars->SetString( "cl_team", "1" );
  522. m_MasterClient->m_ConVars->SetString( "rate", va( "%d", DEFAULT_RATE ) );
  523. m_MasterClient->m_ConVars->SetString( "cl_updaterate", va( "%f", GetSnapshotRate() ) ); // this may not be necessary...
  524. m_MasterClient->m_ConVars->SetString( "cl_cmdrate", "64" );
  525. m_MasterClient->m_ConVars->SetString( "cl_interp_ratio", "1.0" );
  526. m_MasterClient->m_ConVars->SetString( "cl_predict", "0" );
  527. m_nViewEntity = m_MasterClient->GetPlayerSlot() + 1;
  528. m_nPlayerSlot = m_MasterClient->GetPlayerSlot();
  529. // copy server settings from m_Server
  530. m_nGameServerMaxClients = m_Server->GetMaxClients(); // maxclients is different on proxy (128)
  531. serverclasses = m_Server->serverclasses;
  532. serverclassbits = m_Server->serverclassbits;
  533. worldmapCRC = m_Server->worldmapCRC;
  534. clientDllCRC = m_Server->clientDllCRC;
  535. m_flTickInterval= m_Server->GetTickInterval();
  536. // allocate buffers for input frame
  537. m_HLTVFrame.AllocBuffers();
  538. InstallStringTables();
  539. // activate director in game.dll
  540. m_Director->AddHLTVServer( this ); // we RemoveHLTVServer later instead of setting it to NULL now
  541. // register as listener for mod specific events
  542. const char **modevents = m_Director->GetModEvents();
  543. int j = 0;
  544. while ( modevents[j] != NULL )
  545. {
  546. const char *eventname = modevents[j];
  547. CGameEventDescriptor *descriptor = g_GameEventManager.GetEventDescriptor( eventname );
  548. if ( descriptor )
  549. {
  550. g_GameEventManager.AddListener( this, descriptor, CGameEventManager::CLIENTSTUB );
  551. }
  552. else
  553. {
  554. DevMsg("CHLTVServer::StartMaster: game event %s not found.\n", eventname );
  555. }
  556. j++;
  557. }
  558. // copy signon buffers
  559. m_Signon.StartWriting( m_Server->m_Signon.GetBasePointer(), m_Server->m_Signon.m_nDataBytes,
  560. m_Server->m_Signon.GetNumBitsWritten() );
  561. Q_strncpy( m_szMapname, m_Server->m_szMapname, sizeof(m_szMapname) );
  562. Q_strncpy( m_szSkyname, m_Server->m_szSkyname, sizeof(m_szSkyname) );
  563. NET_ListenSocket( m_Socket, true ); // activated HLTV TCP socket
  564. m_MasterClient->ExecuteStringCommand( "spectate" ); // become a spectator
  565. m_MasterClient->UpdateUserSettings(); // make sure UserInfo is correct
  566. // hack reduce signontick by one to catch changes made in the current tick
  567. m_MasterClient->m_nSignonTick--;
  568. if ( m_bMasterOnlyMode )
  569. {
  570. // we allow only one client in master only mode
  571. tv_maxclients.SetValue( MIN(1,tv_maxclients.GetInt()) );
  572. }
  573. SetMaxClients( tv_maxclients.GetInt() );
  574. m_bSignonState = false; //master proxy is instantly connected
  575. m_nSpawnCount++;
  576. m_flStartTime = net_time;
  577. m_State = ss_active;
  578. // stop any previous recordings
  579. StopRecording();
  580. // start new recording if autorecord is enabled
  581. if ( tv_autorecord.GetBool() )
  582. {
  583. m_DemoRecorder.StartAutoRecording();
  584. }
  585. m_Broadcast.OnMasterStarted();
  586. if ( GetIndexedConVar( tv_broadcast, GetInstanceIndex() ).GetBool() )
  587. {
  588. StartBroadcast();
  589. }
  590. m_DemoEventWriteBuffer.StartWriting( m_DemoEventsBuffer, ARRAYSIZE( m_DemoEventsBuffer) );
  591. ReconnectClients();
  592. }
  593. void CHLTVServer::StartBroadcast()
  594. {
  595. m_Broadcast.StartRecording( tv_broadcast_url.GetString() );
  596. }
  597. void CHLTVServer::StartDemo( const char *filename )
  598. {
  599. }
  600. bool CHLTVServer::DispatchToRelay( CHLTVClient *pClient )
  601. {
  602. if ( tv_dispatchmode.GetInt() <= DISPATCH_MODE_OFF )
  603. return false; // don't redirect
  604. CBaseClient *pBestProxy = NULL;
  605. float fBestRatio = 1.0f;
  606. // find best relay proxy
  607. for ( int i = 0; i < GetClientCount(); i++ )
  608. {
  609. CBaseClient *pProxy = m_Clients[ i ];
  610. // check all known proxies
  611. if ( !pProxy->IsConnected() || !pProxy->IsHLTV() || (pClient == pProxy) )
  612. continue;
  613. int slots = Q_atoi( pProxy->GetUserSetting( "hltv_slots" ) );
  614. int clients = Q_atoi( pProxy->GetUserSetting( "hltv_clients" ) );
  615. // skip overloaded proxies or proxies with no slots at all
  616. if ( (clients > slots) || slots <= 0 )
  617. continue;
  618. // calc clients/slots ratio for this proxy
  619. float ratio = ((float)(clients))/((float)slots);
  620. if ( ratio < fBestRatio )
  621. {
  622. fBestRatio = ratio;
  623. pBestProxy = pProxy;
  624. }
  625. }
  626. if ( pBestProxy == NULL )
  627. {
  628. if ( tv_dispatchmode.GetInt() == DISPATCH_MODE_ALWAYS )
  629. {
  630. // we are in always forward mode, drop client if we can't forward it
  631. pClient->Disconnect("No GOTV relay available");
  632. return true;
  633. }
  634. else
  635. {
  636. // just let client connect to this proxy
  637. return false;
  638. }
  639. }
  640. // check if client should stay on this relay server unless we are the master,
  641. // masters always prefer to send clients to relays
  642. if ( (tv_dispatchmode.GetInt() == DISPATCH_MODE_AUTO) && (GetMaxClients() > 0) )
  643. {
  644. // ratio = clients/slots. give relay proxies 25% bonus
  645. int numSlots = GetMaxClients();
  646. if ( tv_maxclients_relayreserved.GetInt() > 0 )
  647. numSlots -= tv_maxclients_relayreserved.GetInt();
  648. numSlots = MAX( 0, numSlots );
  649. int numClients = GetNumClients();
  650. if ( numClients > numSlots )
  651. numSlots = numClients;
  652. float flDispatchWeight = tv_dispatchweight.GetFloat();
  653. if ( flDispatchWeight <= 1.01 )
  654. flDispatchWeight = 1.01;
  655. float myRatio = ((float)numClients/(float)numSlots) * flDispatchWeight;
  656. myRatio = MIN( myRatio, 1.0f ); // clamp to 1
  657. // if we have a better local ratio then other proxies, keep this client here
  658. if ( myRatio < fBestRatio )
  659. return false; // don't redirect
  660. }
  661. const char *pszRelayAddr = pBestProxy->GetUserSetting("hltv_addr");
  662. if ( !pszRelayAddr )
  663. return false;
  664. ConMsg( "Redirecting spectator %s to GOTV relay %s\n",
  665. pClient->GetNetChannel()->GetAddress(),
  666. pszRelayAddr );
  667. // first tell that client that we are a SourceTV server,
  668. // otherwise it's might ignore the "connect" command
  669. CSVCMsg_ServerInfo_t serverInfo;
  670. FillServerInfo( serverInfo );
  671. serverInfo.set_is_redirecting_to_proxy_relay( true );
  672. pClient->SendNetMsg( serverInfo, true );
  673. // tell the client to connect to this new address
  674. CNETMsg_StringCmd_t cmdMsg( va("connect %s\n", pszRelayAddr ) ) ;
  675. pClient->SendNetMsg( cmdMsg, true );
  676. // increase this proxies client number in advance so this proxy isn't used again next time
  677. int clients = Q_atoi( pBestProxy->GetUserSetting( "hltv_clients" ) );
  678. pBestProxy->SetUserCVar( "hltv_clients", va("%d", clients+1 ) );
  679. return true;
  680. }
  681. void CHLTVServer::ConnectRelay(const char *address)
  682. {
  683. m_nExternalTotalViewers = 0;
  684. m_nExternalLinkedViewers = 0;
  685. if ( m_ClientState.IsConnected() )
  686. {
  687. // do not try to reconnect to old connection
  688. m_ClientState.m_Remote.RemoveAll();
  689. // disconnect first
  690. m_ClientState.Disconnect();
  691. Changelevel( true ); // inactivate clients
  692. }
  693. // connect to new server
  694. m_ClientState.Connect( address, address, "HLTVConnectRelay" );
  695. }
  696. void CHLTVServer::StartRelay()
  697. {
  698. if ( !m_ClientState.IsConnected() && !IsPlayingBack() )
  699. {
  700. DevMsg("StartRelay: not connected.\n");
  701. Shutdown();
  702. return;
  703. }
  704. Clear(); // clear old settings & buffers
  705. if ( m_nRecvTables == 0 )
  706. {
  707. // must be done only once since Mod never changes
  708. InitClientRecvTables();
  709. }
  710. m_HLTVFrame.AllocBuffers();
  711. m_StringTables = &m_NetworkStringTables;
  712. SetMaxClients( tv_maxclients.GetInt() );
  713. m_bSignonState = true;
  714. m_flStartTime = net_time;
  715. m_State = ss_loading;
  716. m_nSpawnCount++;
  717. }
  718. int CHLTVServer::GetHLTVSlot( void )
  719. {
  720. return m_nPlayerSlot;
  721. }
  722. float CHLTVServer::GetOnlineTime( void )
  723. {
  724. return MAX(0, net_time - m_flStartTime);
  725. }
  726. void CHLTVServer::GetLocalStats( int &proxies, int &slots, int &clients )
  727. {
  728. int numSlots = GetMaxClients();
  729. if ( tv_maxclients_relayreserved.GetInt() > 0 )
  730. numSlots -= tv_maxclients_relayreserved.GetInt();
  731. numSlots = MAX( 0, numSlots );
  732. int numClients = GetNumClients();
  733. if ( numClients > numSlots )
  734. numSlots = numClients;
  735. proxies = GetNumProxies();
  736. clients = numClients;
  737. slots = numSlots;
  738. }
  739. void CHLTVServer::GetRelayStats( int &proxies, int &slots, int &clients )
  740. {
  741. proxies = slots = clients = 0;
  742. for (int i=0 ; i < GetClientCount() ; i++ )
  743. {
  744. CBaseClient *client = m_Clients[ i ];
  745. if ( !client->IsConnected() || !client->IsHLTV() )
  746. continue;
  747. proxies += Q_atoi( client->GetUserSetting( "hltv_proxies" ) );
  748. slots += Q_atoi( client->GetUserSetting( "hltv_slots" ) );
  749. clients += Q_atoi( client->GetUserSetting( "hltv_clients" ) );
  750. }
  751. }
  752. void CHLTVServer::GetGlobalStats( int &proxies, int &slots, int &clients )
  753. {
  754. // the master proxy is the only one that really has all data to generate
  755. // global stats
  756. if ( IsMasterProxy() )
  757. {
  758. GetRelayStats( m_nGlobalProxies, m_nGlobalSlots, m_nGlobalClients );
  759. int numSlots = GetMaxClients();
  760. if ( tv_maxclients_relayreserved.GetInt() > 0 )
  761. numSlots -= tv_maxclients_relayreserved.GetInt();
  762. numSlots = MAX( 0, numSlots );
  763. int numClients = GetNumClients();
  764. if ( !numClients && m_Broadcast.IsRecording() )
  765. { // Always consider broadcast stream as a non-zero amount of clients
  766. numClients = 1;
  767. }
  768. if ( numClients > numSlots )
  769. numSlots = numClients;
  770. m_nGlobalSlots += numSlots;
  771. m_nGlobalClients += numClients;
  772. }
  773. // if this is a relay proxies, global data comes via the
  774. // wire from the master proxy
  775. proxies = m_nGlobalProxies;
  776. slots = m_nGlobalSlots;
  777. clients = m_nGlobalClients;
  778. }
  779. void CHLTVServer::GetExternalStats( int &numExternalTotalViewers, int &numExternalLinkedViewers )
  780. {
  781. numExternalTotalViewers = m_nExternalTotalViewers;
  782. numExternalLinkedViewers = m_nExternalLinkedViewers;
  783. }
  784. void CHLTVServer::UpdateHltvExternalViewers( uint32 numTotalViewers, uint32 numLinkedViewers )
  785. {
  786. if ( !IsMasterProxy() )
  787. return;
  788. if ( !IsActive() )
  789. return;
  790. m_nExternalTotalViewers = numTotalViewers;
  791. m_nExternalLinkedViewers = numLinkedViewers;
  792. }
  793. const netadr_t *CHLTVServer::GetRelayAddress( void )
  794. {
  795. if ( IsMasterProxy() )
  796. {
  797. return &net_local_adr; // TODO wrong port
  798. }
  799. else if ( m_ClientState.m_NetChannel )
  800. {
  801. const ns_address &adr = m_ClientState.m_NetChannel->GetRemoteAddress();
  802. if ( adr.IsType<netadr_t>() )
  803. return &adr.AsType<netadr_t>();
  804. }
  805. return NULL;
  806. }
  807. bool CHLTVServer::IsMasterProxy( void )
  808. {
  809. return ( m_MasterClient != NULL );
  810. }
  811. bool CHLTVServer::IsTVRelay()
  812. {
  813. return !IsMasterProxy();
  814. }
  815. bool CHLTVServer::IsDemoPlayback( void )
  816. {
  817. return false;
  818. }
  819. void CHLTVServer::BroadcastLocalTitle( CHLTVClient *client )
  820. {
  821. IGameEvent *event = g_GameEventManager.CreateEvent( "hltv_title", true );
  822. if ( !event )
  823. return;
  824. event->SetString( "text", tv_title.GetString() );
  825. CSVCMsg_GameEvent_t eventMsg;
  826. eventMsg.SetReliable( true );
  827. // create bit stream from KeyValues
  828. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg ) )
  829. {
  830. DevMsg("CHLTVServer: failed to serialize title '%s'.\n", event->GetName() );
  831. g_GameEventManager.FreeEvent( event );
  832. return;
  833. }
  834. if ( client )
  835. {
  836. client->SendNetMsg( eventMsg );
  837. }
  838. else
  839. {
  840. for ( int i = 0; i < m_Clients.Count(); i++ )
  841. {
  842. client = Client(i);
  843. if ( !client->IsActive() || client->IsHLTV() )
  844. continue;
  845. client->SendNetMsg( eventMsg );
  846. }
  847. }
  848. g_GameEventManager.FreeEvent( event );
  849. }
  850. void CHLTVServer::BroadcastLocalChat( const char *pszChat, const char *pszGroup )
  851. {
  852. IGameEvent *event = g_GameEventManager.CreateEvent( "hltv_chat", true );
  853. if ( !event )
  854. return;
  855. event->SetString( "text", pszChat );
  856. CSVCMsg_GameEvent_t eventMsg;
  857. eventMsg.SetReliable( false );
  858. // create bit stream from KeyValues
  859. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg ) )
  860. {
  861. DevMsg("CHLTVServer: failed to serialize chat '%s'.\n", event->GetName() );
  862. g_GameEventManager.FreeEvent( event );
  863. return;
  864. }
  865. for ( int i = 0; i < m_Clients.Count(); i++ )
  866. {
  867. CHLTVClient *cl = Client(i);
  868. if ( !cl->IsActive() || !cl->IsSpawned() || cl->IsHLTV() )
  869. continue;
  870. // if this is a spectator chat message and client disabled it, don't show it
  871. if ( Q_strcmp( cl->m_szChatGroup, pszGroup) || cl->m_bNoChat )
  872. continue;
  873. cl->SendNetMsg( eventMsg );
  874. }
  875. g_GameEventManager.FreeEvent( event );
  876. }
  877. void CHLTVServer::BroadcastEventLocal( IGameEvent *event, bool bReliable )
  878. {
  879. CSVCMsg_GameEvent_t eventMsg;
  880. eventMsg.SetReliable( bReliable );
  881. // create bit stream from KeyValues
  882. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg ) )
  883. {
  884. DevMsg("CHLTVServer: failed to serialize local event '%s'.\n", event->GetName() );
  885. return;
  886. }
  887. for ( int i = 0; i < m_Clients.Count(); i++ )
  888. {
  889. CHLTVClient *cl = Client(i);
  890. if ( !cl->IsActive() || !cl->IsSpawned() || cl->IsHLTV() )
  891. continue;
  892. if ( !cl->SendNetMsg( eventMsg ) )
  893. {
  894. if ( eventMsg.IsReliable() )
  895. {
  896. DevMsg( "BroadcastMessage: Reliable broadcast message overflow for client %s", cl->GetClientName() );
  897. }
  898. }
  899. }
  900. if ( tv_debug.GetBool() )
  901. Msg("SourceTV broadcast local event: %s\n", event->GetName() );
  902. }
  903. void CHLTVServer::BroadcastEvent(IGameEvent *event)
  904. {
  905. CSVCMsg_GameEvent_t eventMsg;
  906. // create bit stream from KeyValues
  907. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg ) )
  908. {
  909. DevMsg("CHLTVServer: failed to serialize event '%s'.\n", event->GetName() );
  910. return;
  911. }
  912. BroadcastMessage( eventMsg, true, true );
  913. if ( m_DemoRecorder.IsRecording() || m_Broadcast.IsRecording() )
  914. {
  915. eventMsg.WriteToBuffer( m_DemoEventWriteBuffer );
  916. }
  917. if ( tv_debug.GetBool() )
  918. Msg("SourceTV broadcast event: %s\n", event->GetName() );
  919. }
  920. bool CHLTVServer::IsRecording()
  921. {
  922. return m_DemoRecorder.IsRecording();
  923. }
  924. const char* CHLTVServer::GetRecordingDemoFilename()
  925. {
  926. return m_DemoRecorder.GetDemoFilename();
  927. }
  928. void CHLTVServer::StartAutoRecording()
  929. {
  930. return m_DemoRecorder.StartAutoRecording();
  931. }
  932. void CHLTVServer::FireGameEvent(IGameEvent *event)
  933. {
  934. if ( !IsActive() )
  935. return;
  936. CSVCMsg_GameEvent_t eventMsg;
  937. // create bit stream from KeyValues
  938. if ( g_GameEventManager.SerializeEvent( event, &eventMsg ) )
  939. {
  940. SendNetMsg( eventMsg );
  941. }
  942. else
  943. {
  944. DevMsg("CHLTVServer::FireGameEvent: failed to serialize event '%s'.\n", event->GetName() );
  945. }
  946. }
  947. int CHLTVServer::GetEventDebugID( void )
  948. {
  949. return m_nDebugID;
  950. }
  951. //Whenever we're done recording a demo, go through all the remaining frames and write them too.
  952. //This is done because now the demo recorded is recuring the CURRENT frame instead of the broadcast frame
  953. void CHLTVServer::StopRecordingAndFreeFrames( bool bFreeFrames, const CGameInfo *pGameInfo)
  954. {
  955. // We are only stopping demo recorder. Broadcast does not stop on changelevel, for example. If broadcast is activated, it stays activated until stopped.
  956. if( !m_CurrentFrame || !m_DemoRecorder.IsRecording() )
  957. {
  958. //we aren't recording, just clean up if requested
  959. if( bFreeFrames )
  960. {
  961. DeleteClientFrames( -1 );
  962. FreeAllDeltaFrames();
  963. m_CurrentFrame = NULL;
  964. }
  965. return;
  966. }
  967. int nStartingTick = m_CurrentFrame->tick_count;
  968. CHLTVFrame *pFrame = static_cast< CHLTVFrame *> (m_CurrentFrame->m_pNext );
  969. //clear our current frame since we are flushing all of our frames
  970. if( bFreeFrames )
  971. m_CurrentFrame = NULL;
  972. //flush any remaining current frames that we have
  973. while ( pFrame )
  974. {
  975. // restore string tables for this time
  976. int nFrameTick = pFrame->tick_count;
  977. RestoreTick( pFrame->tick_count );
  978. m_DemoRecorder.WriteFrame( pFrame );
  979. pFrame = static_cast< CHLTVFrame *> (pFrame->m_pNext );
  980. //and clean up the memory for this frame if we need to
  981. if( bFreeFrames )
  982. DeleteClientFrames( nFrameTick );
  983. }
  984. //now we need to handle the delta frames, freeing as we go to avoid a memory explosion
  985. while( m_pOldestDeltaFrame )
  986. {
  987. CHLTVFrame* pExpandedFrame = m_pOldestDeltaFrame->m_pClientFrame;
  988. int nExpandFrameTick = pExpandedFrame->tick_count;
  989. ExpandDeltaFramesToTick( nExpandFrameTick );
  990. RestoreTick( nExpandFrameTick );
  991. m_DemoRecorder.WriteFrame( pExpandedFrame );
  992. //and clean up the memory for this frame if we need to
  993. if( bFreeFrames )
  994. DeleteClientFrames( nExpandFrameTick );
  995. }
  996. // restore our string tables to what they were to begin with
  997. RestoreTick( nStartingTick );
  998. return m_DemoRecorder.StopRecording( pGameInfo );
  999. }
  1000. bool CHLTVServer::ShouldUpdateMasterServer()
  1001. {
  1002. // If the main game server is active, then we let it update Steam with the server info.
  1003. return !sv.IsActive();
  1004. }
  1005. CBaseClient *CHLTVServer::CreateNewClient(int slot )
  1006. {
  1007. return new CHLTVClient( slot, this );
  1008. }
  1009. void CHLTVServer::InstallStringTables( void )
  1010. {
  1011. #ifndef SHARED_NET_STRING_TABLES
  1012. int numTables = m_Server->m_StringTables->GetNumTables();
  1013. m_StringTables = &m_NetworkStringTables;
  1014. Assert( m_StringTables->GetNumTables() == 0); // must be empty
  1015. m_StringTables->AllowCreation( true );
  1016. // master hltv needs to keep a list of changes for all table items
  1017. m_StringTables->EnableRollback( true );
  1018. for ( int i =0; i<numTables; i++)
  1019. {
  1020. // iterate through server tables
  1021. CNetworkStringTable *serverTable =
  1022. (CNetworkStringTable*)m_Server->m_StringTables->GetTable( i );
  1023. if ( !serverTable )
  1024. continue;
  1025. // get matching client table
  1026. CNetworkStringTable *hltvTable =
  1027. (CNetworkStringTable*)m_StringTables->CreateStringTable(
  1028. serverTable->GetTableName(),
  1029. serverTable->GetMaxStrings(),
  1030. serverTable->GetUserDataSize(),
  1031. serverTable->GetUserDataSizeBits(),
  1032. serverTable->IsUsingDictionary() ? NSF_DICTIONARY_ENABLED : NSF_NONE
  1033. );
  1034. if ( !hltvTable )
  1035. {
  1036. DevMsg("SV_InstallHLTVStringTableMirrors! Missing client table \"%s\".\n ", serverTable->GetTableName() );
  1037. continue;
  1038. }
  1039. // make hltv table an exact copy of server table
  1040. hltvTable->CopyStringTable( serverTable );
  1041. // link hltv table to server table
  1042. serverTable->SetMirrorTable( m_nInstanceIndex, hltvTable ); // there may be multiple mirror tables (up to the number of HLTV servers), it's easier to manage than removing the mirror tables from the correct places at the correct times
  1043. }
  1044. m_StringTables->AllowCreation( false );
  1045. #endif
  1046. }
  1047. void CHLTVServer::UninstallStringTables( void )
  1048. {
  1049. if ( !m_Server )
  1050. return;
  1051. int numTables = m_Server->m_StringTables->GetNumTables();
  1052. for ( int i = 0; i < numTables; i++ )
  1053. {
  1054. // iterate through server tables
  1055. CNetworkStringTable *serverTable =
  1056. ( CNetworkStringTable* )m_Server->m_StringTables->GetTable( i );
  1057. if ( !serverTable )
  1058. continue;
  1059. serverTable->SetMirrorTable( m_nInstanceIndex, NULL ); // alternatively, we could find serverTable again and remove mirror table by pointer. It would be a bit more fragile and slower.
  1060. }
  1061. }
  1062. void CHLTVServer::RestoreTick( int tick )
  1063. {
  1064. #ifndef SHARED_NET_STRING_TABLES
  1065. // only master proxy delays time
  1066. if ( !IsMasterProxy() )
  1067. return;
  1068. int numTables = m_StringTables->GetNumTables();
  1069. for ( int i =0; i<numTables; i++)
  1070. {
  1071. // iterate through server tables
  1072. CNetworkStringTable *pTable = (CNetworkStringTable*) m_StringTables->GetTable( i );
  1073. pTable->RestoreTick( tick );
  1074. }
  1075. #endif
  1076. }
  1077. void CHLTVServer::UserInfoChanged( int nClientIndex )
  1078. {
  1079. // don't change UserInfo table, it keeps the infos of the original players
  1080. }
  1081. bool CHLTVServer::GetClassBaseline( ServerClass *pClass, SerializedEntityHandle_t *pHandle)
  1082. {
  1083. // if we are the master proxy the game server has our baseline data
  1084. if ( m_Server )
  1085. {
  1086. return m_Server->GetClassBaseline( pClass, pHandle );
  1087. }
  1088. // otherwise we have it from the hltvclientstate
  1089. return m_ClientState.GetClassBaseline( pClass->m_ClassID, pHandle );
  1090. }
  1091. void CHLTVServer::LinkInstanceBaselines( void )
  1092. {
  1093. // Forces to update m_pInstanceBaselineTable.
  1094. AUTO_LOCK_FM( g_svInstanceBaselineMutex );
  1095. GetInstanceBaselineTable();
  1096. Assert( m_pInstanceBaselineTable );
  1097. // update all found server classes
  1098. for ( ServerClass *pClass = serverGameDLL->GetAllServerClasses(); pClass; pClass=pClass->m_pNext )
  1099. {
  1100. char idString[32];
  1101. Q_snprintf( idString, sizeof( idString ), "%d", pClass->m_ClassID );
  1102. // Ok, make a new instance baseline so they can reference it.
  1103. int index = m_pInstanceBaselineTable->FindStringIndex( idString );
  1104. if ( index != -1 )
  1105. {
  1106. pClass->m_InstanceBaselineIndex = index;
  1107. }
  1108. else
  1109. {
  1110. pClass->m_InstanceBaselineIndex = INVALID_STRING_INDEX;
  1111. }
  1112. }
  1113. }
  1114. /* CHLTVServer::GetOriginFromPackedEntity is such a bad, bad hack.
  1115. extern float DecodeFloat(SendProp const *pProp, bf_read *pIn);
  1116. Vector CHLTVServer::GetOriginFromPackedEntity(PackedEntity* pe)
  1117. {
  1118. Vector origin; origin.Init();
  1119. SendTable *pSendTable = pe->m_pSendTable;
  1120. // recursively go down until BaseEntity sendtable
  1121. while ( Q_strcmp( pSendTable->GetName(), "DT_BaseEntity") )
  1122. {
  1123. SendProp *pProp = pSendTable->GetProp( 0 ); // 0 = baseclass
  1124. pSendTable = pProp->GetDataTable();
  1125. }
  1126. for ( int i=0; i < pSendTable->GetNumProps(); i++ )
  1127. {
  1128. SendProp *pProp = pSendTable->GetProp( i );
  1129. if ( Q_strcmp( pProp->GetName(), "m_vecOrigin" ) == 0 )
  1130. {
  1131. Assert( pProp->GetType() == DPT_Vector );
  1132. bf_read buf( pe->LockData(), Bits2Bytes(pe->GetNumBits()), pProp->GetOffset() );
  1133. origin[0] = DecodeFloat(pProp, &buf);
  1134. origin[1] = DecodeFloat(pProp, &buf);
  1135. origin[2] = DecodeFloat(pProp, &buf);
  1136. break;
  1137. }
  1138. }
  1139. return origin;
  1140. } */
  1141. CHLTVEntityData *FindHLTVDataInSnapshot( CFrameSnapshot * pSnapshot, int iEntIndex )
  1142. {
  1143. int a = 0;
  1144. int z = pSnapshot->m_nValidEntities-1;
  1145. if ( iEntIndex < pSnapshot->m_pValidEntities[a] ||
  1146. iEntIndex > pSnapshot->m_pValidEntities[z] )
  1147. return NULL;
  1148. while ( a < z )
  1149. {
  1150. int m = (a+z)/2;
  1151. int index = pSnapshot->m_pValidEntities[m];
  1152. if ( index == iEntIndex )
  1153. return &pSnapshot->m_pHLTVEntityData[m];
  1154. if ( iEntIndex > index )
  1155. {
  1156. if ( pSnapshot->m_pValidEntities[z] == iEntIndex )
  1157. return &pSnapshot->m_pHLTVEntityData[z];
  1158. if ( a == m )
  1159. return NULL;
  1160. a = m;
  1161. }
  1162. else
  1163. {
  1164. if ( pSnapshot->m_pValidEntities[a] == iEntIndex )
  1165. return &pSnapshot->m_pHLTVEntityData[a];
  1166. if ( z == m )
  1167. return NULL;
  1168. z = m;
  1169. }
  1170. }
  1171. return NULL;
  1172. }
  1173. void CHLTVServer::EntityPVSCheck( CClientFrame *pFrame )
  1174. {
  1175. byte PVS[PAD_NUMBER( MAX_MAP_CLUSTERS,8 ) / 8];
  1176. int nPVSSize = (GetCollisionBSPData()->numclusters + 7) / 8;
  1177. // setup engine PVS
  1178. SV_ResetPVS( PVS, nPVSSize );
  1179. CFrameSnapshot * pSnapshot = pFrame->GetSnapshot();
  1180. Assert ( pSnapshot->m_pHLTVEntityData != NULL );
  1181. int nDirectorEntity = m_Director->GetPVSEntity();
  1182. if ( pSnapshot && nDirectorEntity > 0 )
  1183. {
  1184. CHLTVEntityData *pHLTVData = FindHLTVDataInSnapshot( pSnapshot, nDirectorEntity );
  1185. if ( pHLTVData )
  1186. {
  1187. m_vPVSOrigin.x = pHLTVData->origin[0];
  1188. m_vPVSOrigin.y = pHLTVData->origin[1];
  1189. m_vPVSOrigin.z = pHLTVData->origin[2];
  1190. }
  1191. }
  1192. else
  1193. {
  1194. m_vPVSOrigin = m_Director->GetPVSOrigin();
  1195. }
  1196. SV_AddOriginToPVS( m_vPVSOrigin );
  1197. // know remove all entities that aren't in PVS
  1198. int entindex = -1;
  1199. while ( true )
  1200. {
  1201. entindex = pFrame->transmit_entity.FindNextSetBit( entindex+1 );
  1202. if ( entindex < 0 )
  1203. break;
  1204. // is transmit_always is set -> no PVS check
  1205. if ( pFrame->transmit_always->Get(entindex) )
  1206. {
  1207. pFrame->last_entity = entindex;
  1208. continue;
  1209. }
  1210. CHLTVEntityData *pHLTVData = FindHLTVDataInSnapshot( pSnapshot, entindex );
  1211. if ( !pHLTVData )
  1212. continue;
  1213. unsigned int nNodeCluster = pHLTVData->m_nNodeCluster;
  1214. // check if node or cluster is in PVS
  1215. if ( nNodeCluster & (1<<31) )
  1216. {
  1217. // it's a node SLOW
  1218. nNodeCluster &= ~(1<<31);
  1219. if ( CM_HeadnodeVisible( nNodeCluster, PVS, nPVSSize ) )
  1220. {
  1221. pFrame->last_entity = entindex;
  1222. continue;
  1223. }
  1224. }
  1225. else
  1226. {
  1227. // it's a cluster QUICK
  1228. if ( PVS[nNodeCluster >> 3] & (1 << (nNodeCluster & 7)) )
  1229. {
  1230. pFrame->last_entity = entindex;
  1231. continue;
  1232. }
  1233. }
  1234. // entity is not in PVS, remove from transmit_entity list
  1235. pFrame->transmit_entity.Clear( entindex );
  1236. }
  1237. }
  1238. void CHLTVServer::SignonComplete()
  1239. {
  1240. Assert ( !IsMasterProxy() );
  1241. m_bSignonState = false;
  1242. LinkInstanceBaselines();
  1243. if ( tv_debug.GetBool() )
  1244. Msg("GOTV signon complete.\n" );
  1245. }
  1246. CFrameSnapshot* CHLTVServer::CloneDeltaSnapshot( const CFrameSnapshot *pCopySnapshot )
  1247. {
  1248. //and create a new snapshot for this to reference, of which will not have entity data
  1249. CFrameSnapshot* pNewSnapshot = framesnapshotmanager->CreateEmptySnapshot(
  1250. #ifdef DEBUG_SNAPSHOT_REFERENCES
  1251. "CHLTVServer::CloneDeltaSnapshot",
  1252. #endif
  1253. pCopySnapshot->m_nTickCount, 0, knHLTVSnapshotSet );
  1254. //copy over snapshot data...
  1255. pNewSnapshot->m_iExplicitDeleteSlots = pCopySnapshot->m_iExplicitDeleteSlots;
  1256. pNewSnapshot->m_nTickCount = pCopySnapshot->m_nTickCount;
  1257. //note that we do not copy over the valid entities list. We only copy over info for valid entities, so it is already properly collapsed, and we can save memory by ignoring this
  1258. pNewSnapshot->m_pValidEntities = NULL;
  1259. pNewSnapshot->m_nValidEntities = 0;
  1260. //we don't need to copy over the events. The HLTV frame will actually copy over all the bits from the frame for the temporary entities, so we do not need to serialize this
  1261. pNewSnapshot->m_nTempEntities = 0;
  1262. pNewSnapshot->m_pTempEntities = NULL;
  1263. return pNewSnapshot;
  1264. }
  1265. //the bit buffer assumes that all buffers it reads/writes are 4 byte aligned, which the serialized entity enforces, so this function given a number of bits, will ensure that the proper size
  1266. //in bytes for the buffer is met
  1267. static int GetBitBufBytes( int nNumBits )
  1268. {
  1269. return ( ( nNumBits + 31 ) / 32) * 4;
  1270. }
  1271. //given a tick that the time changed on, and the current value along with the previous properties, this will determine which properties
  1272. //have changed, and create a serialized entity handle that contains just the delta properties
  1273. static SerializedEntityHandle_t CreateDeltaProperties( int nTick, const PackedEntity* pCurrPacked, const PackedEntity *pDeltaBase )
  1274. {
  1275. //the actual max properties is HUGE, and we only need deltas, so to avoid blowing up the stack, we use a smaller buffer size
  1276. static const int knMaxChangedProps = 4 * 1024;
  1277. uint16 nChangedPropIndices[ knMaxChangedProps ];
  1278. //get our source serialized entity data
  1279. const CSerializedEntity* pCurrProps = ( const CSerializedEntity* )pCurrPacked->GetPackedData();
  1280. //if we have no delta base to encode from, we just want the entire structure
  1281. if( !pDeltaBase )
  1282. {
  1283. CSerializedEntity *pNewSerialized = new CSerializedEntity( );
  1284. pNewSerialized->Copy( *pCurrProps );
  1285. return ( SerializedEntityHandle_t )pNewSerialized;
  1286. }
  1287. //we have a delta, so lets build only the properties that vary based upon the tick time
  1288. const int nNumProps = pCurrProps->GetFieldCount();
  1289. //first pass, determine all the memory that we'll need for everything
  1290. const CChangeFrameList *pChangeList = pCurrPacked->GetChangeFrameList();
  1291. int nNumChangedProps = 0;
  1292. int nChangedPropDataBits = 0;
  1293. for( int nCurrProp = 0; nCurrProp < nNumProps; ++nCurrProp )
  1294. {
  1295. CFieldPath PropIndex = pCurrProps->GetFieldPath( nCurrProp );
  1296. if( pChangeList->DidPropChangeAfterTick( nTick, PropIndex ) )
  1297. {
  1298. //this property changed, so add it into our list
  1299. AssertMsg( nNumChangedProps < knMaxChangedProps, "Error: Overflow in modified properties for delta compression. This limit should be increased" );
  1300. nChangedPropIndices[ nNumChangedProps ] = nCurrProp;
  1301. nNumChangedProps++;
  1302. nChangedPropDataBits += pCurrProps->GetFieldDataSizeInBits( nCurrProp );
  1303. }
  1304. }
  1305. //if we have no changed properties, excellent, we can just bail and not have to spend memory
  1306. if( nNumChangedProps == 0 )
  1307. return SERIALIZED_ENTITY_HANDLE_INVALID;
  1308. //we have changed props, so go ahead and allocate a buffer to hold the memory that we are going to setup
  1309. CSerializedEntity *pNewSerialized = new CSerializedEntity( );
  1310. pNewSerialized->SetupPackMemory( nNumChangedProps, nChangedPropDataBits );
  1311. //setup the readers and writers
  1312. bf_read SrcPropData;
  1313. pCurrProps->StartReading( SrcPropData );
  1314. bf_write OutPropData;
  1315. pNewSerialized->StartWriting( OutPropData );
  1316. //and now run through and copy over the data that we need
  1317. for( int nOutputProp = 0; nOutputProp < nNumChangedProps; ++nOutputProp )
  1318. {
  1319. int nDataOffset, nNextOffset;
  1320. CFieldPath PropIndex;
  1321. pCurrProps->GetField( nChangedPropIndices[ nOutputProp ], PropIndex, &nDataOffset, &nNextOffset );
  1322. //record this information in our buffer
  1323. pNewSerialized->SetFieldPath( nOutputProp, PropIndex );
  1324. pNewSerialized->SetFieldDataBitOffset( nOutputProp, OutPropData.GetNumBitsWritten() );
  1325. //copy over the contents that we care about (which are bit aligned)
  1326. SrcPropData.Seek( nDataOffset );
  1327. OutPropData.WriteBitsFromBuffer( &SrcPropData, nNextOffset - nDataOffset );
  1328. }
  1329. //make sure our size calculations line up
  1330. Assert( ( uint32 )OutPropData.GetNumBitsWritten() == pNewSerialized->GetFieldDataBitCount() );
  1331. //we have our final property set now
  1332. return ( SerializedEntityHandle_t )pNewSerialized;
  1333. }
  1334. //given a current and previous packed data, this will determine if the entity data changed, and if so, will setup the new recipient list with the appropraite information
  1335. static CSendProxyRecipients* CopyRecipientList( const PackedEntity* pCurrFramePacked, const PackedEntity* pPrevFramePacked )
  1336. {
  1337. if( ( pPrevFramePacked != NULL ) && pCurrFramePacked->CompareRecipients( CUtlMemory< CSendProxyRecipients >( pPrevFramePacked->GetRecipients(), pPrevFramePacked->GetNumRecipients() ) ) )
  1338. {
  1339. //they match, so we don't need a delta buffer
  1340. return NULL;
  1341. }
  1342. //if we got down here there is a mismatch, so just copy the new list
  1343. const int nCurrRecipients = pCurrFramePacked->GetNumRecipients();
  1344. CSendProxyRecipients* pCopyDest = new CSendProxyRecipients [ nCurrRecipients ];
  1345. const CSendProxyRecipients* pCopySrc = pCurrFramePacked->GetRecipients();
  1346. for( int nCopyRecipient = 0; nCopyRecipient < nCurrRecipients; ++nCopyRecipient )
  1347. pCopyDest[ nCopyRecipient ] = pCopySrc[ nCopyRecipient ];
  1348. return pCopyDest;
  1349. }
  1350. void CHLTVServer::CreateDeltaFrameEntities( SHLTVDeltaFrame_t *pOutputEntities, const CFrameSnapshot *pCurrFrame, const CFrameSnapshot *pPrevFrame )
  1351. {
  1352. //determine how many ints we need to store our bit list
  1353. const uint32 nNumCopyInts = ( pCurrFrame->m_nNumEntities + 31 ) / 32;
  1354. //allocate memory for us to actually store the objects
  1355. pOutputEntities->m_nTotalEntities = pCurrFrame->m_nNumEntities;
  1356. pOutputEntities->m_nNumValidEntities = pCurrFrame->m_nValidEntities;
  1357. pOutputEntities->m_pEntities = NULL;
  1358. pOutputEntities->m_pCopyEntities = new uint32 [ nNumCopyInts ];
  1359. memset( pOutputEntities->m_pCopyEntities, 0, sizeof(uint32) * nNumCopyInts );
  1360. //the index into our current previous frame so that we can find old versions of the object to delta against
  1361. const uint16 *pPrevEntityIndex = ( pPrevFrame ) ? pPrevFrame->m_pValidEntities : NULL;
  1362. const uint16 *pPrevEndEntityIndex = ( pPrevFrame ) ? pPrevEntityIndex + pPrevFrame->m_nValidEntities : NULL;
  1363. //the tick that we want to compare changes against
  1364. int nChangeTick = ( pPrevFrame ) ? pPrevFrame->m_nTickCount : -1;
  1365. //the current tail of our linked list
  1366. SHLTVDeltaEntity_t** pListTail = &pOutputEntities->m_pEntities;
  1367. //don't iterate through the entities, but instead iterate through our valid entity list, which indexes into our entities so we can
  1368. //skip invalid entities
  1369. for( int nValidEntity = 0; nValidEntity < pCurrFrame->m_nValidEntities; ++nValidEntity )
  1370. {
  1371. const int nEntityIndex = pCurrFrame->m_pValidEntities[ nValidEntity ];
  1372. const CFrameSnapshotEntry *pSrcEntity = pCurrFrame->m_pEntities + nEntityIndex;
  1373. //see if we can find a previous entity in order to diff ourself against
  1374. bool bReuseOriginal = false;
  1375. const PackedEntity* pPrevFramePacked = NULL;
  1376. for( ; pPrevEntityIndex != pPrevEndEntityIndex; ++pPrevEntityIndex )
  1377. {
  1378. //see if our entity is higher than our current one, if so, we need to stop searching and let our current frame list catch up
  1379. if( *pPrevEntityIndex > nEntityIndex )
  1380. break;
  1381. //if the slot matches, then we may have a match
  1382. if( *pPrevEntityIndex == nEntityIndex )
  1383. {
  1384. const CFrameSnapshotEntry *pPrevEntity = &pPrevFrame->m_pEntities[ *pPrevEntityIndex ];
  1385. //we found a match. See if it is actually the same object
  1386. if( pPrevEntity->m_nSerialNumber == pSrcEntity->m_nSerialNumber )
  1387. {
  1388. //same object, so we can reuse the data, but first see if it is identical
  1389. if( pPrevEntity->m_pPackedData == pSrcEntity->m_pPackedData )
  1390. {
  1391. //it is identical, we can just reuse it
  1392. bReuseOriginal = true;
  1393. }
  1394. else
  1395. {
  1396. //different, so cache the data
  1397. pPrevFramePacked = framesnapshotmanager->GetPackedEntity( *( const_cast< CFrameSnapshot* >(pPrevFrame) ), nEntityIndex );
  1398. }
  1399. }
  1400. break;
  1401. }
  1402. }
  1403. //first off, see if this is an entity that hasn't changed at all
  1404. if( bReuseOriginal )
  1405. {
  1406. //this is the same entity, so just copy it forward
  1407. pOutputEntities->m_pCopyEntities[ nEntityIndex / 32 ] |= ( 1 << ( nEntityIndex % 32 ) );
  1408. //skip the normal handling
  1409. continue;
  1410. }
  1411. //allocate our new entity and add it to our list
  1412. SHLTVDeltaEntity_t *pOutEntity = new SHLTVDeltaEntity_t;
  1413. *pListTail = pOutEntity;
  1414. pListTail = &pOutEntity->m_pNext;
  1415. //copy over all the state we can from this entity
  1416. pOutEntity->m_nSerialNumber = pSrcEntity->m_nSerialNumber;
  1417. pOutEntity->m_pServerClass = pSrcEntity->m_pClass;
  1418. pOutEntity->m_nSourceIndex = nEntityIndex;
  1419. //handle the situation where there is no packed data (occurs occasionally)
  1420. if( pSrcEntity->m_pPackedData == INVALID_PACKED_ENTITY_HANDLE )
  1421. {
  1422. pOutEntity->m_SerializedEntity = SHLTVDeltaEntity_t::knNoPackedData;
  1423. }
  1424. else
  1425. {
  1426. //along with the data from the packed portion of the entity
  1427. const PackedEntity *pSrcPacked = framesnapshotmanager->GetPackedEntity( *( const_cast< CFrameSnapshot* >(pCurrFrame) ), nEntityIndex );
  1428. //this is a new packed entity that we'll need to construct on the other side
  1429. pOutEntity->m_nSnapshotCreationTick = pSrcPacked->GetSnapshotCreationTick( );
  1430. //build up the property list for this based upon how we want to encode it (absolute, relative, etc)
  1431. pOutEntity->m_SerializedEntity = CreateDeltaProperties( nChangeTick, pSrcPacked, pPrevFramePacked );
  1432. //update our recipient list
  1433. pOutEntity->m_nNumRecipients = ( uint16 )pSrcPacked->GetNumRecipients();
  1434. pOutEntity->m_pNewRecipients = CopyRecipientList( pSrcPacked, pPrevFramePacked );
  1435. }
  1436. }
  1437. }
  1438. void CHLTVServer::AddNewDeltaFrame( CClientFrame *pClientFrame )
  1439. {
  1440. //see if this support is disabled
  1441. if( !tv_enable_delta_frames.GetBool()
  1442. #if HLTV_REPLAY_ENABLED
  1443. || spec_replay_enable.GetBool() // <sergiy> delta frames with small delay make no sense because we'll effectively only compress a second or two ( the min delay )of the packets. Enabling replay means the first 20 seconds of frames must be decompressed. Maybe we'll implement compression between 20 and 100 seconds for competitive sometimes, but for now it seems ok to leave frames uncompressed if we want replay
  1444. #endif
  1445. )
  1446. {
  1447. //we aren't running delta frames, so make sure we decompress any we might currently have, and fall back to normal
  1448. ExpandDeltaFramesToTick( -1 );
  1449. AddNewFrame( pClientFrame );
  1450. return;
  1451. }
  1452. //track the performance of this frame
  1453. VPROF_BUDGET( "CHLTVServer::AddNewDeltaFrame", "HLTV" );
  1454. Assert( pClientFrame->tick_count > m_nLastTick );
  1455. //if we don't have any frames encoded, we will be doing an absolute encode, so we can just store the frame direct (saves a lot of time and allocations)
  1456. if( m_pLastSourceSnapshot == NULL )
  1457. {
  1458. Assert( ( m_pLastTargetSnapshot == NULL ) && ( m_nFirstTick < 0 ) );
  1459. //add the frame. This way it will be ready immediately, and we don't have to duplicate all of the setup work
  1460. AddNewFrame( pClientFrame );
  1461. //use this frame as our encoding relative frame, and our delta that we will build on later as well
  1462. m_pLastSourceSnapshot = pClientFrame->GetSnapshot();
  1463. m_pLastSourceSnapshot->AddReference();
  1464. m_pLastTargetSnapshot = pClientFrame->GetSnapshot();
  1465. m_pLastTargetSnapshot->AddReference();
  1466. return;
  1467. }
  1468. //update internal state based upon the incoming frames (this should match AddNewFrame)
  1469. m_nLastTick = pClientFrame->tick_count;
  1470. m_HLTVFrame.SetSnapshot( pClientFrame->GetSnapshot() );
  1471. m_HLTVFrame.tick_count = pClientFrame->tick_count;
  1472. m_HLTVFrame.last_entity = pClientFrame->last_entity;
  1473. m_HLTVFrame.transmit_entity = pClientFrame->transmit_entity;
  1474. //first off allocate our holding frame and client information
  1475. SHLTVDeltaFrame_t *pNewDeltaFrame = new SHLTVDeltaFrame_t;
  1476. pNewDeltaFrame->m_pNewerDeltaFrame = NULL;
  1477. pNewDeltaFrame->m_pRelativeFrame = NULL;
  1478. //Uncomment these lines if you want to enable validation support
  1479. {
  1480. //pNewDeltaFrame->m_pSourceFrame = pClientFrame->GetSnapshot();
  1481. //pNewDeltaFrame->m_pSourceFrame->AddReference();
  1482. }
  1483. //if we have a previously encoded frame, we want to use that as our relative baseline
  1484. if( m_pLastTargetSnapshot )
  1485. {
  1486. pNewDeltaFrame->m_pRelativeFrame = m_pLastTargetSnapshot;
  1487. pNewDeltaFrame->m_pRelativeFrame->AddReference();
  1488. }
  1489. //create our new frame and copy all the data over
  1490. pNewDeltaFrame->m_pClientFrame = new CHLTVFrame( );
  1491. pNewDeltaFrame->m_pClientFrame->CopyFrame( *pClientFrame );
  1492. pNewDeltaFrame->m_pClientFrame->CopyHLTVData( m_HLTVFrame );
  1493. //create a copy of the snapshot that we can work with
  1494. CFrameSnapshot *pNewSnapshot = CloneDeltaSnapshot( pClientFrame->GetSnapshot() );
  1495. //now we need to create our delta encoded objects
  1496. CreateDeltaFrameEntities( pNewDeltaFrame, pClientFrame->GetSnapshot(), m_pLastSourceSnapshot );
  1497. //transfer ownership of this snapshot over to the client frame (meaning we don't need our reference)
  1498. pNewDeltaFrame->m_pClientFrame->SetSnapshot(pNewSnapshot);
  1499. pNewSnapshot->ReleaseReference();
  1500. //link ourself into the list and make sure the newest link matches
  1501. if( m_pNewestDeltaFrame )
  1502. m_pNewestDeltaFrame->m_pNewerDeltaFrame = pNewDeltaFrame;
  1503. m_pNewestDeltaFrame = pNewDeltaFrame;
  1504. //and if our list was empty, our new frame is now also the oldest
  1505. if( !m_pOldestDeltaFrame )
  1506. m_pOldestDeltaFrame = pNewDeltaFrame;
  1507. // reset HLTV frame for recording next messages etc.
  1508. m_HLTVFrame.Reset();
  1509. m_HLTVFrame.SetSnapshot( NULL );
  1510. //update our references to point to our latest snapshots for subsequent encodes/decodes
  1511. if( m_pLastSourceSnapshot )
  1512. m_pLastSourceSnapshot->ReleaseReference();
  1513. if( m_pLastTargetSnapshot )
  1514. m_pLastTargetSnapshot->ReleaseReference();
  1515. m_pLastSourceSnapshot = pClientFrame->GetSnapshot();
  1516. m_pLastSourceSnapshot->AddReference();
  1517. m_pLastTargetSnapshot = pNewSnapshot;
  1518. m_pLastTargetSnapshot->AddReference();
  1519. }
  1520. //given a baseline property list and a delta from that, this will expand the delta serialized entity to contain a full property set
  1521. static void BuildMergedPropertySet( CSerializedEntity* pDelta, const CSerializedEntity* pBase, CChangeFrameList* pChangeTimes, int nChangeTick )
  1522. {
  1523. int nDataBlockSizeBits = pBase->GetFieldDataBitCount();
  1524. const int nNumFields = pBase->GetFieldCount();
  1525. //see if we can take an optimized path where we can memcpy over our baseline and just overlay
  1526. bool bCanMemCpySrc = true;
  1527. {
  1528. int nOverrideIndex = 0;
  1529. CFieldPath NextOverridePath = pDelta->GetFieldPath( 0 );
  1530. for( int nCurrField = 0; nCurrField < nNumFields; ++nCurrField )
  1531. {
  1532. //see if this is overridden
  1533. CFieldPath FieldPath= pBase->GetFieldPath( nCurrField );
  1534. if( FieldPath == NextOverridePath )
  1535. {
  1536. //we have a property change that we are applying, so update the change tick time
  1537. pChangeTimes->SetChangeTick( FieldPath, nChangeTick );
  1538. //use our modified size, minus our base size to determine the change in size from this override
  1539. int nBaseSize = pBase->GetFieldDataSizeInBits( nCurrField );
  1540. int nModifiedSize = pDelta->GetFieldDataSizeInBits( nOverrideIndex );
  1541. //see if this has changed the layout to the point where we have to re lay out the base fields
  1542. if( nModifiedSize != nBaseSize )
  1543. {
  1544. bCanMemCpySrc = false;
  1545. nDataBlockSizeBits += ( nModifiedSize - nBaseSize );
  1546. }
  1547. //and advance to our next override
  1548. nOverrideIndex++;
  1549. if( nOverrideIndex < pDelta->GetFieldCount( ) )
  1550. NextOverridePath = pDelta->GetFieldPath( nOverrideIndex );
  1551. }
  1552. }
  1553. }
  1554. //create a stack entity to take the new merged results
  1555. CSerializedEntity Merged;
  1556. Merged.SetupPackMemory( nNumFields, nDataBlockSizeBits );
  1557. if(bCanMemCpySrc)
  1558. {
  1559. memcpy( Merged.GetFieldData(), pBase->GetFieldData(), Bits2Bytes( pBase->GetFieldDataBitCount() ) );
  1560. }
  1561. //now merge
  1562. {
  1563. //setup the readers and writers
  1564. bf_read BaseData, ModifiedData;
  1565. pBase->StartReading( BaseData);
  1566. pDelta->StartReading( ModifiedData );
  1567. bf_write OutPropData;
  1568. Merged.StartWriting( OutPropData );
  1569. int nOverrideIndex = 0;
  1570. CFieldPath NextOverridePath = pDelta->GetFieldPath( 0 );
  1571. for( int nCurrField = 0; nCurrField < nNumFields; ++nCurrField )
  1572. {
  1573. //see if this is overridden
  1574. CFieldPath FieldPath = pBase->GetFieldPath( nCurrField );
  1575. //update our storage info
  1576. Merged.SetFieldPath( nCurrField, FieldPath );
  1577. Merged.SetFieldDataBitOffset( nCurrField, OutPropData.GetNumBitsWritten() );
  1578. if( FieldPath == NextOverridePath )
  1579. {
  1580. int nModifiedSize = pDelta->GetFieldDataSizeInBits( nOverrideIndex );
  1581. OutPropData.WriteBitsFromBuffer( &ModifiedData, nModifiedSize );
  1582. //and advance to our next override
  1583. nOverrideIndex++;
  1584. if( nOverrideIndex < pDelta->GetFieldCount( ) )
  1585. NextOverridePath = pDelta->GetFieldPath( nOverrideIndex );
  1586. }
  1587. else
  1588. {
  1589. //use the baseline value
  1590. int nDataStart = pBase->GetFieldDataBitOffset( nCurrField );
  1591. int nDataEnd = pBase->GetFieldDataBitEndOffset( nCurrField );
  1592. if( !bCanMemCpySrc )
  1593. {
  1594. BaseData.Seek( nDataStart );
  1595. OutPropData.WriteBitsFromBuffer( &BaseData, nDataEnd - nDataStart );
  1596. }
  1597. else
  1598. {
  1599. OutPropData.SeekToBit( nDataEnd );
  1600. }
  1601. }
  1602. }
  1603. }
  1604. //now we can discard our results of the delta, and just use that object with the contents of our merged
  1605. Merged.Swap( *pDelta );
  1606. }
  1607. static void CompareArray( int nOldElements, const void* pOldMem, int nNewElements, const void* pNewMem, int nElementSize )
  1608. {
  1609. //to avoid warnings when asserts aren't disabled
  1610. #ifdef DBGFLAG_ASSERT
  1611. Assert( nOldElements == nNewElements );
  1612. int nNumBytes = nOldElements * nElementSize;
  1613. const uint8* pByteOld = ( const uint8* )pOldMem;
  1614. const uint8* pByteNew = ( const uint8* )pNewMem;
  1615. //per byte check so it is more apparent where things went wrong
  1616. for( int nCurrByte = 0; nCurrByte < nNumBytes; ++nCurrByte )
  1617. {
  1618. Assert( pByteOld[ nCurrByte ] == pByteNew[ nCurrByte ] );
  1619. }
  1620. #endif
  1621. }
  1622. //development utility to take two snapshots and compare them for equivalence. This is used to test whether or not the new snapshot matches the original one that was the basis for compression
  1623. static void CompareSnapshot( const CFrameSnapshot* pOldSnapshot, const CFrameSnapshot* pNewSnapshot, int nChangePropTick )
  1624. {
  1625. //to avoid warnings when asserts aren't disabled
  1626. #ifdef DBGFLAG_ASSERT
  1627. //first off compare the valid entity list
  1628. CompareArray( pOldSnapshot->m_nValidEntities, pOldSnapshot->m_pValidEntities, pNewSnapshot->m_nValidEntities, pNewSnapshot->m_pValidEntities, sizeof( uint16 ) );
  1629. CompareArray( pOldSnapshot->m_iExplicitDeleteSlots.Count(), pOldSnapshot->m_iExplicitDeleteSlots.Base(), pNewSnapshot->m_iExplicitDeleteSlots.Count(), pNewSnapshot->m_iExplicitDeleteSlots.Base(), sizeof( int ) );
  1630. //now each element
  1631. for( int nCurrEntity = 0; nCurrEntity < pOldSnapshot->m_nValidEntities; ++nCurrEntity )
  1632. {
  1633. int nEntityIndex = pOldSnapshot->m_pValidEntities[ nCurrEntity ];
  1634. //get info about each entity and compare
  1635. const CFrameSnapshotEntry* pOldEntity = &pOldSnapshot->m_pEntities[ nEntityIndex ];
  1636. const CFrameSnapshotEntry* pNewEntity = &pNewSnapshot->m_pEntities[ nEntityIndex ];
  1637. Assert( pOldEntity->m_nSerialNumber == pNewEntity->m_nSerialNumber );
  1638. Assert( pOldEntity->m_pClass == pNewEntity->m_pClass );
  1639. Assert( ( pOldEntity->m_pPackedData != INVALID_PACKED_ENTITY_HANDLE ) == ( pNewEntity->m_pPackedData != INVALID_PACKED_ENTITY_HANDLE ) );
  1640. //now check the packed data if applicable
  1641. if( pOldEntity->m_pPackedData )
  1642. {
  1643. const PackedEntity* pOldPacked = framesnapshotmanager->GetPackedEntity( *( const_cast< CFrameSnapshot* >(pOldSnapshot) ), nEntityIndex );
  1644. const PackedEntity* pNewPacked = framesnapshotmanager->GetPackedEntity( *( const_cast< CFrameSnapshot* >(pNewSnapshot) ), nEntityIndex );
  1645. Assert( pOldPacked->m_nEntityIndex == pNewPacked->m_nEntityIndex );
  1646. Assert( pOldPacked->GetSnapshotCreationTick() == pNewPacked->GetSnapshotCreationTick() );
  1647. Assert( pOldPacked->ShouldCheckCreationTick() == pNewPacked->ShouldCheckCreationTick() );
  1648. Assert( pOldPacked->m_pServerClass == pNewPacked->m_pServerClass );
  1649. Assert( pOldPacked->m_pClientClass == pNewPacked->m_pClientClass );
  1650. //compare the recipients
  1651. CompareArray( pOldPacked->GetNumRecipients(), pOldPacked->GetRecipients(), pNewPacked->GetNumRecipients(), pNewPacked->GetRecipients(), sizeof( CSendProxyRecipients ) );
  1652. //compare the change list
  1653. {
  1654. int nNumProps = pOldPacked->GetChangeFrameList()->GetNumProps();
  1655. for( int nCurrProp = 0; nCurrProp < nNumProps; ++nCurrProp )
  1656. {
  1657. //see if this is a property that should have changed this frame (we have to do this since we lose some precision on the change flags during reconstruction)
  1658. if( pOldPacked->GetChangeFrameList()->GetPropTick( nCurrProp ) >= nChangePropTick )
  1659. {
  1660. Assert( pNewPacked->GetChangeFrameList()->GetPropTick( nCurrProp ) >= nChangePropTick );
  1661. }
  1662. }
  1663. }
  1664. //now compare properties
  1665. Assert( ( pOldPacked->GetPackedData() == SERIALIZED_ENTITY_HANDLE_INVALID ) == ( pNewPacked->GetPackedData() == SERIALIZED_ENTITY_HANDLE_INVALID ) );
  1666. if( pOldPacked->GetPackedData() != SERIALIZED_ENTITY_HANDLE_INVALID )
  1667. {
  1668. const CSerializedEntity* pOldProps = ( const CSerializedEntity* )pOldPacked->GetPackedData();
  1669. const CSerializedEntity* pNewProps = ( const CSerializedEntity* )pNewPacked->GetPackedData();
  1670. CompareArray( pOldProps->GetFieldCount(), pOldProps->GetFieldPaths(), pNewProps->GetFieldCount(), pNewProps->GetFieldPaths(), sizeof( short ) );
  1671. CompareArray( pOldProps->GetFieldCount(), pOldProps->GetFieldDataBitOffsets(), pNewProps->GetFieldCount(), pNewProps->GetFieldDataBitOffsets(), sizeof( int ) );
  1672. CompareArray( pOldProps->GetFieldDataBitCount() / 8, pOldProps->GetFieldData(), pNewProps->GetFieldDataBitCount() / 8, pNewProps->GetFieldData(), sizeof( uint8 ) );
  1673. }
  1674. }
  1675. }
  1676. #endif
  1677. }
  1678. size_t CHLTVServer::SHLTVDeltaFrame_t::GetMemSize()const
  1679. {
  1680. uint nSize = sizeof( *this );
  1681. if ( CFrameSnapshot *pSnapshot = m_pClientFrame->GetSnapshot() )
  1682. {
  1683. nSize += pSnapshot->GetMemSize();
  1684. }
  1685. return nSize;
  1686. }
  1687. size_t CFrameSnapshot::GetMemSize()const
  1688. {
  1689. size_t nSize = sizeof( *this );
  1690. nSize += m_nNumEntities * sizeof( CFrameSnapshotEntry ) + m_nValidEntities * sizeof( *m_pValidEntities );
  1691. return nSize;
  1692. }
  1693. void CHLTVServer::ExpandDeltaFrameToFullFrame( SHLTVDeltaFrame_t *pDeltaFrame )
  1694. {
  1695. //track the performance of this frame
  1696. VPROF_BUDGET( "CHLTVServer::ExpandDeltaFrameToFullFrame", "HLTV" );
  1697. CFrameSnapshot *pSnapshot = pDeltaFrame->m_pClientFrame->GetSnapshot();
  1698. //we need to construct our full entity list in our snapshot
  1699. pSnapshot->m_pEntities = new CFrameSnapshotEntry[ pDeltaFrame->m_nTotalEntities ];
  1700. pSnapshot->m_nNumEntities = pDeltaFrame->m_nTotalEntities;
  1701. //and create our valid entity list, which is just a one to one index map
  1702. pSnapshot->m_pValidEntities = new uint16 [ pDeltaFrame->m_nNumValidEntities ];
  1703. pSnapshot->m_nValidEntities = pDeltaFrame->m_nNumValidEntities;
  1704. //the index into our current previous frame so that we can find old versions of the object to delta against
  1705. const CFrameSnapshot* pPrevFrame = pDeltaFrame->m_pRelativeFrame;
  1706. const uint32 nPrevFrameEntities = ( pPrevFrame ) ? pPrevFrame->m_nNumEntities : 0;
  1707. //the time tick that we should use for all of our change flags. Since the HLTV server can skip frames, be conservative in our change
  1708. //tick settings and use the frame after our last frame if we have one (effectively collapsing change times backwards), and if not, just our current tick count
  1709. const int nChangePropTick = ( pPrevFrame ) ? pPrevFrame->m_nTickCount + 1 : pSnapshot->m_nTickCount;
  1710. //the bit list that indicates which entities to just copy forward
  1711. const uint32* RESTRICT pCopyEntities = pDeltaFrame->m_pCopyEntities;
  1712. const uint32 nTotalEntities = pDeltaFrame->m_nTotalEntities;
  1713. SHLTVDeltaEntity_t *pCurrDeltaEntity = pDeltaFrame->m_pEntities;
  1714. uint16* RESTRICT pCurrOutValidEntity = pSnapshot->m_pValidEntities;
  1715. //run through each of our valid entities
  1716. for( uint32 nCurrEntity = 0; nCurrEntity < nTotalEntities; ++nCurrEntity )
  1717. {
  1718. CFrameSnapshotEntry *pCurrEntity = pSnapshot->m_pEntities + nCurrEntity;
  1719. //see if we just need to copy this forward
  1720. if( pCopyEntities[ nCurrEntity / 32 ] & ( 1 << ( nCurrEntity % 32 ) ) )
  1721. {
  1722. const CFrameSnapshotEntry *pPrevEntity = &pPrevFrame->m_pEntities[ nCurrEntity ];
  1723. //all we need to do is copy the previous to the current
  1724. pCurrEntity->m_nSerialNumber = pPrevEntity->m_nSerialNumber;
  1725. pCurrEntity->m_pClass = pPrevEntity->m_pClass;
  1726. pCurrEntity->m_pPackedData = pPrevEntity->m_pPackedData;
  1727. //and make sure to reference our packed data so it won't go away on us
  1728. if( pCurrEntity->m_pPackedData != INVALID_PACKED_ENTITY_HANDLE )
  1729. {
  1730. PackedEntity* pPacked = framesnapshotmanager->GetPackedEntity( pCurrEntity->m_pPackedData );
  1731. pPacked->m_ReferenceCount++;
  1732. }
  1733. //and add this entity to the valid entity list
  1734. *pCurrOutValidEntity = ( uint16 )nCurrEntity;
  1735. pCurrOutValidEntity++;
  1736. }
  1737. //otherwise, see if it is our delta entity that we encoded
  1738. else if( pCurrDeltaEntity && ( nCurrEntity == pCurrDeltaEntity->m_nSourceIndex ) )
  1739. {
  1740. //we have matched this delta entity, so move onto the next
  1741. SHLTVDeltaEntity_t* pDeltaEntity = pCurrDeltaEntity;
  1742. pCurrDeltaEntity = pCurrDeltaEntity->m_pNext;
  1743. //setup our valid list to index into this slot
  1744. *pCurrOutValidEntity = ( uint16 )nCurrEntity;
  1745. pCurrOutValidEntity++;
  1746. //copy over the raw data
  1747. pCurrEntity->m_nSerialNumber = pDeltaEntity->m_nSerialNumber;
  1748. pCurrEntity->m_pClass = pDeltaEntity->m_pServerClass;
  1749. pCurrEntity->m_pPackedData = INVALID_PACKED_ENTITY_HANDLE;
  1750. //see if there is no packed data associated with this class
  1751. if( pDeltaEntity->m_SerializedEntity == SHLTVDeltaEntity_t::knNoPackedData )
  1752. continue;
  1753. //see if we can find a previous entity in order to diff ourself against
  1754. PackedEntityHandle_t PrevPackedHandle = INVALID_PACKED_ENTITY_HANDLE;
  1755. PackedEntity *pPrevPacked = NULL;
  1756. if( nCurrEntity < nPrevFrameEntities )
  1757. {
  1758. const CFrameSnapshotEntry *pPrevEntity = &pPrevFrame->m_pEntities[ nCurrEntity ];
  1759. if( ( pPrevEntity->m_nSerialNumber == pDeltaEntity->m_nSerialNumber ) && ( pPrevEntity->m_pClass == pDeltaEntity->m_pServerClass ) )
  1760. {
  1761. PrevPackedHandle = pPrevEntity->m_pPackedData;
  1762. if( PrevPackedHandle != INVALID_PACKED_ENTITY_HANDLE )
  1763. pPrevPacked = framesnapshotmanager->GetPackedEntity( PrevPackedHandle );
  1764. }
  1765. }
  1766. //now the packed entity contents
  1767. PackedEntity* pNewPacked = framesnapshotmanager->CreateLocalPackedEntity( pSnapshot, pDeltaEntity->m_nSourceIndex );
  1768. pNewPacked->SetServerAndClientClass( pDeltaEntity->m_pServerClass, NULL );
  1769. pNewPacked->SetSnapshotCreationTick( pDeltaEntity->m_nSnapshotCreationTick );
  1770. //update our entities (which we either have stored, or we need to take from the previous frame)
  1771. if( pDeltaEntity->m_pNewRecipients )
  1772. {
  1773. pNewPacked->SetRecipients( CUtlMemory< CSendProxyRecipients >( pDeltaEntity->m_pNewRecipients, pDeltaEntity->m_nNumRecipients ) );
  1774. }
  1775. else if( pDeltaEntity->m_nNumRecipients > 0 )
  1776. {
  1777. //sanity check that they didn't change, and that we have valid frame of reference
  1778. Assert( pPrevPacked && ( pPrevPacked->GetNumRecipients() == pDeltaEntity->m_nNumRecipients ) );
  1779. pNewPacked->SetRecipients( CUtlMemory< CSendProxyRecipients >( pPrevPacked->GetRecipients(), pPrevPacked->GetNumRecipients() ) );
  1780. }
  1781. //and handle expanding out our serialized values. Either we can have a new object (just steal the properties), no changes (copy the original properties) or a diff
  1782. //(merge the properties)
  1783. if( !pPrevPacked )
  1784. {
  1785. //creation, so just use ours
  1786. pNewPacked->SetPackedData( pDeltaEntity->m_SerializedEntity );
  1787. pDeltaEntity->m_SerializedEntity = SERIALIZED_ENTITY_HANDLE_INVALID;
  1788. //setup a change list for all of our possible properties that is set to our creation time
  1789. CChangeFrameList* pChangeList = new CChangeFrameList( SendTable_GetNumFlatProps( pDeltaEntity->m_pServerClass->m_pTable ), pDeltaEntity->m_nSnapshotCreationTick );
  1790. pNewPacked->SetChangeFrameList( pChangeList );
  1791. }
  1792. else
  1793. {
  1794. const CSerializedEntity* pPrevProps = ( const CSerializedEntity* )pPrevPacked->GetPackedData();
  1795. //copy over our base change list so we can update the times that the properties changed
  1796. CChangeFrameList* pChangeList = new CChangeFrameList( *pPrevPacked->GetChangeFrameList() );
  1797. pNewPacked->SetChangeFrameList( pChangeList );
  1798. //if we don't have a serialized entity, we can just copy our previous (much faster)
  1799. if(pDeltaEntity->m_SerializedEntity == SERIALIZED_ENTITY_HANDLE_INVALID)
  1800. {
  1801. CSerializedEntity* pNewProps = new CSerializedEntity( );
  1802. pNewProps->Copy( *pPrevProps );
  1803. pNewPacked->SetPackedData( ( SerializedEntityHandle_t )pNewProps );
  1804. }
  1805. else
  1806. {
  1807. //we have to merge our new values onto our old ones
  1808. CSerializedEntity* pDeltaProps = ( CSerializedEntity* )pDeltaEntity->m_SerializedEntity;
  1809. BuildMergedPropertySet( pDeltaProps, pPrevProps, pChangeList, nChangePropTick );
  1810. //transfer ownership of our properties over to this object
  1811. pNewPacked->SetPackedData( ( SerializedEntityHandle_t ) pDeltaProps );
  1812. pDeltaEntity->m_SerializedEntity = SERIALIZED_ENTITY_HANDLE_INVALID;
  1813. }
  1814. }
  1815. }
  1816. else
  1817. {
  1818. //we don't have this object on this frame, so clear it out
  1819. pCurrEntity->m_nSerialNumber = -1;
  1820. pCurrEntity->m_pClass = NULL;
  1821. pCurrEntity->m_pPackedData = INVALID_PACKED_ENTITY_HANDLE;
  1822. }
  1823. }
  1824. //Sanity check our expansion
  1825. if( pDeltaFrame->m_pSourceFrame )
  1826. CompareSnapshot( pDeltaFrame->m_pSourceFrame, pDeltaFrame->m_pClientFrame->GetSnapshot(), nChangePropTick );
  1827. }
  1828. void CHLTVServer::ExpandDeltaFramesToTick( int nTick )
  1829. {
  1830. //just expand and add all frames until we find one that is of a later tick
  1831. while ( m_pOldestDeltaFrame )
  1832. {
  1833. SHLTVDeltaFrame_t *pFrame = m_pOldestDeltaFrame;
  1834. //see if our oldest is still too new to decompress (-1 means decompress them all)
  1835. if( ( nTick != -1 ) && ( pFrame->m_pClientFrame->tick_count > nTick ) )
  1836. break;
  1837. //expand the frame
  1838. ExpandDeltaFrameToFullFrame( pFrame );
  1839. //now add this into our frame list
  1840. AddClientFrame( pFrame->m_pClientFrame );
  1841. //give up ownership of it since someone else is now holding onto it
  1842. pFrame->m_pClientFrame = NULL;
  1843. //remove this frame from our list
  1844. m_pOldestDeltaFrame = m_pOldestDeltaFrame->m_pNewerDeltaFrame;
  1845. if( m_pOldestDeltaFrame == NULL )
  1846. m_pNewestDeltaFrame = NULL;
  1847. //and nuke the memory
  1848. delete pFrame;
  1849. }
  1850. }
  1851. void CHLTVServer::FreeAllDeltaFrames( )
  1852. {
  1853. while( m_pOldestDeltaFrame )
  1854. {
  1855. //advance to the next list entry
  1856. SHLTVDeltaFrame_t* pCurrFrame = m_pOldestDeltaFrame;
  1857. m_pOldestDeltaFrame = pCurrFrame->m_pNewerDeltaFrame;
  1858. //free everything about this frame
  1859. delete pCurrFrame;
  1860. }
  1861. //and make sure to completely reset our list
  1862. m_pNewestDeltaFrame = NULL;
  1863. }
  1864. CClientFrame *CHLTVServer::AddNewFrame( CClientFrame *clientFrame )
  1865. {
  1866. VPROF_BUDGET( "CHLTVServer::AddNewFrame", "HLTV" );
  1867. Assert ( clientFrame );
  1868. Assert( clientFrame->tick_count > m_nLastTick );
  1869. m_nLastTick = clientFrame->tick_count;
  1870. m_HLTVFrame.SetSnapshot( clientFrame->GetSnapshot() );
  1871. m_HLTVFrame.tick_count = clientFrame->tick_count;
  1872. m_HLTVFrame.last_entity = clientFrame->last_entity;
  1873. m_HLTVFrame.transmit_entity = clientFrame->transmit_entity;
  1874. // remember tick of first valid frame
  1875. if ( m_nFirstTick < 0 )
  1876. {
  1877. m_nFirstTick = clientFrame->tick_count;
  1878. m_nTickCount = m_nFirstTick;
  1879. if ( !IsMasterProxy() )
  1880. {
  1881. Assert ( m_State == ss_loading );
  1882. m_State = ss_active; // we are now ready to go
  1883. ReconnectClients();
  1884. ConMsg("GOTV relay active (%d)\n", GetInstanceIndex() ); // there should only be one relay
  1885. Steam3Server().Activate();
  1886. Steam3Server().SendUpdatedServerDetails();
  1887. if ( serverGameDLL )
  1888. {
  1889. serverGameDLL->GameServerSteamAPIActivated( true );
  1890. }
  1891. }
  1892. else
  1893. {
  1894. ConMsg("GOTV[%d] broadcast active.\n", GetInstanceIndex() );
  1895. }
  1896. }
  1897. CHLTVFrame *hltvFrame = new CHLTVFrame;
  1898. // copy tickcount & entities from client frame
  1899. hltvFrame->CopyFrame( *clientFrame );
  1900. //copy rest (messages, tempents) from current HLTV frame
  1901. hltvFrame->CopyHLTVData( m_HLTVFrame );
  1902. // add frame to HLTV server
  1903. int nClientFrameCount = AddClientFrame( hltvFrame );
  1904. // Only keep the number of packets required to satisfy tv_delay at our tv snapshot rate
  1905. static ConVarRef tv_delay( "tv_delay" );
  1906. float flTvDelayToKeep = tv_delay.GetFloat();
  1907. if ( spec_replay_enable.GetBool() )
  1908. flTvDelayToKeep = Max( flTvDelayToKeep, spec_replay_message_time.GetFloat() + spec_replay_leadup_time.GetFloat() );
  1909. extern ConVar tv_snapshotrate;
  1910. int numFramesToKeep = 2 * ( ( 1 + Max( 1.0f, flTvDelayToKeep ) ) * int( m_flSnapshotRate ) );
  1911. if ( numFramesToKeep < MAX_CLIENT_FRAMES )
  1912. numFramesToKeep = MAX_CLIENT_FRAMES;
  1913. while ( nClientFrameCount > numFramesToKeep )
  1914. {
  1915. RemoveOldestFrame();
  1916. -- nClientFrameCount;
  1917. }
  1918. // reset HLTV frame for recording next messages etc.
  1919. m_HLTVFrame.Reset();
  1920. m_HLTVFrame.SetSnapshot( NULL );
  1921. return hltvFrame;
  1922. }
  1923. // Different HLTV servers may have e.g. different snapshot rates.
  1924. // This is a chance for HLTV server to patch up some convars for its clients (like the tv_snapshotrate)
  1925. void CHLTVServer::FixupConvars( CNETMsg_SetConVar_t &convars )
  1926. {
  1927. if ( GetSnapshotRate() != tv_snapshotrate.GetFloat() )
  1928. {
  1929. char rate[ 32 ];
  1930. V_snprintf( rate, sizeof( rate ), "%g", GetSnapshotRate() );
  1931. bool bReplaced = false;
  1932. for ( int i = 0; i < convars.convars().cvars_size(); ++i )
  1933. {
  1934. if ( convars.convars().cvars( i ).name() == "tv_snapshotrate" )
  1935. {
  1936. convars.mutable_convars()->mutable_cvars( i )->set_value( rate );
  1937. bReplaced = true;
  1938. break;
  1939. }
  1940. }
  1941. if ( !bReplaced )
  1942. {
  1943. CMsg_CVars_CVar *pCVar = convars.mutable_convars()->add_cvars();
  1944. pCVar->set_name( "tv_snapshotrate" );
  1945. pCVar->set_value( rate );
  1946. }
  1947. }
  1948. }
  1949. void CHLTVServer::SendClientMessages( bool bSendSnapshots )
  1950. {
  1951. // build individual updates
  1952. for ( int i=0; i< m_Clients.Count(); i++ )
  1953. {
  1954. CHLTVClient* client = Client(i);
  1955. // Update Host client send state...
  1956. if ( !client->ShouldSendMessages() )
  1957. {
  1958. continue;
  1959. }
  1960. // Append the unreliable data (player updates and packet entities)
  1961. if ( m_CurrentFrame && client->IsActive() )
  1962. {
  1963. // don't send same snapshot twice
  1964. client->SendSnapshot( m_CurrentFrame );
  1965. }
  1966. else
  1967. {
  1968. // Connected, but inactive, just send reliable, sequenced info.
  1969. client->m_NetChannel->Transmit();
  1970. }
  1971. client->UpdateSendState();
  1972. client->m_fLastSendTime = net_time;
  1973. }
  1974. }
  1975. bool CHLTVServer::SendClientMessages( CHLTVClient *client )
  1976. {
  1977. // Update Host client send state...
  1978. if ( !client->ShouldSendMessages() )
  1979. {
  1980. return false;
  1981. }
  1982. // Append the unreliable data (player updates and packet entities)
  1983. if ( m_CurrentFrame && client->IsActive() )
  1984. {
  1985. // don't send same snapshot twice
  1986. client->SendSnapshot( m_CurrentFrame );
  1987. }
  1988. else
  1989. {
  1990. // Connected, but inactive, just send reliable, sequenced info.
  1991. client->m_NetChannel->Transmit();
  1992. }
  1993. client->UpdateSendState();
  1994. client->m_fLastSendTime = net_time;
  1995. return true;
  1996. }
  1997. void CHLTVServer::UpdateStats( void )
  1998. {
  1999. if ( m_fNextSendUpdateTime > net_time )
  2000. return;
  2001. m_fNextSendUpdateTime = net_time + 8.0f;
  2002. // fire game event for everyone
  2003. IGameEvent *event = NULL;
  2004. if ( !IsMasterProxy() && !m_ClientState.IsConnected() )
  2005. {
  2006. // we are disconnected from SourceTV server
  2007. event = g_GameEventManager.CreateEvent( "hltv_message", true );
  2008. if ( !event )
  2009. return;
  2010. event->SetString( "text", "#GOTV_Reconnecting" );
  2011. }
  2012. else
  2013. {
  2014. int proxies = 0, slots = 0, clients = 0;
  2015. for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() )
  2016. {
  2017. int nLocalProxyCount, nLocalSlotCount, nLocalClientCount;
  2018. hltv->GetGlobalStats( nLocalProxyCount,nLocalSlotCount,nLocalClientCount );
  2019. proxies += nLocalProxyCount;
  2020. slots += nLocalSlotCount;
  2021. clients += nLocalClientCount;
  2022. }
  2023. event = g_GameEventManager.CreateEvent( "hltv_status", true );
  2024. if ( !event )
  2025. return;
  2026. //
  2027. // There's no reason to ever record IP addresses in GOTV demo
  2028. //
  2029. // char address[32];
  2030. //
  2031. // if ( IsMasterProxy() || tv_overridemaster.GetBool() )
  2032. // {
  2033. // // broadcast own address
  2034. // Q_snprintf( address, sizeof(address), "%s:%u", net_local_adr.ToString(true), GetUDPPort() );
  2035. // }
  2036. // else
  2037. // {
  2038. // // forward address
  2039. // Q_snprintf( address, sizeof(address), "%s", m_RootServer.ToString() );
  2040. // }
  2041. //
  2042. // event->SetString( "master", address );
  2043. event->SetInt( "clients", clients );
  2044. event->SetInt( "slots", slots);
  2045. event->SetInt( "proxies", proxies );
  2046. int numExternalTotalViewers, numExternalLinkedViewers;
  2047. GetExternalStats( numExternalTotalViewers, numExternalLinkedViewers );
  2048. event->SetInt( "externaltotal", numExternalTotalViewers );
  2049. event->SetInt( "externallinked", numExternalLinkedViewers );
  2050. }
  2051. if ( IsMasterProxy() )
  2052. {
  2053. // as a master fire event for every one
  2054. g_GameEventManager.FireEvent( event );
  2055. }
  2056. else
  2057. {
  2058. // as a relay proxy just broadcast event
  2059. BroadcastEvent( event );
  2060. }
  2061. }
  2062. bool CHLTVServer::NETMsg_PlayerAvatarData( const CNETMsg_PlayerAvatarData& msg )
  2063. {
  2064. PlayerAvatarDataMap_t::IndexType_t idxData = m_mapPlayerAvatarData.Find( msg.accountid() );
  2065. if ( idxData != m_mapPlayerAvatarData.InvalidIndex() )
  2066. {
  2067. delete m_mapPlayerAvatarData.Element( idxData );
  2068. m_mapPlayerAvatarData.RemoveAt( idxData );
  2069. }
  2070. CNETMsg_PlayerAvatarData_t *pHtlvDataCopy = new CNETMsg_PlayerAvatarData_t;
  2071. pHtlvDataCopy->CopyFrom( msg );
  2072. m_mapPlayerAvatarData.Insert( pHtlvDataCopy->accountid(), pHtlvDataCopy );
  2073. // Enqueue this message for all fully connected clients immediately
  2074. for ( int iClient = 0; iClient < GetClientCount(); ++iClient )
  2075. {
  2076. CBaseClient *pClient = dynamic_cast< CBaseClient * >( GetClient( iClient ) );
  2077. if ( !pClient->IsActive() )
  2078. continue;
  2079. if ( INetChannel *pNetChannel = pClient->GetNetChannel() )
  2080. {
  2081. pNetChannel->EnqueueVeryLargeAsyncTransfer( *pHtlvDataCopy );
  2082. }
  2083. }
  2084. if ( m_DemoRecorder.IsRecording() )
  2085. {
  2086. m_DemoRecorder.RecordPlayerAvatar( pHtlvDataCopy );
  2087. }
  2088. return true;
  2089. }
  2090. bool CHLTVServer::SendNetMsg( INetMessage &msg, bool bForceReliable, bool bVoice )
  2091. {
  2092. //
  2093. // When sending messages to HLTV client we encrypt some messages with encryption key
  2094. //
  2095. if ( serverGameDLL &&
  2096. ( *tv_encryptdata_key.GetString() || *tv_encryptdata_key_pub.GetString() ) &&
  2097. ( msg.GetType() != svc_EncryptedData ) )
  2098. {
  2099. EncryptedMessageKeyType_t eKeyType = serverGameDLL->GetMessageEncryptionKey( &msg );
  2100. char const *szEncryptionKey = "";
  2101. switch ( eKeyType )
  2102. {
  2103. case kEncryptedMessageKeyType_Private:
  2104. szEncryptionKey = tv_encryptdata_key.GetString();
  2105. break;
  2106. case kEncryptedMessageKeyType_Public:
  2107. szEncryptionKey = tv_encryptdata_key_pub.GetString();
  2108. break;
  2109. }
  2110. if ( szEncryptionKey && *szEncryptionKey )
  2111. {
  2112. CSVCMsg_EncryptedData_t encryptedMessage;
  2113. if ( !CmdEncryptedDataMessageCodec::SVCMsg_EncryptedData_EncryptMessage( encryptedMessage, &msg, szEncryptionKey ) )
  2114. return false;
  2115. encryptedMessage.set_key_type( eKeyType );
  2116. return SendNetMsg( encryptedMessage, true, false ); // recurse and send the generated messages as reliable
  2117. }
  2118. }
  2119. //
  2120. // Special message handling for avatar data
  2121. //
  2122. if ( msg.GetType() == net_PlayerAvatarData )
  2123. {
  2124. CNETMsg_PlayerAvatarData const *pPlayerAvatarData = dynamic_cast< CNETMsg_PlayerAvatarData * >( &msg );
  2125. if ( !pPlayerAvatarData )
  2126. return false;
  2127. return NETMsg_PlayerAvatarData( *pPlayerAvatarData );
  2128. }
  2129. //
  2130. // Send the actual outgoing message
  2131. //
  2132. if ( m_bSignonState )
  2133. {
  2134. return msg.WriteToBuffer( m_Signon );
  2135. }
  2136. int buffer = HLTV_BUFFER_UNRELIABLE; // default destination
  2137. if ( msg.IsReliable() )
  2138. {
  2139. buffer = HLTV_BUFFER_RELIABLE;
  2140. }
  2141. else if ( msg.GetType() == svc_Sounds )
  2142. {
  2143. buffer = HLTV_BUFFER_SOUNDS;
  2144. }
  2145. else if ( msg.GetType() == svc_VoiceData )
  2146. {
  2147. buffer = HLTV_BUFFER_VOICE;
  2148. }
  2149. else if ( msg.GetType() == svc_TempEntities )
  2150. {
  2151. buffer = HLTV_BUFFER_TEMPENTS;
  2152. }
  2153. // anything else goes to the unreliable bin
  2154. return msg.WriteToBuffer( m_HLTVFrame.m_Messages[buffer] );
  2155. }
  2156. bf_write *CHLTVServer::GetBuffer( int nBuffer )
  2157. {
  2158. if ( nBuffer < 0 || nBuffer >= HLTV_BUFFER_MAX )
  2159. return NULL;
  2160. return &m_HLTVFrame.m_Messages[nBuffer];
  2161. }
  2162. IServer *CHLTVServer::GetBaseServer()
  2163. {
  2164. return (IServer*)this;
  2165. }
  2166. IHLTVDirector *CHLTVServer::GetDirector()
  2167. {
  2168. return m_Director;
  2169. }
  2170. CClientFrame *CHLTVServer::GetDeltaFrame( int nTick )
  2171. {
  2172. if ( !tv_deltacache.GetBool() )
  2173. return GetClientFrame( nTick ); //expensive
  2174. AUTO_LOCK_FM( m_FrameCacheMutex ); // we need to lock frame cache because we're potentially modifying it from multiple sendPacket threads
  2175. // TODO make that a utlmap
  2176. FOR_EACH_VEC( m_FrameCache, iFrame )
  2177. {
  2178. if ( m_FrameCache[iFrame].nTick == nTick )
  2179. return m_FrameCache[iFrame].pFrame;
  2180. }
  2181. int i = m_FrameCache.AddToTail();
  2182. CFrameCacheEntry_s &entry = m_FrameCache[i];
  2183. entry.nTick = nTick;
  2184. entry.pFrame = GetClientFrame( nTick ); //expensive
  2185. return entry.pFrame;
  2186. }
  2187. CClientFrame *CHLTVServer::ExpandAndGetClientFrame( int nTick, bool bExact )
  2188. {
  2189. ExpandDeltaFramesToTick( nTick );
  2190. return GetClientFrame( nTick, bExact );
  2191. }
  2192. void CHLTVServer::RunFrame()
  2193. {
  2194. VPROF_BUDGET( "CHLTVServer::RunFrame", "HLTV" );
  2195. // update network time etc
  2196. NET_RunFrame( Plat_FloatTime() );
  2197. if ( m_ClientState.m_nSignonState > SIGNONSTATE_NONE )
  2198. {
  2199. // process data from net socket
  2200. NET_ProcessSocket( m_ClientState.m_Socket, &m_ClientState );
  2201. m_ClientState.RunFrame();
  2202. m_ClientState.SendPacket();
  2203. }
  2204. // check if HLTV server if active
  2205. if ( !IsActive() )
  2206. return;
  2207. if ( host_frametime > 0 )
  2208. {
  2209. m_flFPS = m_flFPS * 0.99f + 0.01f/host_frametime;
  2210. }
  2211. if ( IsPlayingBack() )
  2212. return;
  2213. // get current tick time for director module and restore
  2214. // world (stringtables, framebuffers) as they were at this time
  2215. UpdateTick();
  2216. // Run any commands from client and play client Think functions if it is time.
  2217. CBaseServer::RunFrame();
  2218. UpdateStats();
  2219. SendClientMessages( true );
  2220. // Update the Steam server if we're running a relay.
  2221. if ( !sv.IsActive() )
  2222. Steam3Server().RunFrame();
  2223. UpdateMasterServer();
  2224. }
  2225. void CHLTVServer::UpdateTick( void )
  2226. {
  2227. VPROF_BUDGET( "CHLTVServer::UpdateTick", "HLTV" );
  2228. if ( m_nFirstTick < 0 )
  2229. {
  2230. m_nTickCount = 0;
  2231. m_CurrentFrame = NULL;
  2232. return;
  2233. }
  2234. // set tick time to last frame added
  2235. int nNewTick = m_nLastTick;
  2236. if ( IsMasterProxy() )
  2237. {
  2238. // get tick from director, he decides delay etc
  2239. nNewTick = Max( m_nFirstTick, m_Director->GetDirectorTick() );
  2240. #if HLTV_REPLAY_ENABLED
  2241. if ( spec_replay_enable.GetBool() )
  2242. {
  2243. nNewTick = Max< int >( nNewTick, m_nLastTick - ( spec_replay_message_time.GetFloat() + spec_replay_leadup_time.GetFloat() ) / m_flTickInterval );
  2244. }
  2245. #endif
  2246. }
  2247. //handle expanding any delta frames we have accumulated up to this point
  2248. ExpandDeltaFramesToTick( nNewTick );
  2249. // the the closest available frame
  2250. CHLTVFrame *newFrame = (CHLTVFrame*) GetClientFrame( nNewTick, false );
  2251. if ( newFrame == NULL )
  2252. return; // we dont have a new frame
  2253. if ( m_CurrentFrame == newFrame )
  2254. return; // current frame didn't change
  2255. m_CurrentFrame = newFrame;
  2256. m_nTickCount = m_CurrentFrame->tick_count;
  2257. if ( IsMasterProxy() )
  2258. {
  2259. // now do master proxy stuff
  2260. // restore string tables for this time
  2261. RestoreTick( m_nTickCount );
  2262. // remove entities out of current PVS
  2263. if ( tv_transmitall.GetBool() == false )
  2264. {
  2265. EntityPVSCheck( m_CurrentFrame );
  2266. }
  2267. if ( ( m_DemoRecorder.IsRecording() || m_Broadcast.IsRecording() ) && m_CurrentFrame )
  2268. {
  2269. if ( m_DemoRecorder.IsRecording() )
  2270. m_DemoRecorder.WriteFrame( m_CurrentFrame, &m_DemoEventWriteBuffer );
  2271. if ( m_Broadcast.IsRecording() )
  2272. m_Broadcast.WriteFrame( m_CurrentFrame, &m_DemoEventWriteBuffer );
  2273. m_DemoEventWriteBuffer.Reset();
  2274. }
  2275. }
  2276. else
  2277. {
  2278. // delta entity cache works only for relay proxies
  2279. m_DeltaCache.SetTick( m_CurrentFrame->tick_count, m_CurrentFrame->last_entity+1 );
  2280. }
  2281. int removeTick = m_nTickCount - tv_window_size.GetFloat() / m_flTickInterval; // keep 16 seconds buffer
  2282. if ( removeTick > 0 )
  2283. {
  2284. DeleteClientFrames( removeTick );
  2285. }
  2286. m_FrameCache.RemoveAll();
  2287. }
  2288. const char *CHLTVServer::GetName( void ) const
  2289. {
  2290. return tv_name.GetString();
  2291. }
  2292. void CHLTVServer::FillServerInfo(CSVCMsg_ServerInfo &serverinfo)
  2293. {
  2294. CBaseServer::FillServerInfo( serverinfo );
  2295. serverinfo.set_player_slot( m_nPlayerSlot ); // all spectators think they're the HLTV client
  2296. serverinfo.set_max_clients( m_nGameServerMaxClients );
  2297. }
  2298. void CHLTVServer::Clear( void )
  2299. {
  2300. CBaseServer::Clear();
  2301. m_Director = NULL;
  2302. m_MasterClient = NULL;
  2303. m_ClientState.Clear();
  2304. m_Server = NULL;
  2305. m_nFirstTick = -1;
  2306. m_nLastTick = 0;
  2307. m_nTickCount = 0;
  2308. m_nPlayerSlot = 0;
  2309. m_flStartTime = 0.0f;
  2310. m_nViewEntity = 1;
  2311. m_nGameServerMaxClients = 0;
  2312. m_fNextSendUpdateTime = 0.0f;
  2313. Changelevel( false );
  2314. }
  2315. void CHLTVServer::Init(bool bIsDedicated)
  2316. {
  2317. CBaseServer::Init( bIsDedicated );
  2318. m_Socket = NS_HLTV + m_nInstanceIndex;
  2319. COMPILE_TIME_ASSERT(NS_HLTV1 == NS_HLTV + 1 );
  2320. // check if only master proxy is allowed, no broadcasting
  2321. if ( CommandLine()->FindParm("-tvmasteronly") )
  2322. {
  2323. m_bMasterOnlyMode = true;
  2324. }
  2325. }
  2326. void CHLTVServer::Changelevel( bool bInactivateClients )
  2327. {
  2328. m_Broadcast.StopRecording();// We can't broadcast after level change, because the broadcast manifest (which includes map name) is immutable during broadcast.
  2329. StopRecordingAndFreeFrames( true );
  2330. if ( bInactivateClients )
  2331. {
  2332. InactivateClients();
  2333. }
  2334. m_CurrentFrame = NULL;
  2335. m_HLTVFrame.FreeBuffers();
  2336. m_vPVSOrigin.Init();
  2337. DeleteClientFrames( -1 );
  2338. m_DeltaCache.Flush();
  2339. m_FrameCache.RemoveAll();
  2340. //free any frames that we may have had outstanding
  2341. FreeAllDeltaFrames();
  2342. //release any snapshots that we were referencing for delta frame construction
  2343. if( m_pLastSourceSnapshot )
  2344. {
  2345. m_pLastSourceSnapshot->ReleaseReference();
  2346. m_pLastSourceSnapshot = NULL;
  2347. }
  2348. if( m_pLastTargetSnapshot )
  2349. {
  2350. m_pLastTargetSnapshot->ReleaseReference();
  2351. m_pLastTargetSnapshot = NULL;
  2352. }
  2353. // Free all avatar data
  2354. m_mapPlayerAvatarData.PurgeAndDeleteElements();
  2355. }
  2356. void CHLTVServer::GetNetStats( float &avgIn, float &avgOut )
  2357. {
  2358. CBaseServer::GetNetStats( avgIn, avgOut );
  2359. if ( m_ClientState.IsActive() )
  2360. {
  2361. avgIn += m_ClientState.m_NetChannel->GetAvgData(FLOW_INCOMING);
  2362. avgOut += m_ClientState.m_NetChannel->GetAvgData(FLOW_OUTGOING);
  2363. }
  2364. }
  2365. void CHLTVServer::Shutdown( void )
  2366. {
  2367. m_nExternalTotalViewers = 0;
  2368. m_nExternalLinkedViewers = 0;
  2369. //stop any recording, and free our client frame list
  2370. m_Broadcast.StopRecording();
  2371. StopRecordingAndFreeFrames( true );
  2372. UninstallStringTables();
  2373. if ( IsMasterProxy() )
  2374. {
  2375. if ( m_MasterClient )
  2376. m_MasterClient->Disconnect( "GOTV stop." );
  2377. if ( m_Director )
  2378. m_Director->RemoveHLTVServer( this );
  2379. }
  2380. else
  2381. {
  2382. // do not try to reconnect to old connection
  2383. m_ClientState.m_Remote.RemoveAll();
  2384. m_ClientState.Disconnect();
  2385. }
  2386. g_GameEventManager.RemoveListener( this );
  2387. CBaseServer::Shutdown();
  2388. }
  2389. CDemoFile *CHLTVServer::GetDemoFile()
  2390. {
  2391. return &m_DemoFile;
  2392. }
  2393. //-----------------------------------------------------------------------------
  2394. // Purpose:
  2395. // Output : Returns true on success, false on failure.
  2396. //-----------------------------------------------------------------------------
  2397. bool CHLTVServer::IsPlayingBack( void )const
  2398. {
  2399. return m_bPlayingBack;
  2400. }
  2401. bool CHLTVServer::IsPlaybackPaused()const
  2402. {
  2403. return m_bPlaybackPaused;
  2404. }
  2405. float CHLTVServer::GetPlaybackTimeScale()
  2406. {
  2407. return m_flPlaybackRateModifier;
  2408. }
  2409. void CHLTVServer::SetPlaybackTimeScale(float timescale)
  2410. {
  2411. m_flPlaybackRateModifier = timescale;
  2412. }
  2413. void CHLTVServer::ResyncDemoClock()
  2414. {
  2415. m_nStartTick = host_tickcount;
  2416. }
  2417. int CHLTVServer::GetPlaybackStartTick( void )
  2418. {
  2419. return m_nStartTick;
  2420. }
  2421. int CHLTVServer::GetPlaybackTick( void )
  2422. {
  2423. return host_tickcount - m_nStartTick;
  2424. }
  2425. bool CHLTVServer::StartPlayback( const char *filename, bool bAsTimeDemo, CDemoPlaybackParameters_t const *pPlaybackParameters, int nStartingTick )
  2426. {
  2427. Clear();
  2428. if ( !m_DemoFile.Open( filename, true ) )
  2429. {
  2430. return false;
  2431. }
  2432. // Read in the m_DemoHeader
  2433. demoheader_t *dh = m_DemoFile.ReadDemoHeader( pPlaybackParameters );
  2434. if ( !dh )
  2435. {
  2436. ConMsg( "Failed to read demo header.\n" );
  2437. m_DemoFile.Close();
  2438. return false;
  2439. }
  2440. // create a fake channel with a NULL address (no encryption)
  2441. m_ClientState.m_NetChannel = NET_CreateNetChannel( NS_CLIENT, NULL, "DEMO", &m_ClientState, NULL, false );
  2442. if ( !m_ClientState.m_NetChannel )
  2443. {
  2444. ConMsg( "CDemo::Play: failed to create demo net channel\n" );
  2445. m_DemoFile.Close();
  2446. return false;
  2447. }
  2448. m_ClientState.m_NetChannel->SetTimeout( -1.0f ); // never timeout
  2449. // Now read in the directory structure.
  2450. m_bPlayingBack = true;
  2451. ConMsg( "Reading complete demo file at once...\n");
  2452. double start = Plat_FloatTime();
  2453. ReadCompleteDemoFile();
  2454. double diff; diff = Plat_FloatTime() - start;
  2455. ConMsg( "Reading time :%.4f\n", diff );
  2456. NET_RemoveNetChannel( m_ClientState.m_NetChannel, true );
  2457. m_ClientState.m_NetChannel = NULL;
  2458. return true;
  2459. }
  2460. void CHLTVServer::ReadCompleteDemoFile()
  2461. {
  2462. int tick = 0;
  2463. byte cmd = dem_signon;
  2464. char buffer[NET_MAX_PAYLOAD];
  2465. netpacket_t demoPacket;
  2466. // setup demo packet data buffer
  2467. Q_memset( &demoPacket, 0, sizeof(demoPacket) );
  2468. demoPacket.from.SetAddrType( NSAT_NETADR );
  2469. demoPacket.from.AsType<netadr_t>().SetType( NA_LOOPBACK);
  2470. while ( true )
  2471. {
  2472. int nPlayerSlot = 0;
  2473. m_DemoFile.ReadCmdHeader( cmd, tick, nPlayerSlot );
  2474. // COMMAND HANDLERS
  2475. switch ( cmd )
  2476. {
  2477. case dem_synctick:
  2478. ResyncDemoClock();
  2479. break;
  2480. case dem_stop:
  2481. // MOTODO we finished reading the file
  2482. return ;
  2483. case dem_consolecmd:
  2484. {
  2485. #ifndef DEDICATED
  2486. ACTIVE_SPLITSCREEN_PLAYER_GUARD( nPlayerSlot );
  2487. #endif
  2488. CNETMsg_StringCmd_t cmdmsg( m_DemoFile.ReadConsoleCommand() );
  2489. m_ClientState.NETMsg_StringCmd( cmdmsg );
  2490. }
  2491. break;
  2492. case dem_datatables:
  2493. {
  2494. ALIGN4 char data[64*1024] ALIGN4_POST;
  2495. bf_read buf( "dem_datatables", data, sizeof(data) );
  2496. m_DemoFile.ReadNetworkDataTables( &buf );
  2497. buf.Seek( 0 );
  2498. // support for older engine demos
  2499. if ( !DataTable_LoadDataTablesFromBuffer( &buf, m_DemoFile.m_DemoHeader.demoprotocol ) )
  2500. {
  2501. Host_Error( "Error parsing network data tables during demo playback." );
  2502. }
  2503. }
  2504. break;
  2505. case dem_stringtables:
  2506. {
  2507. void *data = malloc( 512*1024 ); // X360TBD: How much memory is really needed here?
  2508. bf_read buf( "dem_stringtables", data, 512*1024 );
  2509. m_DemoFile.ReadStringTables( &buf );
  2510. buf.Seek( 0 );
  2511. if ( !networkStringTableContainerClient->ReadStringTables( buf ) )
  2512. {
  2513. Host_Error( "Error parsing string tables during demo playback." );
  2514. }
  2515. free( data );
  2516. }
  2517. break;
  2518. case dem_usercmd:
  2519. {
  2520. #ifndef DEDICATED
  2521. ACTIVE_SPLITSCREEN_PLAYER_GUARD( nPlayerSlot );
  2522. #endif
  2523. char buffer[256];
  2524. int length = sizeof(buffer);
  2525. m_DemoFile.ReadUserCmd( buffer, length );
  2526. // MOTODO HLTV must store user commands too
  2527. }
  2528. break;
  2529. case dem_signon:
  2530. case dem_packet:
  2531. {
  2532. int inseq, outseqack = 0;
  2533. m_DemoFile.ReadCmdInfo( m_LastCmdInfo ); // MOTODO must be stored somewhere
  2534. m_DemoFile.ReadSequenceInfo( inseq, outseqack );
  2535. int length = m_DemoFile.ReadRawData( buffer, sizeof(buffer) );
  2536. if ( length > 0 )
  2537. {
  2538. // succsessfully read new demopacket
  2539. demoPacket.received = realtime;
  2540. demoPacket.size = length;
  2541. demoPacket.message.StartReading( buffer, length );
  2542. m_ClientState.m_NetChannel->ProcessPacket( &demoPacket, false );
  2543. }
  2544. }
  2545. break;
  2546. }
  2547. }
  2548. }
  2549. #define DEBUG_GOTV_RELAY_LOCAL 0
  2550. #if DEBUG_GOTV_RELAY_LOCAL
  2551. ConVar debug_gotv_relay_whitelist_ports( "debug_gotv_relay_whitelist_ports", "[27005][27006][27007]" ); // run 3 game servers first
  2552. #endif
  2553. int CHLTVServer::GetChallengeType ( const ns_address &adr )
  2554. {
  2555. if ( serverGameDLL && serverGameDLL->IsValveDS() )
  2556. {
  2557. #if DEBUG_GOTV_RELAY_LOCAL
  2558. if ( strstr( debug_gotv_relay_whitelist_ports.GetString(), CFmtStr( "[%u]", adr.GetPort() ) ) )
  2559. return PROTOCOL_HASHEDCDKEY; // When debugging on local machine only make exception for relay proxies, clients actually auth via SteamID
  2560. #else
  2561. extern bool IsHltvRelayProxyWhitelisted( ns_address const &adr );
  2562. if ( IsHltvRelayProxyWhitelisted( adr ) )
  2563. return PROTOCOL_HASHEDCDKEY; // HLTV makes an exception for the requesting relay proxy address
  2564. #endif
  2565. return CBaseServer::GetChallengeType( adr );
  2566. }
  2567. return PROTOCOL_HASHEDCDKEY; // HLTV doesn't need Steam authentication
  2568. }
  2569. static bool Helper_HLTV_VerifyOfficialPassword( const char *szPassword )
  2570. {
  2571. if ( !szPassword || !szPassword[0] )
  2572. return false;
  2573. if ( Q_strlen( szPassword ) != 32 )
  2574. return false;
  2575. char chSignHash[16] = {0};
  2576. Q_snprintf( chSignHash, ARRAYSIZE( chSignHash ), "%08lX", CRC32_ProcessSingleBuffer( szPassword, 24 ) );
  2577. return ( 0 == Q_strncmp( chSignHash, szPassword + 24, 8 ) );
  2578. }
  2579. static char const *Helper_HLTV_GenerateUniquePassword()
  2580. {
  2581. return "HLTV Official Password Must Be Encrypted";
  2582. }
  2583. #if 0
  2584. CON_COMMAND( debug_make_hltv_encrypted_password, "" )
  2585. {
  2586. char const *szPasswordProvidedByClient = args.Arg( 1 );
  2587. if ( !szPasswordProvidedByClient || !*szPasswordProvidedByClient || ( Q_strlen( szPasswordProvidedByClient ) != 32 ) )
  2588. {
  2589. Warning( "Bad password!\n" );
  2590. return;
  2591. }
  2592. char chClientHash[64]={0};
  2593. Q_snprintf( chClientHash, ARRAYSIZE( chClientHash ), "%08X%08X%08X",
  2594. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient, 32 ),
  2595. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient + 10, 22 ),
  2596. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient + 20, 12 ) );
  2597. Q_snprintf( chClientHash + 24, ARRAYSIZE( chClientHash ) - 24, "%08X",
  2598. CRC32_ProcessSingleBuffer( chClientHash, 24 ) );
  2599. Msg( "{%s}->{%s}\n", szPasswordProvidedByClient, chClientHash );
  2600. }
  2601. #endif
  2602. bool CHLTVServer::CheckHltvPasswordMatch( const char *szPasswordProvidedByClient, const char *szServerRequiredPassword, CSteamID steamidClient )
  2603. {
  2604. // Official servers must have a special encrypted password
  2605. if ( serverGameDLL && serverGameDLL->IsValveDS() )
  2606. {
  2607. if ( !Helper_HLTV_VerifyOfficialPassword( szServerRequiredPassword ) )
  2608. {
  2609. ExecuteNTimes( 3, Warning( "WARNING: %s (%s)\n", Helper_HLTV_GenerateUniquePassword(), szServerRequiredPassword ? szServerRequiredPassword : "none" ) );
  2610. return false; // without the encrypted password clients cannot connect
  2611. }
  2612. if ( !szPasswordProvidedByClient || !szPasswordProvidedByClient[0] )
  2613. return false; // server requires a password, but client didn't provide a password
  2614. // Enforce client password length to be 32 characters
  2615. if ( Q_strlen( szPasswordProvidedByClient ) == 32 )
  2616. {
  2617. // Compute a client password hash
  2618. char chClientHash[64]={0};
  2619. Q_snprintf( chClientHash, ARRAYSIZE( chClientHash ), "%08lX%08lX%08lX",
  2620. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient, 32 ),
  2621. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient + 10, 22 ),
  2622. CRC32_ProcessSingleBuffer( szPasswordProvidedByClient + 20, 12 ) );
  2623. if ( !Q_strncmp( chClientHash, szServerRequiredPassword, 24 ) )
  2624. return true;
  2625. }
  2626. }
  2627. else
  2628. {
  2629. if ( !szServerRequiredPassword || !szServerRequiredPassword[0] || !Q_stricmp( szServerRequiredPassword, "none" ) )
  2630. return true; // server doesn't require a password, allow client
  2631. if ( !szPasswordProvidedByClient || !szPasswordProvidedByClient[0] )
  2632. return false; // server requires a password, but client didn't provide a password
  2633. if ( !Q_strcmp( szPasswordProvidedByClient, szServerRequiredPassword ) )
  2634. return true; // compare passwords
  2635. }
  2636. //
  2637. // Check if client is connecting via watchable reservation
  2638. //
  2639. if ( tv_advertise_watchable.GetBool() && sv.IsReserved() && sv.GetReservationCookie() &&
  2640. szPasswordProvidedByClient && ( Q_strlen( szPasswordProvidedByClient ) == 64 ) )
  2641. {
  2642. // Decode client TV watchable password ascii
  2643. unsigned char chEncryptedPassword[ 32 + 1 ] = {0};
  2644. for ( int k = 0; k < 32; ++ k )
  2645. {
  2646. char chScan[5] = { '0', 'x', szPasswordProvidedByClient[2*k], szPasswordProvidedByClient[2*k+1], 0 };
  2647. uint32 uiByte = 0;
  2648. sscanf( chScan, "0x%02X", &uiByte );
  2649. chEncryptedPassword[k] = uiByte;
  2650. }
  2651. // Decrypt the encoded password
  2652. IceKey iceKey( 2 );
  2653. if ( iceKey.keySize() == 16 )
  2654. {
  2655. iceKey.set( ( unsigned char * ) CFmtStr( "%016llX", sv.GetReservationCookie() ).Access() );
  2656. char chDecryptedPassword[ 32 + 1 ] = {0};
  2657. for ( int k = 0; k < 32; k += iceKey.blockSize() )
  2658. {
  2659. iceKey.decrypt( chEncryptedPassword + k, ( unsigned char * ) chDecryptedPassword + k );
  2660. }
  2661. if ( !Q_strcmp( chDecryptedPassword, CFmtStr( "WATCH100%08X%016llX", steamidClient.GetAccountID(), sv.GetReservationCookie() ).Access() ) )
  2662. return true;
  2663. }
  2664. }
  2665. return false;
  2666. }
  2667. const char *CHLTVServer::GetPassword() const
  2668. {
  2669. const char *password = tv_password.GetString();
  2670. // Official servers must have a special encrypted password
  2671. if ( serverGameDLL && serverGameDLL->IsValveDS() && !Helper_HLTV_VerifyOfficialPassword( password ) )
  2672. return Helper_HLTV_GenerateUniquePassword();
  2673. // if password is empty or "none", return NULL
  2674. if ( !password[0] || !Q_stricmp(password, "none" ) )
  2675. {
  2676. return NULL;
  2677. }
  2678. return password;
  2679. }
  2680. const char *CHLTVServer::GetHltvRelayPassword() const
  2681. {
  2682. static ConVarRef tv_relaypassword( "tv_relaypassword" );
  2683. const char *password = tv_relaypassword.GetString();
  2684. if ( serverGameDLL && serverGameDLL->IsValveDS() && !Helper_HLTV_VerifyOfficialPassword( password ) )
  2685. return Helper_HLTV_GenerateUniquePassword();
  2686. // if password is empty or "none", return NULL
  2687. if ( !password[0] || !Q_stricmp(password, "none" ) )
  2688. {
  2689. return NULL;
  2690. }
  2691. return password;
  2692. }
  2693. IClient *CHLTVServer::ConnectClient ( const ns_address &adr, int protocol, int challenge, int authProtocol,
  2694. const char *name, const char *password, const char *hashedCDkey, int cdKeyLen,
  2695. CUtlVector< CCLCMsg_SplitPlayerConnect_t * > & splitScreenClients, bool isClientLowViolence, CrossPlayPlatform_t clientPlatform,
  2696. const byte *pbEncryptionKey, int nEncryptionKeyIndex )
  2697. {
  2698. IClient *client = (CHLTVClient*)CBaseServer::ConnectClient(
  2699. adr, protocol, challenge,authProtocol, name, password, hashedCDkey, cdKeyLen, splitScreenClients, isClientLowViolence, clientPlatform,
  2700. pbEncryptionKey, nEncryptionKeyIndex );
  2701. if ( client )
  2702. {
  2703. // remember password
  2704. CHLTVClient *pHltvClient = (CHLTVClient*)client;
  2705. Q_strncpy( pHltvClient->m_szPassword, password, sizeof(pHltvClient->m_szPassword) );
  2706. }
  2707. return client;
  2708. }
  2709. bool CHLTVServer::GetRedirectAddressForConnectClient( const ns_address &adr, CUtlVector< CCLCMsg_SplitPlayerConnect_t* > & splitScreenClients, ns_address *pNetAdrRedirect )
  2710. {
  2711. bool bConnectingClientIsTvRelay = false;
  2712. if ( splitScreenClients.Count() )
  2713. {
  2714. const CMsg_CVars& convars = splitScreenClients[0]->convars();
  2715. for ( int i = 0; i< convars.cvars_size(); ++i )
  2716. {
  2717. const char *cvname = NetMsgGetCVarUsingDictionary( convars.cvars(i) );
  2718. const char *value = convars.cvars(i).value().c_str();
  2719. if ( stricmp( cvname, "tv_relay" ) )
  2720. continue;
  2721. bConnectingClientIsTvRelay = ( value[0] == '1' );
  2722. break;
  2723. }
  2724. }
  2725. if ( bConnectingClientIsTvRelay )
  2726. return false;
  2727. // This is a human spectator, check if we should dispatch them down the chain
  2728. //
  2729. // The section below is largely a copy of DispatchToRelay function
  2730. //
  2731. if ( tv_dispatchmode.GetInt() <= DISPATCH_MODE_OFF )
  2732. return false; // don't redirect
  2733. CBaseClient *pBestProxy = NULL;
  2734. float fBestRatio = 1.0f;
  2735. // find best relay proxy
  2736. for (int i=0; i < GetClientCount(); i++ )
  2737. {
  2738. CBaseClient *pProxy = m_Clients[ i ];
  2739. // check all known proxies
  2740. if ( !pProxy->IsConnected() || !pProxy->IsHLTV() )
  2741. continue;
  2742. int slots = Q_atoi( pProxy->GetUserSetting( "hltv_slots" ) );
  2743. int clients = Q_atoi( pProxy->GetUserSetting( "hltv_clients" ) );
  2744. // skip overloaded proxies or proxies with no slots at all
  2745. if ( (clients > slots) || slots <= 0 )
  2746. continue;
  2747. // calc clients/slots ratio for this proxy
  2748. float ratio = ((float)(clients))/((float)slots);
  2749. if ( ratio < fBestRatio )
  2750. {
  2751. fBestRatio = ratio;
  2752. pBestProxy = pProxy;
  2753. }
  2754. }
  2755. if ( pBestProxy == NULL )
  2756. {
  2757. if ( tv_dispatchmode.GetInt() == DISPATCH_MODE_ALWAYS )
  2758. {
  2759. // we are in always forward mode, drop client if we can't forward it
  2760. pNetAdrRedirect->Clear();
  2761. RejectConnection( adr, "No GOTV relay available" );
  2762. return true;
  2763. }
  2764. else
  2765. {
  2766. // just let client connect to this proxy
  2767. return false;
  2768. }
  2769. }
  2770. // check if client should stay on this relay server unless we are the master,
  2771. // masters always prefer to send clients to relays
  2772. if ( (tv_dispatchmode.GetInt() == DISPATCH_MODE_AUTO) && (GetMaxClients() > 0) )
  2773. {
  2774. // ratio = clients/slots. give relay proxies 25% bonus
  2775. int numSlots = GetMaxClients();
  2776. if ( tv_maxclients_relayreserved.GetInt() > 0 )
  2777. numSlots -= tv_maxclients_relayreserved.GetInt();
  2778. numSlots = MAX( 0, numSlots );
  2779. int numClients = GetNumClients();
  2780. if ( numClients > numSlots )
  2781. numSlots = numClients;
  2782. float flDispatchWeight = tv_dispatchweight.GetFloat();
  2783. if ( flDispatchWeight <= 1.01 )
  2784. flDispatchWeight = 1.01;
  2785. float myRatio = ((float)numClients/(float)numSlots) * flDispatchWeight;
  2786. myRatio = MIN( myRatio, 1.0f ); // clamp to 1
  2787. // if we have a better local ratio then other proxies, keep this client here
  2788. if ( myRatio < fBestRatio )
  2789. return false; // don't redirect
  2790. }
  2791. CFmtStr fmtAdditionalInfo;
  2792. const char *pszRelayAddr = pBestProxy->GetUserSetting( "hltv_addr" );
  2793. if ( !pszRelayAddr )
  2794. return false;
  2795. // If the client is attempting a connection over SDR and the relay downstream allows
  2796. // connection over SDR, then redirect to SDR port instead
  2797. switch ( adr.GetAddressType() )
  2798. {
  2799. case NSAT_PROXIED_CLIENT:
  2800. if ( const char *pszRelaySdrAddr = pBestProxy->GetUserSetting("hltv_sdr") )
  2801. {
  2802. if ( *pszRelaySdrAddr )
  2803. {
  2804. ns_address nsadrsdr;
  2805. if ( nsadrsdr.SetFromString( pszRelaySdrAddr ) && ( nsadrsdr.GetAddressType() == NSAT_PROXIED_GAMESERVER ) )
  2806. {
  2807. // Ensure that client gets a ticket for the new SDR address
  2808. // and that the game server allows redirect
  2809. if ( serverGameDLL->IsValveDS() && serverGameDLL->OnEngineClientProxiedRedirect(
  2810. adr.m_steamID.GetSteamID().ConvertToUint64(), pszRelaySdrAddr, pszRelayAddr ) )
  2811. {
  2812. //
  2813. // Build a P2P HLTV channel SDR address for client redirect
  2814. //
  2815. fmtAdditionalInfo.AppendFormat( " @ %s SDR:%d", pszRelayAddr, nsadrsdr.m_steamID.GetSteamChannel() );
  2816. nsadrsdr.m_steamID.SetSteamChannel( STEAM_P2P_HLTV );
  2817. ns_address_render nsadrRendered( nsadrsdr );
  2818. char *pchStackCopy = ( char * ) stackalloc( 1 + V_strlen( nsadrRendered.String() ) );
  2819. V_strcpy( pchStackCopy, nsadrRendered.String() );
  2820. pszRelayAddr = pchStackCopy;
  2821. }
  2822. }
  2823. }
  2824. }
  2825. break;
  2826. }
  2827. ConMsg( "Redirecting spectator connect packet from %s to GOTV relay %s%s\n",
  2828. ns_address_render( adr ).String(),
  2829. pszRelayAddr, fmtAdditionalInfo.Access() );
  2830. // tell the client to connect to this new address
  2831. pNetAdrRedirect->SetFromString( pszRelayAddr );
  2832. // increase this proxies client number in advance so this proxy isn't used again next time
  2833. int clients = Q_atoi( pBestProxy->GetUserSetting( "hltv_clients" ) );
  2834. pBestProxy->SetUserCVar( "hltv_clients", va("%d", clients+1 ) );
  2835. return true;
  2836. }
  2837. CON_COMMAND( tv_status, "Show GOTV server status." )
  2838. {
  2839. int slots, proxies, clients;
  2840. float in, out;
  2841. char gd[MAX_OSPATH];
  2842. Q_FileBase( com_gamedir, gd, sizeof( gd ) );
  2843. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  2844. {
  2845. hltv->GetNetStats( in, out );
  2846. in /= 1024; // as KB
  2847. out /= 1024;
  2848. ConMsg( "--- GOTV[%u] Status ---\n", hltv.GetIndex() );
  2849. ConMsg( "Online %s, FPS %.1f, Version %i (%s)\n",
  2850. COM_FormatSeconds( hltv->GetOnlineTime() ), hltv->m_flFPS, build_number(),
  2851. #if defined( _WIN32 )
  2852. "Win32"
  2853. #else
  2854. "Linux"
  2855. #endif
  2856. );
  2857. if ( hltv->IsDemoPlayback() )
  2858. {
  2859. ConMsg( "Playing Demo File \"%s\"\n", "TODO demo file name" );
  2860. }
  2861. else if ( hltv->IsMasterProxy() )
  2862. {
  2863. ConMsg( "Master \"%s\", delay %.0f, rate %.1f\n", hltv->GetName(), hltv->GetDirector()->GetDelay(), hltv->GetSnapshotRate() );
  2864. }
  2865. else // if ( m_Server->IsRelayProxy() )
  2866. {
  2867. if ( hltv->GetRelayAddress() )
  2868. {
  2869. ConMsg( "Relay \"%s\", connect to %s\n", hltv->GetName(), hltv->GetRelayAddress()->ToString() );
  2870. }
  2871. else
  2872. {
  2873. ConMsg( "Relay \"%s\", not connect.\n", hltv->GetName() );
  2874. }
  2875. }
  2876. ConMsg( "Game Time %s, Mod \"%s\", Map \"%s\", Players %i\n", COM_FormatSeconds( hltv->GetTime() ),
  2877. gd, hltv->GetMapName(), hltv->GetNumPlayers() );
  2878. ConMsg( "Local IP %s:%i, KB/sec In %.1f, Out %.1f\n",
  2879. net_local_adr.ToString( true ), hltv->GetUDPPort(), in, out );
  2880. hltv->GetLocalStats( proxies, slots, clients );
  2881. ConMsg( "Local Slots %i, Spectators %i, Proxies %i\n",
  2882. slots, clients - proxies, proxies );
  2883. hltv->GetGlobalStats( proxies, slots, clients );
  2884. ConMsg( "Total Slots %i, Spectators %i, Proxies %i\n",
  2885. slots, clients - proxies, proxies );
  2886. hltv->GetExternalStats( slots, clients );
  2887. if ( slots > 0 )
  2888. {
  2889. if ( clients > 0 )
  2890. ConMsg( "Streaming spectators %i, linked to Steam %i\n", slots, clients );
  2891. else
  2892. ConMsg( "Streaming spectators %i\n", slots );
  2893. }
  2894. if ( hltv->m_DemoRecorder.IsRecording() )
  2895. {
  2896. ConMsg( "Recording to \"%s\", length %s.\n", hltv->m_DemoRecorder.GetDemoFilename(),
  2897. COM_FormatSeconds( host_state.interval_per_tick * hltv->m_DemoRecorder.GetRecordingTick() ) );
  2898. }
  2899. if ( hltv->m_Broadcast.IsRecording() )
  2900. {
  2901. ConMsg( "Broadcasting\n" );
  2902. }
  2903. ConMsg( "\n" );
  2904. extern ConVar host_name;
  2905. ConMsg( "hostname: %s\n", host_name.GetString() );
  2906. // the header for the status rows
  2907. ConMsg( "# userid name uniqueid connected ping loss state rate adr\n" );
  2908. for ( int j = 0; j < hltv->GetClientCount(); j++ )
  2909. {
  2910. IClient *client = hltv->GetClient( j );
  2911. if ( !client || !client->IsConnected() )
  2912. continue; // not connected yet, maybe challenging
  2913. extern void Host_Status_PrintClient( IClient *client, bool bShowAddress, void( *print ) ( const char *fmt, ... ) );
  2914. Host_Status_PrintClient( client, true, ConMsg );
  2915. }
  2916. }
  2917. ConMsg( "#end\n" );
  2918. }
  2919. CON_COMMAND( sv_getinfo, "Show user info of a connected client" )
  2920. {
  2921. if ( args.ArgC() < 4 )
  2922. {
  2923. ConMsg( "Usage: userinfo_show [sv|tv0|tv1] [id] [var]\n" );
  2924. return;
  2925. }
  2926. CBaseServer *psv = &sv;
  2927. if ( char const *szTvN = StringAfterPrefix( args.Arg( 1 ), "tv" ) )
  2928. {
  2929. int nTV = V_atoi( szTvN );
  2930. nTV = clamp( nTV, 0, HLTV_SERVER_MAX_COUNT - 1 );
  2931. psv = g_pHltvServer[ nTV ];
  2932. if ( !psv )
  2933. {
  2934. ConMsg( "TV%d not active\n", nTV );
  2935. return;
  2936. }
  2937. }
  2938. else if ( !psv )
  2939. {
  2940. ConMsg( "Main server not active\n" );
  2941. return;
  2942. }
  2943. int nClientID = V_atoi( args.Arg( 2 ) );
  2944. if ( nClientID < 0 || nClientID >= psv->GetClientCount() )
  2945. {
  2946. ConMsg( "Found %d clients on server\n", psv->GetClientCount() );
  2947. return;
  2948. }
  2949. IClient *pClient = psv->GetClient( nClientID );
  2950. if ( !pClient || !pClient->IsConnected() )
  2951. {
  2952. ConMsg( "Client #%d is %s\n", nClientID, pClient ? "not connected" : "null" );
  2953. return;
  2954. }
  2955. const char *pszVar = args.Arg( 3 );
  2956. const char *pszValue = pClient->GetUserSetting( pszVar );
  2957. ConMsg( "Client #%d '%s'<%s> '%s'='%s'\n", nClientID, pClient->GetClientName(), pClient->GetNetworkIDString(), pszVar, pszValue );
  2958. }
  2959. CON_COMMAND( tv_relay, "Connect to GOTV server and relay broadcast." )
  2960. {
  2961. if ( args.ArgC() < 2 )
  2962. {
  2963. ConMsg( "Usage: tv_relay <ip:port> [-instance <inst>]\n" );
  2964. return;
  2965. }
  2966. const char *address = args.ArgS();
  2967. // If it's not a single player connection to "localhost", initialize networking & stop listenserver
  2968. if ( StringHasPrefixCaseSensitive( address, "localhost" ) )
  2969. {
  2970. ConMsg( "GOTV can't connect to localhost.\n" );
  2971. return;
  2972. }
  2973. int nHltvIndex = clamp( args.FindArgInt( "-instance", 0 ), 0, HLTV_SERVER_MAX_COUNT - 1 );
  2974. CHLTVServer * & hltv = g_pHltvServer[ nHltvIndex ];
  2975. if ( !hltv )
  2976. {
  2977. hltv = new CHLTVServer( nHltvIndex, ( nHltvIndex ? tv_snapshotrate1.GetFloat() : tv_snapshotrate.GetFloat() ) );
  2978. hltv->Init( NET_IsDedicated() );
  2979. }
  2980. if ( hltv->m_bMasterOnlyMode )
  2981. {
  2982. ConMsg("GOTV[%d] in Master-Only mode.\n", nHltvIndex );
  2983. return;
  2984. }
  2985. // If the main server instance is running then we want to re-use it's
  2986. // logged on anonymous SteamID
  2987. if ( sv.IsDedicated() && sv.IsActive() )
  2988. sv.FlagForSteamIDReuseAfterShutdown();
  2989. // shutdown anything else
  2990. Host_Disconnect( false );
  2991. // start networking
  2992. NET_Init( NET_IsDedicated() );
  2993. NET_SetMultiplayer( true );
  2994. hltv->ConnectRelay( address );
  2995. }
  2996. CON_COMMAND( tv_stop, "Stops the GOTV broadcast [-instance <inst> ]" )
  2997. {
  2998. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  2999. {
  3000. int nClients = hltv->GetNumClients();
  3001. hltv->Shutdown();
  3002. ConMsg( "GOTV[%u] stopped, %i clients disconnected.\n", hltv.GetIndex(), nClients );
  3003. }
  3004. }
  3005. CON_COMMAND( tv_retry, "Reconnects the GOTV relay proxy " )
  3006. {
  3007. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  3008. {
  3009. if ( hltv->m_bMasterOnlyMode )
  3010. {
  3011. ConMsg( "GOTV[%u] in Master-Only mode.\n", hltv.GetIndex() );
  3012. return;
  3013. }
  3014. if ( !hltv->m_ClientState.m_Remote.Count() )
  3015. {
  3016. ConMsg( "Can't retry, no previous GOTV[%u] connection\n", hltv.GetIndex() );
  3017. return;
  3018. }
  3019. ConMsg( "Commencing GOTV[%u] connection retry to %s\n", hltv.GetIndex(), hltv->m_ClientState.m_Remote.Get( 0 ).m_szRetryAddress.String() );
  3020. Cbuf_AddText( Cbuf_GetCurrentPlayer(), va( "tv_relay %s\n", hltv->m_ClientState.m_Remote.Get( 0 ).m_szRetryAddress.String() ) );
  3021. }
  3022. }
  3023. CON_COMMAND( tv_record, "Starts GOTV demo recording [-instance <inst> ]" )
  3024. {
  3025. if ( args.ArgC() < 2 )
  3026. {
  3027. ConMsg( "Usage: tv_record <filename> [-instance <inst> ]\n" );
  3028. return;
  3029. }
  3030. int nHltvInstance = clamp( args.FindArgInt( "-instance", 0 ), 0, HLTV_SERVER_MAX_COUNT );
  3031. CHLTVServer * hltv = g_pHltvServer[ nHltvInstance ];
  3032. if ( hltv && hltv->IsActive() )
  3033. {
  3034. if ( !hltv->IsMasterProxy() )
  3035. {
  3036. ConMsg( "GOTV[%u]: Only GOTV Master can record demos instantly.\n", nHltvInstance );
  3037. return;
  3038. }
  3039. if ( hltv->m_DemoRecorder.IsRecording() )
  3040. {
  3041. ConMsg( "GOTV[%u] already recording to %s.\n", nHltvInstance, hltv->m_DemoRecorder.GetDemoFilename() );
  3042. return;
  3043. }
  3044. // check path first
  3045. if ( !COM_IsValidPath( args[ 1 ] ) )
  3046. {
  3047. ConMsg( "record %s: invalid path.\n", args[ 1 ] );
  3048. return;
  3049. }
  3050. char name[ MAX_OSPATH ];
  3051. Q_strncpy( name, args[ 1 ], sizeof( name ) );
  3052. // add .dem if not already set by user
  3053. Q_DefaultExtension( name, ".dem", sizeof( name ) );
  3054. bool bConflict = false;
  3055. for ( CHltvServerIterator other; other; other.Next() )
  3056. {
  3057. CHLTVServer *pOtherHltvServer = other;
  3058. if ( pOtherHltvServer != hltv && pOtherHltvServer->IsRecording() && !V_stricmp( pOtherHltvServer->GetRecordingDemoFilename(), name ) )
  3059. {
  3060. Warning( "Cannot record on GOTV[%d]: another GOTV[%d] is currently recording into that file\n", nHltvInstance, pOtherHltvServer->GetInstanceIndex() );
  3061. bConflict = true;
  3062. }
  3063. }
  3064. if ( !bConflict )
  3065. {
  3066. hltv->m_DemoRecorder.StartRecording( name, false );
  3067. }
  3068. }
  3069. else
  3070. {
  3071. ConMsg( "GOTV[%d] is not active\n", nHltvInstance );
  3072. }
  3073. }
  3074. // tv_broadcast change callback
  3075. void OnTvBroadcast( )
  3076. {
  3077. for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() )
  3078. {
  3079. if ( GetIndexedConVar( tv_broadcast, hltv.GetIndex() ).GetBool() )
  3080. {
  3081. if ( hltv->IsTVRelay() )
  3082. {
  3083. Warning( "GOTV[%d] is a relay.", hltv.GetIndex() );
  3084. }
  3085. else
  3086. {
  3087. if ( !hltv->m_Broadcast.IsRecording() )
  3088. {
  3089. hltv->StartBroadcast();
  3090. ConMsg( "Broadcast on GOTV[%d] started\n", hltv.GetIndex() );
  3091. }
  3092. else
  3093. {
  3094. ConMsg( "Broadcast on GOTV[%d] is already active\n", hltv.GetIndex() );
  3095. }
  3096. }
  3097. }
  3098. else
  3099. {
  3100. if ( hltv->m_Broadcast.IsRecording() )
  3101. {
  3102. hltv->m_Broadcast.StopRecording();
  3103. ConMsg( "Broadcast on GOTV[%d] stopped\n", hltv.GetIndex() );
  3104. }
  3105. else
  3106. {
  3107. ConMsg( "Broadcast on GOTV[%d] is not active\n", hltv.GetIndex() );
  3108. }
  3109. }
  3110. }
  3111. }
  3112. void OnTvBroadcast( IConVar *var, const char *pOldValue, float flOldValue ) { OnTvBroadcast(); }
  3113. CON_COMMAND( tv_broadcast_status, "Print out broadcast status" )
  3114. {
  3115. int nActiveServers = 0, nBroadcastingServers = 0;
  3116. for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() )
  3117. {
  3118. nActiveServers++;
  3119. if ( hltv->m_Broadcast.IsRecording() )
  3120. {
  3121. nBroadcastingServers++;
  3122. Msg( "GOTV[%d] is broadcasting to %s: ", hltv.GetIndex(), hltv->m_Broadcast.GetUrl() );
  3123. hltv->m_Broadcast.DumpStats();
  3124. }
  3125. }
  3126. if ( !nBroadcastingServers )
  3127. {
  3128. // print something
  3129. if ( nActiveServers )
  3130. Msg( "GOTV is not broadcasting\n" );
  3131. else
  3132. Msg( "GOTV is not active\n" );
  3133. }
  3134. }
  3135. // tv_stopbroadcast is effectively accomplished by tv_broadcast 0
  3136. CON_COMMAND( tv_stoprecord, "Stops GOTV demo recording [-instance <inst> ]" )
  3137. {
  3138. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  3139. {
  3140. //this is painful, but we need to expand all of the delta frames before we stop recording. That means this console command will cause a big spike in memory
  3141. hltv->StopRecording();
  3142. }
  3143. }
  3144. CON_COMMAND( tv_clients, "Shows list of connected GOTV clients [-instance <inst> ]" )
  3145. {
  3146. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  3147. {
  3148. int nCount = 0;
  3149. ConMsg( "GOTV[%u]\n", hltv.GetIndex() );
  3150. for ( int i = 0; i < hltv->GetClientCount(); i++ )
  3151. {
  3152. CHLTVClient *client = hltv->Client( i );
  3153. INetChannel *netchan = client->GetNetChannel();
  3154. if ( !netchan )
  3155. continue;
  3156. bool bClientIsHLTV = client->IsHLTV();
  3157. char const *szClientHLTVRedirect = bClientIsHLTV ? client->GetUserSetting( "hltv_addr" ) : NULL;
  3158. ConMsg( "ID: %i, \"%s\"%s, Time %s, %s%s%s, In %.1f, Out %.1f.\n",
  3159. client->GetUserID(),
  3160. client->GetClientName(),
  3161. bClientIsHLTV ? " (Relay)" : "",
  3162. COM_FormatSeconds( netchan->GetTimeConnected() ),
  3163. netchan->GetAddress(),
  3164. bClientIsHLTV ? ( szClientHLTVRedirect ? " redirecting to " : " BAD REDIRECT ADDR" ) : "",
  3165. ( bClientIsHLTV && szClientHLTVRedirect ) ? szClientHLTVRedirect : "",
  3166. netchan->GetAvgData( FLOW_INCOMING ) / 1024,
  3167. netchan->GetAvgData( FLOW_OUTGOING ) / 1024 );
  3168. nCount++;
  3169. }
  3170. ConMsg( "--- Total %i connected clients ---\n", nCount );
  3171. }
  3172. }
  3173. CON_COMMAND( tv_msg, "Send a screen message to all clients [-instance <inst> ]" )
  3174. {
  3175. for ( CActiveHltvServerSelector hltv( args ); hltv; hltv.Next() )
  3176. {
  3177. IGameEvent *msg = g_GameEventManager.CreateEvent( "hltv_message", true );
  3178. if ( msg )
  3179. {
  3180. msg->SetString( "text", args.ArgS() );
  3181. hltv->BroadcastEventLocal( msg, false );
  3182. g_GameEventManager.FreeEvent( msg );
  3183. }
  3184. }
  3185. }
  3186. CActiveHltvServerSelector::CActiveHltvServerSelector( const CCommand &args )
  3187. {
  3188. const char *pInstance = args.FindArg( "-instance" );
  3189. m_nIndex = HLTV_SERVER_MAX_COUNT; // by default, iterator is empty/invalid
  3190. m_nMask = 0;
  3191. if ( ( pInstance && !V_stricmp( pInstance, "all" ) ) || args.FindArg( "-all" ) )
  3192. {
  3193. // iterate all active instances
  3194. m_nMask = ( 1 << HLTV_SERVER_MAX_COUNT ) - 1;
  3195. m_nIndex = -1;
  3196. Next();
  3197. if ( m_nIndex >= HLTV_SERVER_MAX_COUNT )
  3198. {
  3199. ConMsg( "No active GOTV instances at this time.\n" );
  3200. }
  3201. }
  3202. else
  3203. {
  3204. int nInstance = pInstance ? V_atoi( pInstance ) : 0; // the default is GOTV[0]
  3205. if ( nInstance >= HLTV_SERVER_MAX_COUNT )
  3206. {
  3207. ConMsg( "GOTV[%d] index out of range.\n", nInstance );
  3208. }
  3209. else if ( g_pHltvServer[ nInstance ] && g_pHltvServer[ nInstance ]->IsActive() )
  3210. {
  3211. m_nMask = 1 << nInstance;
  3212. m_nIndex = nInstance; // valid GOTV instance found
  3213. }
  3214. else
  3215. {
  3216. ConMsg( "GOTV[%d] not active.\n", nInstance );
  3217. }
  3218. }
  3219. }
  3220. CON_COMMAND( tv_mem, "hltv memory statistics" )
  3221. {
  3222. bool bActive = false;
  3223. for ( CActiveHltvServerIterator hltv; hltv; hltv.Next() )
  3224. {
  3225. hltv->DumpMem();
  3226. bActive = true;
  3227. }
  3228. if ( !bActive )
  3229. Msg( "No active GOTV servers found\n" );
  3230. }
  3231. void CHLTVServer::DumpMem()
  3232. {
  3233. Msg( "GOTV[%d] memory consumption:", m_nInstanceIndex );
  3234. uint nHltvFrameSize = 0, nHltvFrameCount = 0;
  3235. for ( CHLTVFrame *pFrame = m_CurrentFrame; pFrame; pFrame = static_cast< CHLTVFrame * > ( pFrame->m_pNext ) )
  3236. {
  3237. nHltvFrameSize += pFrame->GetMemSize();
  3238. nHltvFrameCount++;
  3239. }
  3240. Msg( "%4u Hltv Frames: %10s\n", nHltvFrameCount, V_pretifynum( nHltvFrameSize ) );
  3241. uint nDeltaFrameCount = 0, nDeltaFrameSize = 0;
  3242. for ( SHLTVDeltaFrame_t *pFrame = m_pOldestDeltaFrame; pFrame; pFrame = pFrame->m_pNewerDeltaFrame )
  3243. {
  3244. nDeltaFrameSize += pFrame->GetMemSize();
  3245. nDeltaFrameCount++;
  3246. }
  3247. Msg( "%4u Delta Frames: %10s\n", nDeltaFrameCount, V_pretifynum( nDeltaFrameSize ) );
  3248. // dump packed entity sizes from framesnapshotmanager?
  3249. }
  3250. #ifndef DEDICATED
  3251. //-----------------------------------------------------------------------------
  3252. // Purpose:
  3253. //-----------------------------------------------------------------------------
  3254. void EditDemo_f( const CCommand &args )
  3255. {
  3256. if ( args.ArgC() < 2 )
  3257. {
  3258. Msg ("editdemo <demoname> [-instance <inst>]: edits a demo\n");
  3259. return;
  3260. }
  3261. CActiveHltvServerSelector hltv( args );
  3262. if ( hltv )
  3263. {
  3264. // set current demo player to client demo player
  3265. demoplayer = hltv;
  3266. //
  3267. // open the demo file
  3268. //
  3269. char name[ MAX_OSPATH ];
  3270. Q_strncpy( name, args[ 1 ], sizeof( name ) );
  3271. Q_DefaultExtension( name, ".dem", sizeof( name ) );
  3272. hltv->m_ClientState.m_bSaveMemory = true;
  3273. hltv->StartPlayback( name, false, NULL, -1 );
  3274. }
  3275. }
  3276. CON_COMMAND_AUTOCOMPLETEFILE( editdemo, EditDemo_f, "Edit a recorded demo file (.dem ).", NULL, dem );
  3277. #endif