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

2426 lines
56 KiB

  1. //========= Copyright 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 <tier0/vcrmode.h>
  15. #include <KeyValues.h>
  16. #include <edict.h>
  17. #include <eiface.h>
  18. #include <PlayerState.h>
  19. #include <ihltvdirector.h>
  20. #include <time.h>
  21. #include "hltvserver.h"
  22. #include "sv_client.h"
  23. #include "hltvclient.h"
  24. #include "server.h"
  25. #include "sv_main.h"
  26. #include "framesnapshot.h"
  27. #include "networkstringtable.h"
  28. #include "cmodel_engine.h"
  29. #include "dt_recv_eng.h"
  30. #include "cdll_engine_int.h"
  31. #include "GameEventManager.h"
  32. #include "host.h"
  33. #include "proto_version.h"
  34. #include "proto_oob.h"
  35. #include "dt_common_eng.h"
  36. #include "baseautocompletefilelist.h"
  37. #include "sv_steamauth.h"
  38. #include "tier0/icommandline.h"
  39. #include "sys_dll.h"
  40. // memdbgon must be the last include file in a .cpp file!!!
  41. #include "tier0/memdbgon.h"
  42. #define S2A_EXTRA_DATA_HAS_GAME_PORT 0x80 // Next 2 bytes include the game port.
  43. #define S2A_EXTRA_DATA_HAS_SPECTATOR_DATA 0x40 // Next 2 bytes include the spectator port, then the spectator server name.
  44. #define S2A_EXTRA_DATA_HAS_GAMETAG_DATA 0x20 // Next bytes are the game tag string
  45. #define S2A_EXTRA_DATA_HAS_STEAMID 0x10 // Next 8 bytes are the steamID
  46. #define S2A_EXTRA_DATA_GAMEID 0x01 // Next 8 bytes are the gameID of the server
  47. #define A2S_KEY_STRING_STEAM "Source Engine Query" // required postfix to a A2S_INFO query
  48. extern CNetworkStringTableContainer *networkStringTableContainerClient;
  49. extern ConVar sv_tags;
  50. //////////////////////////////////////////////////////////////////////
  51. // Construction/Destruction
  52. //////////////////////////////////////////////////////////////////////
  53. CHLTVServer *hltv = NULL;
  54. static void tv_title_changed_f( IConVar *var, const char *pOldString, float flOldValue )
  55. {
  56. if ( hltv && hltv->IsActive() )
  57. {
  58. hltv->BroadcastLocalTitle();
  59. }
  60. }
  61. static void tv_name_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  62. {
  63. Steam3Server().NotifyOfServerNameChange();
  64. }
  65. static ConVar tv_maxclients( "tv_maxclients", "128", 0, "Maximum client number on SourceTV server.",
  66. true, 0, true, 255 );
  67. ConVar tv_autorecord( "tv_autorecord", "0", 0, "Automatically records all games as SourceTV demos." );
  68. ConVar tv_name( "tv_name", "SourceTV", 0, "SourceTV host name", tv_name_changed_f );
  69. static ConVar tv_password( "tv_password", "", FCVAR_NOTIFY | FCVAR_PROTECTED | FCVAR_DONTRECORD, "SourceTV password for all clients" );
  70. static ConVar tv_overridemaster( "tv_overridemaster", "0", 0, "Overrides the SourceTV master root address." );
  71. static ConVar tv_dispatchmode( "tv_dispatchmode", "1", 0, "Dispatch clients to relay proxies: 0=never, 1=if appropriate, 2=always" );
  72. ConVar tv_transmitall( "tv_transmitall", "0", FCVAR_REPLICATED, "Transmit all entities (not only director view)" );
  73. ConVar tv_debug( "tv_debug", "0", 0, "SourceTV debug info." );
  74. ConVar tv_title( "tv_title", "SourceTV", 0, "Set title for SourceTV spectator UI", tv_title_changed_f );
  75. static ConVar tv_deltacache( "tv_deltacache", "2", 0, "Enable delta entity bit stream cache" );
  76. static ConVar tv_relayvoice( "tv_relayvoice", "1", 0, "Relay voice data: 0=off, 1=on" );
  77. CDeltaEntityCache::CDeltaEntityCache()
  78. {
  79. Q_memset( m_Cache, 0, sizeof(m_Cache) );
  80. m_nTick = 0;
  81. m_nMaxEntities = 0;
  82. m_nCacheSize = 0;
  83. }
  84. CDeltaEntityCache::~CDeltaEntityCache()
  85. {
  86. Flush();
  87. }
  88. void CDeltaEntityCache::Flush()
  89. {
  90. if ( m_nMaxEntities != 0 )
  91. {
  92. // at least one entity was set
  93. for ( int i=0; i<m_nMaxEntities; i++ )
  94. {
  95. if ( m_Cache[i] != NULL )
  96. {
  97. free( m_Cache[i] );
  98. m_Cache[i] = NULL;
  99. }
  100. }
  101. m_nMaxEntities = 0;
  102. }
  103. m_nCacheSize = 0;
  104. }
  105. void CDeltaEntityCache::SetTick( int nTick, int nMaxEntities )
  106. {
  107. if ( nTick == m_nTick )
  108. return;
  109. Flush();
  110. m_nCacheSize = tv_deltacache.GetInt() * 1024;
  111. if ( m_nCacheSize <= 0 )
  112. return;
  113. m_nMaxEntities = min(nMaxEntities,MAX_EDICTS);
  114. m_nTick = nTick;
  115. }
  116. unsigned char* CDeltaEntityCache::FindDeltaBits( int nEntityIndex, int nDeltaTick, int &nBits )
  117. {
  118. nBits = -1;
  119. if ( nEntityIndex < 0 || nEntityIndex >= m_nMaxEntities )
  120. return NULL;
  121. DeltaEntityEntry_s *pEntry = m_Cache[nEntityIndex];
  122. while ( pEntry )
  123. {
  124. if ( pEntry->nDeltaTick == nDeltaTick )
  125. {
  126. nBits = pEntry->nBits;
  127. return (unsigned char*)(pEntry) + sizeof(DeltaEntityEntry_s);
  128. }
  129. else
  130. {
  131. // keep searching entry list
  132. pEntry = pEntry->pNext;
  133. }
  134. }
  135. return NULL;
  136. }
  137. void CDeltaEntityCache::AddDeltaBits( int nEntityIndex, int nDeltaTick, int nBits, bf_write *pBuffer )
  138. {
  139. if ( nEntityIndex < 0 || nEntityIndex >= m_nMaxEntities || m_nCacheSize <= 0 )
  140. return;
  141. int nBufferSize = PAD_NUMBER( Bits2Bytes(nBits), 4);
  142. DeltaEntityEntry_s *pEntry = m_Cache[nEntityIndex];
  143. if ( pEntry == NULL )
  144. {
  145. if ( (int)(nBufferSize+sizeof(DeltaEntityEntry_s)) > m_nCacheSize )
  146. return; // way too big, don't even create an entry
  147. pEntry = m_Cache[nEntityIndex] = (DeltaEntityEntry_s *) malloc( m_nCacheSize );
  148. }
  149. else
  150. {
  151. char *pEnd = (char*)(pEntry) + m_nCacheSize; // end marker
  152. while( pEntry->pNext )
  153. {
  154. pEntry = pEntry->pNext;
  155. }
  156. int entrySize = sizeof(DeltaEntityEntry_s) + PAD_NUMBER( Bits2Bytes(pEntry->nBits), 4);
  157. DeltaEntityEntry_s *pNew = (DeltaEntityEntry_s*)((char*)(pEntry) + entrySize);
  158. if ( ((char*)(pNew) + sizeof(DeltaEntityEntry_s) + nBufferSize) > pEnd )
  159. return; // data wouldn't fit into cache anymore, don't add new entries
  160. pEntry = pNew;
  161. pEntry->pNext = pEntry;
  162. }
  163. pEntry->pNext = NULL; // link to next
  164. pEntry->nDeltaTick = nDeltaTick;
  165. pEntry->nBits = nBits;
  166. if ( nBits > 0 )
  167. {
  168. bf_read inBuffer;
  169. inBuffer.StartReading( pBuffer->GetData(), pBuffer->m_nDataBytes, pBuffer->GetNumBitsWritten() );
  170. bf_write outBuffer( (char*)(pEntry) + sizeof(DeltaEntityEntry_s), nBufferSize );
  171. outBuffer.WriteBitsFromBuffer( &inBuffer, nBits );
  172. }
  173. }
  174. static RecvTable* FindRecvTable( const char *pName, RecvTable **pRecvTables, int nRecvTables )
  175. {
  176. for ( int i=0; i< nRecvTables; i++ )
  177. {
  178. if ( !Q_strcmp( pName, pRecvTables[i]->GetName() ) )
  179. return pRecvTables[i];
  180. }
  181. return NULL;
  182. }
  183. static RecvTable* AddRecvTableR( SendTable *sendt, RecvTable **pRecvTables, int &nRecvTables )
  184. {
  185. RecvTable *recvt = FindRecvTable( sendt->m_pNetTableName, pRecvTables, nRecvTables );
  186. if ( recvt )
  187. return recvt; // already in list
  188. if ( sendt->m_nProps > 0 )
  189. {
  190. RecvProp *receiveProps = new RecvProp[sendt->m_nProps];
  191. for ( int i=0; i < sendt->m_nProps; i++ )
  192. {
  193. // copy property data
  194. SendProp * sp = sendt->GetProp( i );
  195. RecvProp * rp = &receiveProps[i];
  196. rp->m_pVarName = sp->m_pVarName;
  197. rp->m_RecvType = sp->m_Type;
  198. if ( sp->IsExcludeProp() )
  199. {
  200. // if prop is excluded, give different name
  201. rp->m_pVarName = "IsExcludedProp";
  202. }
  203. if ( sp->IsInsideArray() )
  204. {
  205. rp->SetInsideArray();
  206. rp->m_pVarName = "InsideArrayProp"; // give different name
  207. }
  208. if ( sp->GetType() == DPT_Array )
  209. {
  210. Assert ( sp->GetArrayProp() == sendt->GetProp( i-1 ) );
  211. Assert( receiveProps[i-1].IsInsideArray() );
  212. rp->SetArrayProp( &receiveProps[i-1] );
  213. rp->InitArray( sp->m_nElements, sp->m_ElementStride );
  214. }
  215. if ( sp->GetType() == DPT_DataTable )
  216. {
  217. // recursive create
  218. Assert ( sp->GetDataTable() );
  219. RecvTable *subTable = AddRecvTableR( sp->GetDataTable(), pRecvTables, nRecvTables );
  220. rp->SetDataTable( subTable );
  221. }
  222. }
  223. recvt = new RecvTable( receiveProps, sendt->m_nProps, sendt->m_pNetTableName );
  224. }
  225. else
  226. {
  227. // table with no properties
  228. recvt = new RecvTable( NULL, 0, sendt->m_pNetTableName );
  229. }
  230. pRecvTables[nRecvTables] = recvt;
  231. nRecvTables++;
  232. return recvt;
  233. }
  234. void CHLTVServer::FreeClientRecvTables()
  235. {
  236. for ( int i=0; i< m_nRecvTables; i++ )
  237. {
  238. RecvTable *rt = m_pRecvTables[i];
  239. // delete recv table props
  240. if ( rt->m_pProps )
  241. {
  242. Assert( rt->m_nProps > 0 );
  243. delete [] rt->m_pProps;
  244. }
  245. // delete the table itself
  246. delete rt;
  247. }
  248. Q_memset( m_pRecvTables, 0, sizeof( m_pRecvTables ) );
  249. m_nRecvTables = 0;
  250. }
  251. // creates client receive tables from server send tables
  252. void CHLTVServer::InitClientRecvTables()
  253. {
  254. ServerClass* pCur = NULL;
  255. if ( ClientDLL_GetAllClasses() != NULL )
  256. return; //already initialized
  257. // first create all SendTables
  258. for ( pCur = serverGameDLL->GetAllServerClasses(); pCur; pCur=pCur->m_pNext )
  259. {
  260. // create receive table from send table.
  261. AddRecvTableR( pCur->m_pTable, m_pRecvTables, m_nRecvTables );
  262. ErrorIfNot(
  263. m_nRecvTables < ARRAYSIZE( m_pRecvTables ),
  264. ("AddRecvTableR: overflowed MAX_DATATABLES")
  265. );
  266. }
  267. // now register client classes
  268. for ( pCur = serverGameDLL->GetAllServerClasses(); pCur; pCur=pCur->m_pNext )
  269. {
  270. ErrorIfNot(
  271. m_nRecvTables < ARRAYSIZE( m_pRecvTables ),
  272. ("ClientDLL_InitRecvTableMgr: overflowed MAX_DATATABLES")
  273. );
  274. // find top receive table for class
  275. RecvTable * recvt = FindRecvTable( pCur->m_pTable->GetName(), m_pRecvTables, m_nRecvTables );
  276. Assert ( recvt );
  277. // register class, constructor addes clientClass to g_pClientClassHead list
  278. ClientClass * clientclass = new ClientClass( pCur->m_pNetworkName, NULL, NULL, recvt );
  279. if ( !clientclass )
  280. {
  281. Msg("HLTV_InitRecvTableMgr: failed to allocate client class %s.\n", pCur->m_pNetworkName );
  282. return;
  283. }
  284. }
  285. RecvTable_Init( m_pRecvTables, m_nRecvTables );
  286. }
  287. CHLTVFrame::CHLTVFrame()
  288. {
  289. }
  290. CHLTVFrame::~CHLTVFrame()
  291. {
  292. FreeBuffers();
  293. }
  294. void CHLTVFrame::Reset( void )
  295. {
  296. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  297. {
  298. m_Messages[i].Reset();
  299. }
  300. }
  301. bool CHLTVFrame::HasData( void )
  302. {
  303. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  304. {
  305. if ( m_Messages[i].GetNumBitsWritten() > 0 )
  306. return true;
  307. }
  308. return false;
  309. }
  310. void CHLTVFrame::CopyHLTVData( CHLTVFrame &frame )
  311. {
  312. // copy reliable messages
  313. int bits = frame.m_Messages[HLTV_BUFFER_RELIABLE].GetNumBitsWritten();
  314. if ( bits > 0 )
  315. {
  316. int bytes = PAD_NUMBER( Bits2Bytes(bits), 4 );
  317. m_Messages[HLTV_BUFFER_RELIABLE].StartWriting( new char[ bytes ], bytes, bits );
  318. Q_memcpy( m_Messages[HLTV_BUFFER_RELIABLE].GetBasePointer(), frame.m_Messages[HLTV_BUFFER_RELIABLE].GetBasePointer(), bytes );
  319. }
  320. // copy unreliable messages
  321. bits = frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetNumBitsWritten();
  322. bits += frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetNumBitsWritten();
  323. bits += frame.m_Messages[HLTV_BUFFER_SOUNDS].GetNumBitsWritten();
  324. if ( tv_relayvoice.GetBool() )
  325. bits += frame.m_Messages[HLTV_BUFFER_VOICE].GetNumBitsWritten();
  326. if ( bits > 0 )
  327. {
  328. // collapse all unreliable buffers in one
  329. int bytes = PAD_NUMBER( Bits2Bytes(bits), 4 );
  330. m_Messages[HLTV_BUFFER_UNRELIABLE].StartWriting( new char[ bytes ], bytes );
  331. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetData(), frame.m_Messages[HLTV_BUFFER_UNRELIABLE].GetNumBitsWritten() );
  332. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetData(), frame.m_Messages[HLTV_BUFFER_TEMPENTS].GetNumBitsWritten() );
  333. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_SOUNDS].GetData(), frame.m_Messages[HLTV_BUFFER_SOUNDS].GetNumBitsWritten() );
  334. if ( tv_relayvoice.GetBool() )
  335. m_Messages[HLTV_BUFFER_UNRELIABLE].WriteBits( frame.m_Messages[HLTV_BUFFER_VOICE].GetData(), frame.m_Messages[HLTV_BUFFER_VOICE].GetNumBitsWritten() );
  336. }
  337. }
  338. void CHLTVFrame::AllocBuffers( void )
  339. {
  340. // allocate buffers for input frame
  341. for ( int i=0; i < HLTV_BUFFER_MAX; i++ )
  342. {
  343. Assert( m_Messages[i].GetBasePointer() == NULL );
  344. m_Messages[i].StartWriting( new char[NET_MAX_PAYLOAD], NET_MAX_PAYLOAD);
  345. }
  346. }
  347. void CHLTVFrame::FreeBuffers( void )
  348. {
  349. for ( int i=0; i<HLTV_BUFFER_MAX; i++ )
  350. {
  351. bf_write &msg = m_Messages[i];
  352. if ( msg.GetBasePointer() )
  353. {
  354. delete[] msg.GetBasePointer();
  355. msg.StartWriting( NULL, 0 );
  356. }
  357. }
  358. }
  359. CHLTVServer::CHLTVServer()
  360. {
  361. m_flTickInterval = 0.03;
  362. m_MasterClient = NULL;
  363. m_Server = NULL;
  364. m_Director = NULL;
  365. m_nFirstTick = -1;
  366. m_nLastTick = 0;
  367. m_CurrentFrame = NULL;
  368. m_nViewEntity = 0;
  369. m_nPlayerSlot = 0;
  370. m_bSignonState = false;
  371. m_flStartTime = 0;
  372. m_flFPS = 0;
  373. m_nGameServerMaxClients = 0;
  374. m_fNextSendUpdateTime = 0;
  375. Q_memset( m_pRecvTables, 0, sizeof( m_pRecvTables ) );
  376. m_nRecvTables = 0;
  377. m_vPVSOrigin.Init();
  378. m_nStartTick = 0;
  379. m_bPlayingBack = false;
  380. m_bPlaybackPaused = false;
  381. m_flPlaybackRateModifier = 0;
  382. m_nSkipToTick = 0;
  383. m_bMasterOnlyMode = false;
  384. m_ClientState.m_pHLTV = this;
  385. m_nGlobalSlots = 0;
  386. m_nGlobalClients = 0;
  387. m_nGlobalProxies = 0;
  388. }
  389. CHLTVServer::~CHLTVServer()
  390. {
  391. if ( m_nRecvTables > 0 )
  392. {
  393. RecvTable_Term();
  394. FreeClientRecvTables();
  395. }
  396. // make sure everything was destroyed
  397. Assert( m_CurrentFrame == NULL );
  398. Assert( CountClientFrames() == 0 );
  399. }
  400. void CHLTVServer::SetMaxClients( int number )
  401. {
  402. // allow max clients 0 in HLTV
  403. m_nMaxclients = clamp( number, 0, ABSOLUTE_PLAYER_LIMIT );
  404. }
  405. void CHLTVServer::StartMaster(CGameClient *client)
  406. {
  407. Clear(); // clear old settings & buffers
  408. if ( !client )
  409. {
  410. ConMsg("SourceTV client not found.\n");
  411. return;
  412. }
  413. m_Director = serverGameDirector;
  414. if ( !m_Director )
  415. {
  416. ConMsg("Mod doesn't support SourceTV. No director module found.\n");
  417. return;
  418. }
  419. m_MasterClient = client;
  420. m_MasterClient->m_bIsHLTV = true;
  421. #if defined( REPLAY_ENABLED )
  422. m_MasterClient->m_bIsReplay = false;
  423. #endif
  424. // let game.dll know that we are the HLTV client
  425. Assert( serverGameClients );
  426. CPlayerState *player = serverGameClients->GetPlayerState( m_MasterClient->edict );
  427. player->hltv = true;
  428. m_Server = (CGameServer*)m_MasterClient->GetServer();
  429. // set default user settings
  430. m_MasterClient->m_ConVars->SetString( "name", tv_name.GetString() );
  431. m_MasterClient->m_ConVars->SetString( "cl_team", "1" );
  432. m_MasterClient->m_ConVars->SetString( "rate", "30000" );
  433. m_MasterClient->m_ConVars->SetString( "cl_updaterate", "22" );
  434. m_MasterClient->m_ConVars->SetString( "cl_interp_ratio", "1.0" );
  435. m_MasterClient->m_ConVars->SetString( "cl_predict", "0" );
  436. m_nViewEntity = m_MasterClient->GetPlayerSlot() + 1;
  437. m_nPlayerSlot = m_MasterClient->GetPlayerSlot();
  438. // copy server settings from m_Server
  439. m_nGameServerMaxClients = m_Server->GetMaxClients(); // maxclients is different on proxy (128)
  440. serverclasses = m_Server->serverclasses;
  441. serverclassbits = m_Server->serverclassbits;
  442. V_memcpy( worldmapMD5.bits, m_Server->worldmapMD5.bits, MD5_DIGEST_LENGTH );
  443. m_flTickInterval= m_Server->GetTickInterval();
  444. // allocate buffers for input frame
  445. m_HLTVFrame.AllocBuffers();
  446. InstallStringTables();
  447. // activate director in game.dll
  448. m_Director->SetHLTVServer( this );
  449. // register as listener for mod specific events
  450. const char **modevents = m_Director->GetModEvents();
  451. int j = 0;
  452. while ( modevents[j] != NULL )
  453. {
  454. const char *eventname = modevents[j];
  455. CGameEventDescriptor *descriptor = g_GameEventManager.GetEventDescriptor( eventname );
  456. if ( descriptor )
  457. {
  458. g_GameEventManager.AddListener( this, descriptor, CGameEventManager::CLIENTSTUB );
  459. }
  460. else
  461. {
  462. DevMsg("CHLTVServer::StartMaster: game event %s not found.\n", eventname );
  463. }
  464. j++;
  465. }
  466. // copy signon buffers
  467. m_Signon.StartWriting( m_Server->m_Signon.GetBasePointer(), m_Server->m_Signon.m_nDataBytes,
  468. m_Server->m_Signon.GetNumBitsWritten() );
  469. Q_strncpy( m_szMapname, m_Server->m_szMapname, sizeof(m_szMapname) );
  470. Q_strncpy( m_szSkyname, m_Server->m_szSkyname, sizeof(m_szSkyname) );
  471. NET_ListenSocket( m_Socket, true ); // activated HLTV TCP socket
  472. m_MasterClient->ExecuteStringCommand( "spectate" ); // become a spectator
  473. m_MasterClient->UpdateUserSettings(); // make sure UserInfo is correct
  474. // hack reduce signontick by one to catch changes made in the current tick
  475. m_MasterClient->m_nSignonTick--;
  476. if ( m_bMasterOnlyMode )
  477. {
  478. // we allow only one client in master only mode
  479. tv_maxclients.SetValue( min(1,tv_maxclients.GetInt()) );
  480. }
  481. SetMaxClients( tv_maxclients.GetInt() );
  482. m_bSignonState = false; //master proxy is instantly connected
  483. m_nSpawnCount++;
  484. m_flStartTime = net_time;
  485. m_State = ss_active;
  486. // stop any previous recordings
  487. m_DemoRecorder.StopRecording();
  488. // start new recording if autorecord is enabled
  489. if ( tv_autorecord.GetBool() )
  490. {
  491. m_DemoRecorder.StartAutoRecording();
  492. }
  493. ReconnectClients();
  494. }
  495. void CHLTVServer::StartDemo(const char *filename)
  496. {
  497. }
  498. bool CHLTVServer::DispatchToRelay( CHLTVClient *pClient )
  499. {
  500. if ( tv_dispatchmode.GetInt() <= DISPATCH_MODE_OFF )
  501. return false; // don't redirect
  502. CBaseClient *pBestProxy = NULL;
  503. float fBestRatio = 1.0f;
  504. // find best relay proxy
  505. for (int i=0; i < GetClientCount(); i++ )
  506. {
  507. CBaseClient *pProxy = m_Clients[ i ];
  508. // check all known proxies
  509. if ( !pProxy->IsConnected() || !pProxy->IsHLTV() || (pClient == pProxy) )
  510. continue;
  511. int slots = Q_atoi( pProxy->GetUserSetting( "hltv_slots" ) );
  512. int clients = Q_atoi( pProxy->GetUserSetting( "hltv_clients" ) );
  513. // skip overloaded proxies or proxies with no slots at all
  514. if ( (clients > slots) || slots <= 0 )
  515. continue;
  516. // calc clients/slots ratio for this proxy
  517. float ratio = ((float)(clients))/((float)slots);
  518. if ( ratio < fBestRatio )
  519. {
  520. fBestRatio = ratio;
  521. pBestProxy = pProxy;
  522. }
  523. }
  524. if ( pBestProxy == NULL )
  525. {
  526. if ( tv_dispatchmode.GetInt() == DISPATCH_MODE_ALWAYS )
  527. {
  528. // we are in always forward mode, drop client if we can't forward it
  529. pClient->Disconnect("No SourceTV relay available");
  530. return true;
  531. }
  532. else
  533. {
  534. // just let client connect to this proxy
  535. return false;
  536. }
  537. }
  538. // check if client should stay on this relay server
  539. if ( (tv_dispatchmode.GetInt() == DISPATCH_MODE_AUTO) && (GetMaxClients() > 0) )
  540. {
  541. // ratio = clients/slots. give relay proxies 25% bonus
  542. float myRatio = ((float)GetNumClients()/(float)GetMaxClients()) * 1.25f;
  543. myRatio = min( myRatio, 1.0f ); // clamp to 1
  544. // if we have a better local ratio then other proxies, keep this client here
  545. if ( myRatio < fBestRatio )
  546. return false; // don't redirect
  547. }
  548. const char *pszRelayAddr = pBestProxy->GetUserSetting("hltv_addr");
  549. if ( !pszRelayAddr )
  550. return false;
  551. ConMsg( "Redirecting spectator %s to SourceTV relay %s\n",
  552. pClient->GetNetChannel()->GetRemoteAddress().ToString(),
  553. pszRelayAddr );
  554. // first tell that client that we are a SourceTV server,
  555. // otherwise it's might ignore the command
  556. SVC_ServerInfo serverInfo;
  557. FillServerInfo( serverInfo );
  558. pClient->SendNetMsg( serverInfo, true );
  559. // tell the client to connect to this new address
  560. NET_StringCmd cmdMsg( va("redirect %s\n", pszRelayAddr ) ) ;
  561. pClient->SendNetMsg( cmdMsg, true );
  562. // increase this proxies client number in advance so this proxy isn't used again next time
  563. int clients = Q_atoi( pBestProxy->GetUserSetting( "hltv_clients" ) );
  564. pBestProxy->SetUserCVar( "hltv_clients", va("%d", clients+1 ) );
  565. return true;
  566. }
  567. void CHLTVServer::ConnectRelay(const char *address)
  568. {
  569. if ( m_ClientState.IsConnected() )
  570. {
  571. // do not try to reconnect to old connection
  572. m_ClientState.m_szRetryAddress[0] = 0;
  573. // disconnect first
  574. m_ClientState.Disconnect( "HLTV server connecting to relay", true );
  575. Changelevel(); // inactivate clients
  576. }
  577. // connect to new server
  578. m_ClientState.Connect( address, "tvrelay" );
  579. }
  580. void CHLTVServer::StartRelay()
  581. {
  582. if ( !m_ClientState.IsConnected() && !IsPlayingBack() )
  583. {
  584. DevMsg("StartRelay: not connected.\n");
  585. Shutdown();
  586. return;
  587. }
  588. Clear(); // clear old settings & buffers
  589. if ( m_nRecvTables == 0 )
  590. {
  591. // must be done only once since Mod never changes
  592. InitClientRecvTables();
  593. }
  594. m_HLTVFrame.AllocBuffers();
  595. m_StringTables = &m_NetworkStringTables;
  596. SetMaxClients( tv_maxclients.GetInt() );
  597. m_bSignonState = true;
  598. m_flStartTime = net_time;
  599. m_State = ss_loading;
  600. m_nSpawnCount++;
  601. }
  602. int CHLTVServer::GetHLTVSlot( void )
  603. {
  604. return m_nPlayerSlot;
  605. }
  606. float CHLTVServer::GetOnlineTime( void )
  607. {
  608. return max(0., net_time - m_flStartTime);
  609. }
  610. void CHLTVServer::GetLocalStats( int &proxies, int &slots, int &clients )
  611. {
  612. proxies = GetNumProxies();
  613. clients = GetNumClients();
  614. slots = GetMaxClients();
  615. }
  616. void CHLTVServer::GetRelayStats( int &proxies, int &slots, int &clients )
  617. {
  618. proxies = slots = clients = 0;
  619. for (int i=0 ; i < GetClientCount() ; i++ )
  620. {
  621. CBaseClient *client = m_Clients[ i ];
  622. if ( !client->IsConnected() || !client->IsHLTV() )
  623. continue;
  624. proxies += Q_atoi( client->GetUserSetting( "hltv_proxies" ) );
  625. slots += Q_atoi( client->GetUserSetting( "hltv_slots" ) );
  626. clients += Q_atoi( client->GetUserSetting( "hltv_clients" ) );
  627. }
  628. }
  629. void CHLTVServer::GetGlobalStats( int &proxies, int &slots, int &clients )
  630. {
  631. // the master proxy is the only one that really has all data to generate
  632. // global stats
  633. if ( IsMasterProxy() )
  634. {
  635. GetRelayStats( m_nGlobalProxies, m_nGlobalSlots, m_nGlobalClients );
  636. m_nGlobalSlots += GetMaxClients();
  637. m_nGlobalClients += GetNumClients();
  638. }
  639. // if this is a relay proxies, global data comes via the
  640. // wire from the master proxy
  641. proxies = m_nGlobalProxies;
  642. slots = m_nGlobalSlots;
  643. clients = m_nGlobalClients;
  644. }
  645. const netadr_t *CHLTVServer::GetRelayAddress( void )
  646. {
  647. if ( IsMasterProxy() )
  648. {
  649. return &net_local_adr; // TODO wrong port
  650. }
  651. else if ( m_ClientState.m_NetChannel )
  652. {
  653. return &m_ClientState.m_NetChannel->GetRemoteAddress();
  654. }
  655. else
  656. {
  657. return NULL;
  658. }
  659. }
  660. bool CHLTVServer::IsMasterProxy( void )
  661. {
  662. return ( m_MasterClient != NULL );
  663. }
  664. bool CHLTVServer::IsTVRelay()
  665. {
  666. return !IsMasterProxy();
  667. }
  668. bool CHLTVServer::IsDemoPlayback( void )
  669. {
  670. return false;
  671. }
  672. void CHLTVServer::BroadcastLocalTitle( CHLTVClient *client )
  673. {
  674. IGameEvent *event = g_GameEventManager.CreateEvent( "hltv_title", true );
  675. if ( !event )
  676. return;
  677. event->SetString( "text", tv_title.GetString() );
  678. char buffer_data[MAX_EVENT_BYTES];
  679. SVC_GameEvent eventMsg;
  680. eventMsg.SetReliable( true );
  681. eventMsg.m_DataOut.StartWriting( buffer_data, sizeof(buffer_data) );
  682. // create bit stream from KeyValues
  683. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg.m_DataOut ) )
  684. {
  685. DevMsg("CHLTVServer: failed to serialize title '%s'.\n", event->GetName() );
  686. g_GameEventManager.FreeEvent( event );
  687. return;
  688. }
  689. if ( client )
  690. {
  691. client->SendNetMsg( eventMsg );
  692. }
  693. else
  694. {
  695. for ( int i = 0; i < m_Clients.Count(); i++ )
  696. {
  697. client = Client(i);
  698. if ( !client->IsActive() || client->IsHLTV() )
  699. continue;
  700. client->SendNetMsg( eventMsg );
  701. }
  702. }
  703. g_GameEventManager.FreeEvent( event );
  704. }
  705. void CHLTVServer::BroadcastLocalChat( const char *pszChat, const char *pszGroup )
  706. {
  707. IGameEvent *event = g_GameEventManager.CreateEvent( "hltv_chat", true );
  708. if ( !event )
  709. return;
  710. event->SetString( "text", pszChat );
  711. char buffer_data[MAX_EVENT_BYTES];
  712. SVC_GameEvent eventMsg;
  713. eventMsg.SetReliable( false );
  714. eventMsg.m_DataOut.StartWriting( buffer_data, sizeof(buffer_data) );
  715. // create bit stream from KeyValues
  716. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg.m_DataOut ) )
  717. {
  718. DevMsg("CHLTVServer: failed to serialize chat '%s'.\n", event->GetName() );
  719. g_GameEventManager.FreeEvent( event );
  720. return;
  721. }
  722. for ( int i = 0; i < m_Clients.Count(); i++ )
  723. {
  724. CHLTVClient *cl = Client(i);
  725. if ( !cl->IsActive() || !cl->IsSpawned() || cl->IsHLTV() )
  726. continue;
  727. // if this is a spectator chat message and client disabled it, don't show it
  728. if ( Q_strcmp( cl->m_szChatGroup, pszGroup) || cl->m_bNoChat )
  729. continue;
  730. cl->SendNetMsg( eventMsg );
  731. }
  732. g_GameEventManager.FreeEvent( event );
  733. }
  734. void CHLTVServer::BroadcastEventLocal( IGameEvent *event, bool bReliable )
  735. {
  736. char buffer_data[MAX_EVENT_BYTES];
  737. SVC_GameEvent eventMsg;
  738. eventMsg.SetReliable( bReliable );
  739. eventMsg.m_DataOut.StartWriting( buffer_data, sizeof(buffer_data) );
  740. // create bit stream from KeyValues
  741. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg.m_DataOut ) )
  742. {
  743. DevMsg("CHLTVServer: failed to serialize local event '%s'.\n", event->GetName() );
  744. return;
  745. }
  746. for ( int i = 0; i < m_Clients.Count(); i++ )
  747. {
  748. CHLTVClient *cl = Client(i);
  749. if ( !cl->IsActive() || !cl->IsSpawned() || cl->IsHLTV() )
  750. continue;
  751. if ( !cl->SendNetMsg( eventMsg ) )
  752. {
  753. if ( eventMsg.IsReliable() )
  754. {
  755. DevMsg( "BroadcastMessage: Reliable broadcast message overflow for client %s", cl->GetClientName() );
  756. }
  757. }
  758. }
  759. if ( tv_debug.GetBool() )
  760. Msg("SourceTV broadcast local event: %s\n", event->GetName() );
  761. }
  762. void CHLTVServer::BroadcastEvent(IGameEvent *event)
  763. {
  764. char buffer_data[MAX_EVENT_BYTES];
  765. SVC_GameEvent eventMsg;
  766. eventMsg.m_DataOut.StartWriting( buffer_data, sizeof(buffer_data) );
  767. // create bit stream from KeyValues
  768. if ( !g_GameEventManager.SerializeEvent( event, &eventMsg.m_DataOut ) )
  769. {
  770. DevMsg("CHLTVServer: failed to serialize event '%s'.\n", event->GetName() );
  771. return;
  772. }
  773. BroadcastMessage( eventMsg, true, true );
  774. if ( tv_debug.GetBool() )
  775. Msg("SourceTV broadcast event: %s\n", event->GetName() );
  776. }
  777. void CHLTVServer::FireGameEvent(IGameEvent *event)
  778. {
  779. if ( !IsActive() )
  780. return;
  781. char buffer_data[MAX_EVENT_BYTES];
  782. SVC_GameEvent eventMsg;
  783. eventMsg.m_DataOut.StartWriting( buffer_data, sizeof(buffer_data) );
  784. // create bit stream from KeyValues
  785. if ( g_GameEventManager.SerializeEvent( event, &eventMsg.m_DataOut ) )
  786. {
  787. SendNetMsg( eventMsg );
  788. }
  789. else
  790. {
  791. DevMsg("CHLTVServer::FireGameEvent: failed to serialize event '%s'.\n", event->GetName() );
  792. }
  793. }
  794. bool CHLTVServer::ShouldUpdateMasterServer()
  795. {
  796. // If the main game server is active, then we let it update Steam with the server info.
  797. return !sv.IsActive();
  798. }
  799. CBaseClient *CHLTVServer::CreateNewClient(int slot )
  800. {
  801. return new CHLTVClient( slot, this );
  802. }
  803. void CHLTVServer::InstallStringTables( void )
  804. {
  805. #ifndef SHARED_NET_STRING_TABLES
  806. int numTables = m_Server->m_StringTables->GetNumTables();
  807. m_StringTables = &m_NetworkStringTables;
  808. Assert( m_StringTables->GetNumTables() == 0); // must be empty
  809. m_StringTables->AllowCreation( true );
  810. // master hltv needs to keep a list of changes for all table items
  811. m_StringTables->EnableRollback( true );
  812. for ( int i =0; i<numTables; i++)
  813. {
  814. // iterate through server tables
  815. CNetworkStringTable *serverTable =
  816. (CNetworkStringTable*)m_Server->m_StringTables->GetTable( i );
  817. if ( !serverTable )
  818. continue;
  819. // get matching client table
  820. CNetworkStringTable *hltvTable =
  821. (CNetworkStringTable*)m_StringTables->CreateStringTableEx(
  822. serverTable->GetTableName(),
  823. serverTable->GetMaxStrings(),
  824. serverTable->GetUserDataSize(),
  825. serverTable->GetUserDataSizeBits(),
  826. serverTable->HasFileNameStrings()
  827. );
  828. if ( !hltvTable )
  829. {
  830. DevMsg("SV_InstallHLTVStringTableMirrors! Missing client table \"%s\".\n ", serverTable->GetTableName() );
  831. continue;
  832. }
  833. // make hltv table an exact copy of server table
  834. hltvTable->CopyStringTable( serverTable );
  835. // link hltv table to server table
  836. serverTable->SetMirrorTable( hltvTable );
  837. }
  838. m_StringTables->AllowCreation( false );
  839. #endif
  840. }
  841. void CHLTVServer::RestoreTick( int tick )
  842. {
  843. #ifndef SHARED_NET_STRING_TABLES
  844. // only master proxy delays time
  845. if ( !IsMasterProxy() )
  846. return;
  847. int numTables = m_StringTables->GetNumTables();
  848. for ( int i =0; i<numTables; i++)
  849. {
  850. // iterate through server tables
  851. CNetworkStringTable *pTable = (CNetworkStringTable*) m_StringTables->GetTable( i );
  852. pTable->RestoreTick( tick );
  853. }
  854. #endif
  855. }
  856. void CHLTVServer::UserInfoChanged( int nClientIndex )
  857. {
  858. // don't change UserInfo table, it keeps the infos of the original players
  859. }
  860. void CHLTVServer::LinkInstanceBaselines( void )
  861. {
  862. // Forces to update m_pInstanceBaselineTable.
  863. AUTO_LOCK( g_svInstanceBaselineMutex );
  864. GetInstanceBaselineTable();
  865. Assert( m_pInstanceBaselineTable );
  866. // update all found server classes
  867. for ( ServerClass *pClass = serverGameDLL->GetAllServerClasses(); pClass; pClass=pClass->m_pNext )
  868. {
  869. char idString[32];
  870. Q_snprintf( idString, sizeof( idString ), "%d", pClass->m_ClassID );
  871. // Ok, make a new instance baseline so they can reference it.
  872. int index = m_pInstanceBaselineTable->FindStringIndex( idString );
  873. if ( index != -1 )
  874. {
  875. pClass->m_InstanceBaselineIndex = index;
  876. }
  877. else
  878. {
  879. pClass->m_InstanceBaselineIndex = INVALID_STRING_INDEX;
  880. }
  881. }
  882. }
  883. /* CHLTVServer::GetOriginFromPackedEntity is such a bad, bad hack.
  884. extern float DecodeFloat(SendProp const *pProp, bf_read *pIn);
  885. Vector CHLTVServer::GetOriginFromPackedEntity(PackedEntity* pe)
  886. {
  887. Vector origin; origin.Init();
  888. SendTable *pSendTable = pe->m_pSendTable;
  889. // recursively go down until BaseEntity sendtable
  890. while ( Q_strcmp( pSendTable->GetName(), "DT_BaseEntity") )
  891. {
  892. SendProp *pProp = pSendTable->GetProp( 0 ); // 0 = baseclass
  893. pSendTable = pProp->GetDataTable();
  894. }
  895. for ( int i=0; i < pSendTable->GetNumProps(); i++ )
  896. {
  897. SendProp *pProp = pSendTable->GetProp( i );
  898. if ( Q_strcmp( pProp->GetName(), "m_vecOrigin" ) == 0 )
  899. {
  900. Assert( pProp->GetType() == DPT_Vector );
  901. bf_read buf( pe->LockData(), Bits2Bytes(pe->GetNumBits()), pProp->GetOffset() );
  902. origin[0] = DecodeFloat(pProp, &buf);
  903. origin[1] = DecodeFloat(pProp, &buf);
  904. origin[2] = DecodeFloat(pProp, &buf);
  905. break;
  906. }
  907. }
  908. return origin;
  909. } */
  910. CHLTVEntityData *FindHLTVDataInSnapshot( CFrameSnapshot * pSnapshot, int iEntIndex )
  911. {
  912. int a = 0;
  913. int z = pSnapshot->m_nValidEntities-1;
  914. if ( iEntIndex < pSnapshot->m_pValidEntities[a] ||
  915. iEntIndex > pSnapshot->m_pValidEntities[z] )
  916. return NULL;
  917. while ( a < z )
  918. {
  919. int m = (a+z)/2;
  920. int index = pSnapshot->m_pValidEntities[m];
  921. if ( index == iEntIndex )
  922. return &pSnapshot->m_pHLTVEntityData[m];
  923. if ( iEntIndex > index )
  924. {
  925. if ( pSnapshot->m_pValidEntities[z] == iEntIndex )
  926. return &pSnapshot->m_pHLTVEntityData[z];
  927. if ( a == m )
  928. return NULL;
  929. a = m;
  930. }
  931. else
  932. {
  933. if ( pSnapshot->m_pValidEntities[a] == iEntIndex )
  934. return &pSnapshot->m_pHLTVEntityData[a];
  935. if ( z == m )
  936. return NULL;
  937. z = m;
  938. }
  939. }
  940. return NULL;
  941. }
  942. void CHLTVServer::EntityPVSCheck( CClientFrame *pFrame )
  943. {
  944. byte PVS[PAD_NUMBER( MAX_MAP_CLUSTERS,8 ) / 8];
  945. int nPVSSize = (GetCollisionBSPData()->numclusters + 7) / 8;
  946. // setup engine PVS
  947. SV_ResetPVS( PVS, nPVSSize );
  948. CFrameSnapshot * pSnapshot = pFrame->GetSnapshot();
  949. Assert ( pSnapshot->m_pHLTVEntityData != NULL );
  950. int nDirectorEntity = m_Director->GetPVSEntity();
  951. if ( pSnapshot && nDirectorEntity > 0 )
  952. {
  953. CHLTVEntityData *pHLTVData = FindHLTVDataInSnapshot( pSnapshot, nDirectorEntity );
  954. if ( pHLTVData )
  955. {
  956. m_vPVSOrigin.x = pHLTVData->origin[0];
  957. m_vPVSOrigin.y = pHLTVData->origin[1];
  958. m_vPVSOrigin.z = pHLTVData->origin[2];
  959. }
  960. }
  961. else
  962. {
  963. m_vPVSOrigin = m_Director->GetPVSOrigin();
  964. }
  965. SV_AddOriginToPVS( m_vPVSOrigin );
  966. // know remove all entities that aren't in PVS
  967. int entindex = -1;
  968. while ( true )
  969. {
  970. entindex = pFrame->transmit_entity.FindNextSetBit( entindex+1 );
  971. if ( entindex < 0 )
  972. break;
  973. // is transmit_always is set -> no PVS check
  974. if ( pFrame->transmit_always->Get(entindex) )
  975. {
  976. pFrame->last_entity = entindex;
  977. continue;
  978. }
  979. CHLTVEntityData *pHLTVData = FindHLTVDataInSnapshot( pSnapshot, entindex );
  980. if ( !pHLTVData )
  981. continue;
  982. unsigned int nNodeCluster = pHLTVData->m_nNodeCluster;
  983. // check if node or cluster is in PVS
  984. if ( nNodeCluster & (1<<31) )
  985. {
  986. // it's a node SLOW
  987. nNodeCluster &= ~(1<<31);
  988. if ( CM_HeadnodeVisible( nNodeCluster, PVS, nPVSSize ) )
  989. {
  990. pFrame->last_entity = entindex;
  991. continue;
  992. }
  993. }
  994. else
  995. {
  996. // it's a cluster QUICK
  997. if ( PVS[nNodeCluster >> 3] & (1 << (nNodeCluster & 7)) )
  998. {
  999. pFrame->last_entity = entindex;
  1000. continue;
  1001. }
  1002. }
  1003. // entity is not in PVS, remove from transmit_entity list
  1004. pFrame->transmit_entity.Clear( entindex );
  1005. }
  1006. }
  1007. void CHLTVServer::SignonComplete()
  1008. {
  1009. Assert ( !IsMasterProxy() );
  1010. m_bSignonState = false;
  1011. LinkInstanceBaselines();
  1012. if ( tv_debug.GetBool() )
  1013. Msg("SourceTV signon complete.\n" );
  1014. }
  1015. CClientFrame *CHLTVServer::AddNewFrame( CClientFrame *clientFrame )
  1016. {
  1017. VPROF_BUDGET( "CHLTVServer::AddNewFrame", "HLTV" );
  1018. Assert ( clientFrame );
  1019. Assert( clientFrame->tick_count > m_nLastTick );
  1020. m_nLastTick = clientFrame->tick_count;
  1021. m_HLTVFrame.SetSnapshot( clientFrame->GetSnapshot() );
  1022. m_HLTVFrame.tick_count = clientFrame->tick_count;
  1023. m_HLTVFrame.last_entity = clientFrame->last_entity;
  1024. m_HLTVFrame.transmit_entity = clientFrame->transmit_entity;
  1025. // remember tick of first valid frame
  1026. if ( m_nFirstTick < 0 )
  1027. {
  1028. m_nFirstTick = clientFrame->tick_count;
  1029. m_nTickCount = m_nFirstTick;
  1030. if ( !IsMasterProxy() )
  1031. {
  1032. Assert ( m_State == ss_loading );
  1033. m_State = ss_active; // we are now ready to go
  1034. ReconnectClients();
  1035. ConMsg("SourceTV relay active.\n" );
  1036. Steam3Server().Activate( CSteam3Server::eServerTypeTVRelay );
  1037. Steam3Server().SendUpdatedServerDetails();
  1038. }
  1039. else
  1040. {
  1041. ConMsg("SourceTV broadcast active.\n" );
  1042. }
  1043. }
  1044. CHLTVFrame *hltvFrame = new CHLTVFrame;
  1045. // copy tickcount & entities from client frame
  1046. hltvFrame->CopyFrame( *clientFrame );
  1047. //copy rest (messages, tempents) from current HLTV frame
  1048. hltvFrame->CopyHLTVData( m_HLTVFrame );
  1049. // add frame to HLTV server
  1050. AddClientFrame( hltvFrame );
  1051. if ( IsMasterProxy() && m_DemoRecorder.IsRecording() )
  1052. {
  1053. m_DemoRecorder.WriteFrame( &m_HLTVFrame );
  1054. }
  1055. // reset HLTV frame for recording next messages etc.
  1056. m_HLTVFrame.Reset();
  1057. m_HLTVFrame.SetSnapshot( NULL );
  1058. return hltvFrame;
  1059. }
  1060. void CHLTVServer::SendClientMessages ( bool bSendSnapshots )
  1061. {
  1062. // build individual updates
  1063. for ( int i=0; i< m_Clients.Count(); i++ )
  1064. {
  1065. CHLTVClient* client = Client(i);
  1066. // Update Host client send state...
  1067. if ( !client->ShouldSendMessages() )
  1068. {
  1069. continue;
  1070. }
  1071. // Append the unreliable data (player updates and packet entities)
  1072. if ( m_CurrentFrame && client->IsActive() )
  1073. {
  1074. // don't send same snapshot twice
  1075. client->SendSnapshot( m_CurrentFrame );
  1076. }
  1077. else
  1078. {
  1079. // Connected, but inactive, just send reliable, sequenced info.
  1080. client->m_NetChannel->Transmit();
  1081. }
  1082. client->UpdateSendState();
  1083. client->m_fLastSendTime = net_time;
  1084. }
  1085. }
  1086. void CHLTVServer::UpdateStats( void )
  1087. {
  1088. if ( m_fNextSendUpdateTime > net_time )
  1089. return;
  1090. m_fNextSendUpdateTime = net_time + 8.0f;
  1091. // fire game event for everyone
  1092. IGameEvent *event = NULL;
  1093. if ( !IsMasterProxy() && !m_ClientState.IsConnected() )
  1094. {
  1095. // we are disconnected from SourceTV server
  1096. event = g_GameEventManager.CreateEvent( "hltv_message", true );
  1097. if ( !event )
  1098. return;
  1099. event->SetString( "text", "SourceTV reconnecting ..." );
  1100. }
  1101. else
  1102. {
  1103. int proxies, slots, clients;
  1104. GetGlobalStats( proxies, slots, clients );
  1105. event = g_GameEventManager.CreateEvent( "hltv_status", true );
  1106. if ( !event )
  1107. return;
  1108. char address[32];
  1109. if ( IsMasterProxy() || tv_overridemaster.GetBool() )
  1110. {
  1111. // broadcast own address
  1112. Q_snprintf( address, sizeof(address), "%s:%u", net_local_adr.ToString(true), GetUDPPort() );
  1113. }
  1114. else
  1115. {
  1116. // forward address
  1117. Q_snprintf( address, sizeof(address), "%s", m_RootServer.ToString() );
  1118. }
  1119. event->SetString( "master", address );
  1120. event->SetInt( "clients", clients );
  1121. event->SetInt( "slots", slots);
  1122. event->SetInt( "proxies", proxies );
  1123. }
  1124. if ( IsMasterProxy() )
  1125. {
  1126. // as a master fire event for every one
  1127. g_GameEventManager.FireEvent( event );
  1128. }
  1129. else
  1130. {
  1131. // as a relay proxy just broadcast event
  1132. BroadcastEvent( event );
  1133. }
  1134. }
  1135. bool CHLTVServer::SendNetMsg( INetMessage &msg, bool bForceReliable )
  1136. {
  1137. if ( m_bSignonState )
  1138. {
  1139. return msg.WriteToBuffer( m_Signon );
  1140. }
  1141. int buffer = HLTV_BUFFER_UNRELIABLE; // default destination
  1142. if ( msg.IsReliable() )
  1143. {
  1144. buffer = HLTV_BUFFER_RELIABLE;
  1145. }
  1146. else if ( msg.GetType() == svc_Sounds )
  1147. {
  1148. buffer = HLTV_BUFFER_SOUNDS;
  1149. }
  1150. else if ( msg.GetType() == svc_VoiceData )
  1151. {
  1152. buffer = HLTV_BUFFER_VOICE;
  1153. }
  1154. else if ( msg.GetType() == svc_TempEntities )
  1155. {
  1156. buffer = HLTV_BUFFER_TEMPENTS;
  1157. }
  1158. // anything else goes to the unreliable bin
  1159. return msg.WriteToBuffer( m_HLTVFrame.m_Messages[buffer] );
  1160. }
  1161. bf_write *CHLTVServer::GetBuffer( int nBuffer )
  1162. {
  1163. if ( nBuffer < 0 || nBuffer >= HLTV_BUFFER_MAX )
  1164. return NULL;
  1165. return &m_HLTVFrame.m_Messages[nBuffer];
  1166. }
  1167. IServer *CHLTVServer::GetBaseServer()
  1168. {
  1169. return (IServer*)this;
  1170. }
  1171. IHLTVDirector *CHLTVServer::GetDirector()
  1172. {
  1173. return m_Director;
  1174. }
  1175. CClientFrame *CHLTVServer::GetDeltaFrame( int nTick )
  1176. {
  1177. if ( !tv_deltacache.GetBool() )
  1178. return GetClientFrame( nTick ); //expensive
  1179. // TODO make that a utlmap
  1180. FOR_EACH_VEC( m_FrameCache, iFrame )
  1181. {
  1182. if ( m_FrameCache[iFrame].nTick == nTick )
  1183. return m_FrameCache[iFrame].pFrame;
  1184. }
  1185. int i = m_FrameCache.AddToTail();
  1186. CFrameCacheEntry_s &entry = m_FrameCache[i];
  1187. entry.nTick = nTick;
  1188. entry.pFrame = GetClientFrame( nTick ); //expensive
  1189. return entry.pFrame;
  1190. }
  1191. void CHLTVServer::RunFrame()
  1192. {
  1193. VPROF_BUDGET( "CHLTVServer::RunFrame", "HLTV" );
  1194. // update network time etc
  1195. NET_RunFrame( Plat_FloatTime() );
  1196. if ( m_ClientState.m_nSignonState > SIGNONSTATE_NONE )
  1197. {
  1198. // process data from net socket
  1199. NET_ProcessSocket( m_ClientState.m_Socket, &m_ClientState );
  1200. m_ClientState.RunFrame();
  1201. m_ClientState.SendPacket();
  1202. }
  1203. // check if HLTV server if active
  1204. if ( !IsActive() )
  1205. return;
  1206. if ( host_frametime > 0 )
  1207. {
  1208. m_flFPS = m_flFPS * 0.99f + 0.01f/host_frametime;
  1209. }
  1210. if ( IsPlayingBack() )
  1211. return;
  1212. // get current tick time for director module and restore
  1213. // world (stringtables, framebuffers) as they were at this time
  1214. UpdateTick();
  1215. // Run any commands from client and play client Think functions if it is time.
  1216. CBaseServer::RunFrame();
  1217. UpdateStats();
  1218. SendClientMessages( true );
  1219. // Update the Steam server if we're running a relay.
  1220. if ( !sv.IsActive() )
  1221. Steam3Server().RunFrame();
  1222. UpdateMasterServer();
  1223. }
  1224. void CHLTVServer::UpdateTick( void )
  1225. {
  1226. VPROF_BUDGET( "CHLTVServer::UpdateTick", "HLTV" );
  1227. if ( m_nFirstTick < 0 )
  1228. {
  1229. m_nTickCount = 0;
  1230. m_CurrentFrame = NULL;
  1231. return;
  1232. }
  1233. // set tick time to last frame added
  1234. int nNewTick = m_nLastTick;
  1235. if ( IsMasterProxy() )
  1236. {
  1237. // get tick from director, he decides delay etc
  1238. nNewTick = max( m_nFirstTick, m_Director->GetDirectorTick() );
  1239. }
  1240. // the the closest available frame
  1241. CHLTVFrame *newFrame = (CHLTVFrame*) GetClientFrame( nNewTick, false );
  1242. if ( newFrame == NULL )
  1243. return; // we dont have a new frame
  1244. if ( m_CurrentFrame == newFrame )
  1245. return; // current frame didn't change
  1246. m_CurrentFrame = newFrame;
  1247. m_nTickCount = m_CurrentFrame->tick_count;
  1248. if ( IsMasterProxy() )
  1249. {
  1250. // now do master proxy stuff
  1251. // restore string tables for this time
  1252. RestoreTick( m_nTickCount );
  1253. // remove entities out of current PVS
  1254. if ( tv_transmitall.GetBool() == false )
  1255. {
  1256. EntityPVSCheck( m_CurrentFrame );
  1257. }
  1258. }
  1259. else
  1260. {
  1261. // delta entity cache works only for relay proxies
  1262. m_DeltaCache.SetTick( m_CurrentFrame->tick_count, m_CurrentFrame->last_entity+1 );
  1263. }
  1264. int removeTick = m_nTickCount - 16.0f/m_flTickInterval; // keep 16 seconds buffer
  1265. if ( removeTick > 0 )
  1266. {
  1267. DeleteClientFrames( removeTick );
  1268. }
  1269. m_FrameCache.RemoveAll();
  1270. }
  1271. const char *CHLTVServer::GetName( void ) const
  1272. {
  1273. return tv_name.GetString();
  1274. }
  1275. void CHLTVServer::FillServerInfo(SVC_ServerInfo &serverinfo)
  1276. {
  1277. CBaseServer::FillServerInfo( serverinfo );
  1278. serverinfo.m_nPlayerSlot = m_nPlayerSlot; // all spectators think they're the HLTV client
  1279. serverinfo.m_nMaxClients = m_nGameServerMaxClients;
  1280. }
  1281. void CHLTVServer::Clear( void )
  1282. {
  1283. CBaseServer::Clear();
  1284. m_Director = NULL;
  1285. m_MasterClient = NULL;
  1286. m_ClientState.Clear();
  1287. m_Server = NULL;
  1288. m_nFirstTick = -1;
  1289. m_nLastTick = 0;
  1290. m_nTickCount = 0;
  1291. m_CurrentFrame = NULL;
  1292. m_nPlayerSlot = 0;
  1293. m_flStartTime = 0.0f;
  1294. m_nViewEntity = 1;
  1295. m_nGameServerMaxClients = 0;
  1296. m_fNextSendUpdateTime = 0.0f;
  1297. m_HLTVFrame.FreeBuffers();
  1298. m_vPVSOrigin.Init();
  1299. DeleteClientFrames( -1 );
  1300. m_DeltaCache.Flush();
  1301. m_FrameCache.RemoveAll();
  1302. }
  1303. bool CHLTVServer::ProcessConnectionlessPacket( netpacket_t * packet )
  1304. {
  1305. bf_read msg = packet->message; // We're copying the message, so we don't need to seek back when passing packet to the base class.
  1306. // int bits = msg.GetNumBitsRead();
  1307. char c = msg.ReadChar();
  1308. if ( c == 0 )
  1309. {
  1310. return false;
  1311. }
  1312. switch ( c )
  1313. {
  1314. #ifndef NO_STEAM
  1315. case A2S_INFO:
  1316. char rgchInfoPostfix[64];
  1317. msg.ReadString( rgchInfoPostfix, sizeof( rgchInfoPostfix ) );
  1318. if ( !Q_stricmp( rgchInfoPostfix, A2S_KEY_STRING_STEAM ) )
  1319. {
  1320. ReplyInfo( packet->from );
  1321. return true;
  1322. }
  1323. break;
  1324. //case A2S_PLAYER:
  1325. // return true;
  1326. #endif // #ifndef NO_STEAM
  1327. }
  1328. return CBaseServer::ProcessConnectionlessPacket( packet );
  1329. }
  1330. void CHLTVServer::Init(bool bIsDedicated)
  1331. {
  1332. CBaseServer::Init( bIsDedicated );
  1333. m_Socket = NS_HLTV;
  1334. // check if only master proxy is allowed, no broadcasting
  1335. if ( CommandLine()->FindParm("-tvmasteronly") )
  1336. {
  1337. m_bMasterOnlyMode = true;
  1338. }
  1339. }
  1340. void CHLTVServer::Changelevel()
  1341. {
  1342. m_DemoRecorder.StopRecording();
  1343. InactivateClients();
  1344. DeleteClientFrames(-1);
  1345. m_CurrentFrame = NULL;
  1346. }
  1347. void CHLTVServer::GetNetStats( float &avgIn, float &avgOut )
  1348. {
  1349. CBaseServer::GetNetStats( avgIn, avgOut );
  1350. if ( m_ClientState.IsActive() )
  1351. {
  1352. avgIn += m_ClientState.m_NetChannel->GetAvgData(FLOW_INCOMING);
  1353. avgOut += m_ClientState.m_NetChannel->GetAvgData(FLOW_OUTGOING);
  1354. }
  1355. }
  1356. void CHLTVServer::Shutdown( void )
  1357. {
  1358. m_DemoRecorder.StopRecording(); // if recording, stop now
  1359. if ( IsMasterProxy() )
  1360. {
  1361. if ( m_MasterClient )
  1362. m_MasterClient->Disconnect( "SourceTV stop." );
  1363. if ( m_Director )
  1364. m_Director->SetHLTVServer( NULL );
  1365. }
  1366. else
  1367. {
  1368. // do not try to reconnect to old connection
  1369. m_ClientState.m_szRetryAddress[0] = 0;
  1370. m_ClientState.Disconnect( "HLTV server shutting down", true );
  1371. }
  1372. g_GameEventManager.RemoveListener( this );
  1373. CBaseServer::Shutdown();
  1374. }
  1375. CDemoFile *CHLTVServer::GetDemoFile()
  1376. {
  1377. return &m_DemoFile;
  1378. }
  1379. //-----------------------------------------------------------------------------
  1380. // Purpose:
  1381. // Output : Returns true on success, false on failure.
  1382. //-----------------------------------------------------------------------------
  1383. bool CHLTVServer::IsPlayingBack( void )
  1384. {
  1385. return m_bPlayingBack;
  1386. }
  1387. bool CHLTVServer::IsPlaybackPaused()
  1388. {
  1389. return m_bPlaybackPaused;
  1390. }
  1391. float CHLTVServer::GetPlaybackTimeScale()
  1392. {
  1393. return m_flPlaybackRateModifier;
  1394. }
  1395. void CHLTVServer::SetPlaybackTimeScale(float timescale)
  1396. {
  1397. m_flPlaybackRateModifier = timescale;
  1398. }
  1399. void CHLTVServer::ResyncDemoClock()
  1400. {
  1401. m_nStartTick = host_tickcount;
  1402. }
  1403. int CHLTVServer::GetPlaybackStartTick( void )
  1404. {
  1405. return m_nStartTick;
  1406. }
  1407. int CHLTVServer::GetPlaybackTick( void )
  1408. {
  1409. return host_tickcount - m_nStartTick;
  1410. }
  1411. int CHLTVServer::GetTotalTicks(void)
  1412. {
  1413. return m_DemoFile.m_DemoHeader.playback_ticks;
  1414. }
  1415. bool CHLTVServer::StartPlayback( const char *filename, bool bAsTimeDemo )
  1416. {
  1417. Clear();
  1418. if ( !m_DemoFile.Open( filename, true ) )
  1419. {
  1420. return false;
  1421. }
  1422. // Read in the m_DemoHeader
  1423. demoheader_t *dh = m_DemoFile.ReadDemoHeader();
  1424. if ( !dh )
  1425. {
  1426. ConMsg( "Failed to read demo header.\n" );
  1427. m_DemoFile.Close();
  1428. return false;
  1429. }
  1430. // create a fake channel with a NULL address
  1431. m_ClientState.m_NetChannel = NET_CreateNetChannel( NS_CLIENT, NULL, "DEMO", &m_ClientState );
  1432. if ( !m_ClientState.m_NetChannel )
  1433. {
  1434. ConMsg( "CDemo::Play: failed to create demo net channel\n" );
  1435. m_DemoFile.Close();
  1436. return false;
  1437. }
  1438. m_ClientState.m_NetChannel->SetTimeout( -1.0f ); // never timeout
  1439. // Now read in the directory structure.
  1440. m_bPlayingBack = true;
  1441. ConMsg( "Reading complete demo file at once...\n");
  1442. double start = Plat_FloatTime();
  1443. ReadCompleteDemoFile();
  1444. double diff = Plat_FloatTime() - start;
  1445. ConMsg( "Reading time :%.4f\n", diff );
  1446. NET_RemoveNetChannel( m_ClientState.m_NetChannel, true );
  1447. m_ClientState.m_NetChannel = NULL;
  1448. return true;
  1449. }
  1450. void CHLTVServer::ReadCompleteDemoFile()
  1451. {
  1452. int tick = 0;
  1453. byte cmd = dem_signon;
  1454. char buffer[NET_MAX_PAYLOAD];
  1455. netpacket_t demoPacket;
  1456. // setup demo packet data buffer
  1457. Q_memset( &demoPacket, 0, sizeof(demoPacket) );
  1458. demoPacket.from.SetType( NA_LOOPBACK);
  1459. while ( true )
  1460. {
  1461. m_DemoFile.ReadCmdHeader( cmd, tick );
  1462. // COMMAND HANDLERS
  1463. switch ( cmd )
  1464. {
  1465. case dem_synctick:
  1466. ResyncDemoClock();
  1467. break;
  1468. case dem_stop:
  1469. // MOTODO we finished reading the file
  1470. return ;
  1471. case dem_consolecmd:
  1472. {
  1473. NET_StringCmd cmdmsg( m_DemoFile.ReadConsoleCommand() );
  1474. m_ClientState.ProcessStringCmd( &cmdmsg );
  1475. }
  1476. break;
  1477. case dem_datatables:
  1478. {
  1479. ALIGN4 char data[64*1024] ALIGN4_POST;
  1480. bf_read buf( "dem_datatables", data, sizeof(data) );
  1481. m_DemoFile.ReadNetworkDataTables( &buf );
  1482. buf.Seek( 0 );
  1483. // support for older engine demos
  1484. if ( !DataTable_LoadDataTablesFromBuffer( &buf, m_DemoFile.m_DemoHeader.demoprotocol ) )
  1485. {
  1486. Host_Error( "Error parsing network data tables during demo playback." );
  1487. }
  1488. }
  1489. break;
  1490. case dem_stringtables:
  1491. {
  1492. void *data = NULL;
  1493. int dataLen = 512 * 1024;
  1494. while ( dataLen <= DEMO_FILE_MAX_STRINGTABLE_SIZE )
  1495. {
  1496. data = realloc( data, dataLen );
  1497. bf_read buf( "dem_stringtables", data, dataLen );
  1498. // did we successfully read
  1499. if ( m_DemoFile.ReadStringTables( &buf ) > 0 )
  1500. {
  1501. buf.Seek( 0 );
  1502. if ( !networkStringTableContainerClient->ReadStringTables( buf ) )
  1503. {
  1504. Host_Error( "Error parsing string tables during demo playback." );
  1505. }
  1506. break;
  1507. }
  1508. // Didn't fit. Try doubling the size of the buffer
  1509. dataLen *= 2;
  1510. }
  1511. if ( dataLen > DEMO_FILE_MAX_STRINGTABLE_SIZE )
  1512. {
  1513. Warning( "ReadCompleteDemoFile failed to read string tables. Trying to read string tables that's bigger than max string table size\n" );
  1514. }
  1515. free( data );
  1516. }
  1517. break;
  1518. case dem_usercmd:
  1519. {
  1520. char bufferIn[256];
  1521. int length = sizeof( bufferIn );
  1522. m_DemoFile.ReadUserCmd( bufferIn, length );
  1523. // MOTODO HLTV must store user commands too
  1524. }
  1525. break;
  1526. case dem_signon:
  1527. case dem_packet:
  1528. {
  1529. int inseq, outseqack = 0;
  1530. m_DemoFile.ReadCmdInfo( m_LastCmdInfo ); // MOTODO must be stored somewhere
  1531. m_DemoFile.ReadSequenceInfo( inseq, outseqack );
  1532. int length = m_DemoFile.ReadRawData( buffer, sizeof(buffer) );
  1533. if ( length > 0 )
  1534. {
  1535. // succsessfully read new demopacket
  1536. demoPacket.received = realtime;
  1537. demoPacket.size = length;
  1538. demoPacket.message.StartReading( buffer, length );
  1539. m_ClientState.m_NetChannel->ProcessPacket( &demoPacket, false );
  1540. }
  1541. }
  1542. break;
  1543. }
  1544. }
  1545. }
  1546. int CHLTVServer::GetChallengeType ( netadr_t &adr )
  1547. {
  1548. return PROTOCOL_HASHEDCDKEY; // HLTV doesn't need Steam authentication
  1549. }
  1550. const char *CHLTVServer::GetPassword() const
  1551. {
  1552. const char *password = tv_password.GetString();
  1553. // if password is empty or "none", return NULL
  1554. if ( !password[0] || !Q_stricmp(password, "none" ) )
  1555. {
  1556. return NULL;
  1557. }
  1558. return password;
  1559. }
  1560. IClient *CHLTVServer::ConnectClient ( netadr_t &adr, int protocol, int challenge, int clientChallenge, int authProtocol,
  1561. const char *name, const char *password, const char *hashedCDkey, int cdKeyLen )
  1562. {
  1563. IClient *client = (CHLTVClient*)CBaseServer::ConnectClient(
  1564. adr, protocol, challenge, clientChallenge, authProtocol, name, password, hashedCDkey, cdKeyLen );
  1565. if ( client )
  1566. {
  1567. // remember password
  1568. CHLTVClient *pHltvClient = (CHLTVClient*)client;
  1569. Q_strncpy( pHltvClient->m_szPassword, password, sizeof(pHltvClient->m_szPassword) );
  1570. }
  1571. return client;
  1572. }
  1573. int CHLTVServer::GetProtocolVersion()
  1574. {
  1575. if ( GetDemoFile() )
  1576. return GetDemoFile()->GetProtocolVersion();
  1577. return PROTOCOL_VERSION;
  1578. }
  1579. #ifndef NO_STEAM
  1580. void CHLTVServer::ReplyInfo( const netadr_t &adr )
  1581. {
  1582. static char gamedir[MAX_OSPATH];
  1583. Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
  1584. CUtlBuffer buf;
  1585. buf.EnsureCapacity( 2048 );
  1586. buf.PutUnsignedInt( LittleDWord( CONNECTIONLESS_HEADER ) );
  1587. buf.PutUnsignedChar( S2A_INFO_SRC );
  1588. buf.PutUnsignedChar( GetProtocolVersion() ); // Hardcoded protocol version number
  1589. buf.PutString( GetName() );
  1590. buf.PutString( GetMapName() );
  1591. buf.PutString( gamedir );
  1592. buf.PutString( serverGameDLL->GetGameDescription() );
  1593. // The next field is a 16-bit version of the AppID. If our AppID < 65536,
  1594. // then let's go ahead and put in in there, to maximize compatibility
  1595. // with old clients who might be only using this field but not the new one.
  1596. // However, if our AppID won't fit, there's no way we can be compatible,
  1597. // anyway, so just put in a zero, which is better than a bogus AppID.
  1598. uint16 usAppIdShort = (uint16)GetSteamAppID();
  1599. if ( (AppId_t)usAppIdShort != GetSteamAppID() )
  1600. {
  1601. usAppIdShort = 0;
  1602. }
  1603. buf.PutShort( LittleWord( usAppIdShort ) );
  1604. // player info
  1605. buf.PutUnsignedChar( GetNumClients() );
  1606. buf.PutUnsignedChar( GetMaxClients() );
  1607. buf.PutUnsignedChar( 0 );
  1608. // NOTE: This key's meaning is changed in the new version. Since we send gameport and specport,
  1609. // it knows whether we're running SourceTV or not. Then it only needs to know if we're a dedicated or listen server.
  1610. if ( IsDedicated() )
  1611. buf.PutUnsignedChar( 'd' ); // d = dedicated server
  1612. else
  1613. buf.PutUnsignedChar( 'l' ); // l = listen server
  1614. #if defined(_WIN32)
  1615. buf.PutUnsignedChar( 'w' );
  1616. #elif defined(OSX)
  1617. buf.PutUnsignedChar( 'm' );
  1618. #else // LINUX?
  1619. buf.PutUnsignedChar( 'l' );
  1620. #endif
  1621. // Password?
  1622. buf.PutUnsignedChar( GetPassword() != NULL ? 1 : 0 );
  1623. buf.PutUnsignedChar( Steam3Server().BSecure() ? 1 : 0 );
  1624. buf.PutString( GetSteamInfIDVersionInfo().szVersionString );
  1625. //
  1626. // NEW DATA.
  1627. //
  1628. // Write a byte with some flags that describe what is to follow.
  1629. const char *pchTags = sv_tags.GetString();
  1630. byte nNewFlags = 0;
  1631. //if ( GetGamePort() != 0 )
  1632. // nNewFlags |= S2A_EXTRA_DATA_HAS_GAME_PORT;
  1633. if ( Steam3Server().GetGSSteamID().IsValid() )
  1634. nNewFlags |= S2A_EXTRA_DATA_HAS_STEAMID;
  1635. if ( GetUDPPort() != 0 )
  1636. nNewFlags |= S2A_EXTRA_DATA_HAS_SPECTATOR_DATA;
  1637. if ( pchTags && pchTags[0] != '\0' )
  1638. nNewFlags |= S2A_EXTRA_DATA_HAS_GAMETAG_DATA;
  1639. nNewFlags |= S2A_EXTRA_DATA_GAMEID;
  1640. buf.PutUnsignedChar( nNewFlags );
  1641. // Write the rest of the data.
  1642. //if ( nNewFlags & S2A_EXTRA_DATA_HAS_GAME_PORT )
  1643. //{
  1644. // buf.PutShort( LittleWord( GetGamePort() ) );
  1645. //}
  1646. if ( nNewFlags & S2A_EXTRA_DATA_HAS_STEAMID )
  1647. {
  1648. buf.PutUint64( LittleQWord( Steam3Server().GetGSSteamID().ConvertToUint64() ) );
  1649. }
  1650. if ( nNewFlags & S2A_EXTRA_DATA_HAS_SPECTATOR_DATA )
  1651. {
  1652. buf.PutShort( LittleWord( GetUDPPort() ) );
  1653. buf.PutString( GetName() );
  1654. }
  1655. if ( nNewFlags & S2A_EXTRA_DATA_HAS_GAMETAG_DATA )
  1656. {
  1657. buf.PutString( pchTags );
  1658. }
  1659. if ( nNewFlags & S2A_EXTRA_DATA_GAMEID )
  1660. {
  1661. // !FIXME! Is there a reason we aren't using the other half
  1662. // of this field? Shouldn't we put the game mod ID in there, too?
  1663. // We have the game dir.
  1664. buf.PutUint64( LittleQWord( CGameID( GetSteamAppID() ).ToUint64() ) );
  1665. }
  1666. NET_SendPacket( NULL, m_Socket, adr, (unsigned char *)buf.Base(), buf.TellPut() );
  1667. }
  1668. #endif // #ifndef NO_STEAM
  1669. CON_COMMAND( tv_status, "Show SourceTV server status." )
  1670. {
  1671. int slots, proxies, clients;
  1672. float in, out;
  1673. char gd[MAX_OSPATH];
  1674. Q_FileBase( com_gamedir, gd, sizeof( gd ) );
  1675. if ( !hltv || !hltv->IsActive() )
  1676. {
  1677. ConMsg("SourceTV not active.\n" );
  1678. return;
  1679. }
  1680. hltv->GetNetStats( in, out );
  1681. in /= 1024; // as KB
  1682. out /= 1024;
  1683. ConMsg("--- SourceTV Status ---\n");
  1684. ConMsg("Online %s, FPS %.1f, Version %i (%s)\n",
  1685. COM_FormatSeconds( hltv->GetOnlineTime() ), hltv->m_flFPS, build_number(),
  1686. #ifdef _WIN32
  1687. "Win32" );
  1688. #else
  1689. "Linux" );
  1690. #endif
  1691. if ( hltv->IsDemoPlayback() )
  1692. {
  1693. ConMsg("Playing Demo File \"%s\"\n", "TODO demo file name" );
  1694. }
  1695. else if ( hltv->IsMasterProxy() )
  1696. {
  1697. ConMsg("Master \"%s\", delay %.0f\n", hltv->GetName(), hltv->GetDirector()->GetDelay() );
  1698. }
  1699. else // if ( m_Server->IsRelayProxy() )
  1700. {
  1701. if ( hltv->GetRelayAddress() )
  1702. {
  1703. ConMsg("Relay \"%s\", connect to %s\n", hltv->GetName(), hltv->GetRelayAddress()->ToString() );
  1704. }
  1705. else
  1706. {
  1707. ConMsg("Relay \"%s\", not connect.\n", hltv->GetName() );
  1708. }
  1709. }
  1710. ConMsg("Game Time %s, Mod \"%s\", Map \"%s\", Players %i\n", COM_FormatSeconds( hltv->GetTime() ),
  1711. gd, hltv->GetMapName(), hltv->GetNumPlayers() );
  1712. ConMsg("Local IP %s:%i, KB/sec In %.1f, Out %.1f\n",
  1713. net_local_adr.ToString( true ), hltv->GetUDPPort(), in ,out );
  1714. hltv->GetLocalStats( proxies, slots, clients );
  1715. ConMsg("Local Slots %i, Spectators %i, Proxies %i\n",
  1716. slots, clients-proxies, proxies );
  1717. hltv->GetGlobalStats( proxies, slots, clients);
  1718. ConMsg("Total Slots %i, Spectators %i, Proxies %i\n",
  1719. slots, clients-proxies, proxies);
  1720. if ( hltv->m_DemoRecorder.IsRecording() )
  1721. {
  1722. ConMsg("Recording to \"%s\", length %s.\n", hltv->m_DemoRecorder.GetDemoFile()->m_szFileName,
  1723. COM_FormatSeconds( host_state.interval_per_tick * hltv->m_DemoRecorder.GetRecordingTick() ) );
  1724. }
  1725. }
  1726. CON_COMMAND( tv_relay, "Connect to SourceTV server and relay broadcast." )
  1727. {
  1728. if ( args.ArgC() < 2 )
  1729. {
  1730. ConMsg( "Usage: tv_relay <ip:port>\n" );
  1731. return;
  1732. }
  1733. const char *address = args.ArgS();
  1734. // If it's not a single player connection to "localhost", initialize networking & stop listenserver
  1735. if ( !Q_strncmp( address, "localhost", 9 ) )
  1736. {
  1737. ConMsg( "SourceTV can't connect to localhost.\n" );
  1738. return;
  1739. }
  1740. if ( !hltv )
  1741. {
  1742. hltv = new CHLTVServer;
  1743. hltv->Init( NET_IsDedicated() );
  1744. }
  1745. if ( hltv->m_bMasterOnlyMode )
  1746. {
  1747. ConMsg("SourceTV in Master-Only mode.\n" );
  1748. return;
  1749. }
  1750. // shutdown anything else
  1751. Host_Disconnect( false );
  1752. // start networking
  1753. NET_SetMutiplayer( true );
  1754. hltv->ConnectRelay( address );
  1755. }
  1756. CON_COMMAND( tv_stop, "Stops the SourceTV broadcast." )
  1757. {
  1758. if ( !hltv || !hltv->IsActive() )
  1759. {
  1760. ConMsg("SourceTV not active.\n" );
  1761. return;
  1762. }
  1763. int nClients = hltv->GetNumClients();
  1764. hltv->Shutdown();
  1765. ConMsg("SourceTV stopped, %i clients disconnected.\n", nClients );
  1766. }
  1767. CON_COMMAND( tv_retry, "Reconnects the SourceTV relay proxy." )
  1768. {
  1769. if ( !hltv )
  1770. {
  1771. ConMsg("SourceTV not active.\n" );
  1772. return;
  1773. }
  1774. if ( hltv->m_bMasterOnlyMode )
  1775. {
  1776. ConMsg("SourceTV in Master-Only mode.\n" );
  1777. return;
  1778. }
  1779. if ( !hltv->m_ClientState.m_szRetryAddress[ 0 ] )
  1780. {
  1781. ConMsg( "Can't retry, no previous SourceTV connection\n" );
  1782. return;
  1783. }
  1784. ConMsg( "Commencing SourceTV connection retry to %s\n", hltv->m_ClientState.m_szRetryAddress );
  1785. Cbuf_AddText( va( "tv_relay %s\n", hltv->m_ClientState.m_szRetryAddress ) );
  1786. }
  1787. CON_COMMAND( tv_record, "Starts SourceTV demo recording." )
  1788. {
  1789. if ( args.ArgC() < 2 )
  1790. {
  1791. ConMsg( "Usage: tv_record <filename>\n" );
  1792. return;
  1793. }
  1794. if ( !hltv || !hltv->IsActive() )
  1795. {
  1796. ConMsg("SourceTV not active.\n" );
  1797. return;
  1798. }
  1799. if ( !hltv->IsMasterProxy() )
  1800. {
  1801. ConMsg("Only SourceTV Master can record demos instantly.\n" );
  1802. return;
  1803. }
  1804. if ( hltv->m_DemoRecorder.IsRecording() )
  1805. {
  1806. ConMsg("SourceTV already recording to %s.\n", hltv->m_DemoRecorder.GetDemoFile()->m_szFileName );
  1807. return;
  1808. }
  1809. // check path first
  1810. if ( !COM_IsValidPath( args[1] ) )
  1811. {
  1812. ConMsg( "record %s: invalid path.\n", args[1] );
  1813. return;
  1814. }
  1815. char name[ MAX_OSPATH ];
  1816. Q_strncpy( name, args[1], sizeof( name ) );
  1817. // add .dem if not already set by user
  1818. Q_DefaultExtension( name, ".dem", sizeof( name ) );
  1819. hltv->m_DemoRecorder.StartRecording( name, false );
  1820. }
  1821. CON_COMMAND( tv_stoprecord, "Stops SourceTV demo recording." )
  1822. {
  1823. if ( !hltv || !hltv->IsActive() )
  1824. {
  1825. ConMsg("SourceTV not active.\n" );
  1826. return;
  1827. }
  1828. hltv->m_DemoRecorder.StopRecording();
  1829. }
  1830. CON_COMMAND( tv_clients, "Shows list of connected SourceTV clients." )
  1831. {
  1832. if ( !hltv || !hltv->IsActive() )
  1833. {
  1834. ConMsg("SourceTV not active.\n" );
  1835. return;
  1836. }
  1837. int nCount = 0;
  1838. for ( int i=0; i<hltv->GetClientCount(); i++)
  1839. {
  1840. CHLTVClient *client = hltv->Client( i );
  1841. INetChannel *netchan = client->GetNetChannel();
  1842. if ( !netchan )
  1843. continue;
  1844. ConMsg("ID: %i, \"%s\" %s, Time %s, %s, In %.1f, Out %.1f.\n",
  1845. client->GetUserID(),
  1846. client->GetClientName(),
  1847. client->IsHLTV() ? "(Relay)" : "",
  1848. COM_FormatSeconds( netchan->GetTimeConnected() ),
  1849. netchan->GetAddress(),
  1850. netchan->GetAvgData( FLOW_INCOMING ) / 1024,
  1851. netchan->GetAvgData( FLOW_OUTGOING ) / 1024 );
  1852. nCount++;
  1853. }
  1854. ConMsg("--- Total %i connected clients ---\n", nCount );
  1855. }
  1856. CON_COMMAND( tv_msg, "Send a screen message to all clients." )
  1857. {
  1858. if ( !hltv || !hltv->IsActive() )
  1859. {
  1860. ConMsg("SourceTV not active.\n" );
  1861. return;
  1862. }
  1863. IGameEvent *msg = g_GameEventManager.CreateEvent( "hltv_message", true );
  1864. if ( msg )
  1865. {
  1866. msg->SetString( "text", args.ArgS() );
  1867. hltv->BroadcastEventLocal( msg, false );
  1868. g_GameEventManager.FreeEvent( msg );
  1869. }
  1870. }
  1871. #ifndef SWDS
  1872. //-----------------------------------------------------------------------------
  1873. // Purpose:
  1874. //-----------------------------------------------------------------------------
  1875. void EditDemo_f( const CCommand &args )
  1876. {
  1877. if ( cmd_source != src_command )
  1878. return;
  1879. if ( args.ArgC() < 2 )
  1880. {
  1881. Msg ("editdemo <demoname> : edits a demo\n");
  1882. return;
  1883. }
  1884. // set current demo player to client demo player
  1885. demoplayer = hltv;
  1886. //
  1887. // open the demo file
  1888. //
  1889. char name[ MAX_OSPATH ];
  1890. Q_strncpy( name, args[1], sizeof( name ) );
  1891. Q_DefaultExtension( name, ".dem", sizeof( name ) );
  1892. hltv->m_ClientState.m_bSaveMemory = true;
  1893. demoplayer->StartPlayback( name, false );
  1894. }
  1895. CON_COMMAND_AUTOCOMPLETEFILE( editdemo, EditDemo_f, "Edit a recorded demo file (.dem ).", NULL, dem );
  1896. #endif