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.

517 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include <tier1/strtools.h>
  7. #include <eiface.h>
  8. #include <bitbuf.h>
  9. #include <time.h>
  10. #include "hltvdemo.h"
  11. #include "hltvserver.h"
  12. #include "demo.h"
  13. #include "host_cmd.h"
  14. #include "demofile/demoformat.h"
  15. #include "filesystem_engine.h"
  16. #include "net.h"
  17. #include "networkstringtable.h"
  18. #include "dt_common_eng.h"
  19. #include "host.h"
  20. #include "server.h"
  21. #include "networkstringtableclient.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  25. //////////////////////////////////////////////////////////////////////
  26. // Construction/Destruction
  27. //////////////////////////////////////////////////////////////////////
  28. CHLTVDemoRecorder::CHLTVDemoRecorder(CHLTVServer *pHltvServer) :hltv( pHltvServer )
  29. {
  30. m_bIsRecording = false;
  31. }
  32. CHLTVDemoRecorder::~CHLTVDemoRecorder()
  33. {
  34. StopRecording();
  35. }
  36. void CHLTVDemoRecorder::StartAutoRecording()
  37. {
  38. char fileName[MAX_OSPATH];
  39. struct tm today;
  40. Plat_GetLocalTime( &today );
  41. Q_snprintf( fileName, sizeof(fileName), "%sauto%d-%04i%02i%02i-%02i%02i%02i-%u-%s-%s.dem",
  42. ( serverGameDLL && serverGameDLL->IsValveDS() ) ? "replays/" : "",
  43. hltv->GetInstanceIndex(),
  44. 1900 + today.tm_year, today.tm_mon + 1, today.tm_mday,
  45. today.tm_hour, today.tm_min, today.tm_sec,
  46. RandomInt( 1, INT_MAX - 1 ),
  47. hltv->GetMapName(), host_name.GetString() );
  48. // replace any bad characters that might be in the host name
  49. int nLen = Q_strlen( fileName );
  50. for ( int i = 9; i < nLen; i++ )
  51. {
  52. if ( fileName[i] >= 'a' && fileName[i] <= 'z' )
  53. continue;
  54. if ( fileName[i] >= 'A' && fileName[i] <= 'Z' )
  55. continue;
  56. if ( fileName[i] >= '0' && fileName[i] <= '9' )
  57. continue;
  58. if ( fileName[i] == '_' || fileName[i] == '-' || fileName[i] == '.' )
  59. continue;
  60. fileName[i] = '_';
  61. }
  62. StartRecording( fileName, false );
  63. }
  64. void CHLTVDemoRecorder::StartRecording( const char *filename, bool bContinuously )
  65. {
  66. StopRecording(); // stop if we're already recording
  67. if ( !m_DemoFile.Open( filename, false ) )
  68. {
  69. ConMsg ("StartRecording: couldn't open demo file %s.\n", filename );
  70. return;
  71. }
  72. ConMsg ("Recording GOTV demo to %s...\n", filename);
  73. demoheader_t *dh = &m_DemoFile.m_DemoHeader;
  74. // open demo header file containing sigondata
  75. Q_memset( dh, 0, sizeof(demoheader_t));
  76. Q_strncpy( dh->demofilestamp, DEMO_HEADER_ID, sizeof(dh->demofilestamp) );
  77. dh->demoprotocol = DEMO_PROTOCOL;
  78. dh->networkprotocol = GetHostVersion();
  79. Q_strncpy( dh->mapname, hltv->GetMapName(), sizeof( dh->mapname ) );
  80. char szGameDir[MAX_OSPATH];
  81. Q_strncpy(szGameDir, com_gamedir, sizeof( szGameDir ) );
  82. Q_FileBase ( szGameDir, dh->gamedirectory, sizeof( dh->gamedirectory ) );
  83. Q_strncpy( dh->servername, host_name.GetString(), sizeof( dh->servername ) );
  84. Q_strncpy( dh->clientname, "GOTV Demo", sizeof( dh->servername ) );
  85. // Write demo file header info. This is mostly to allocate space in the output file,
  86. // the final demo header will be written when we finish recording.
  87. m_DemoFile.WriteDemoHeader();
  88. // We keep these identical so that GetRecordingTick() returns 0 until at least the first
  89. // frame is recorded.
  90. //
  91. // -1 here is really a sentinel value so that we can detect when the first tick is written
  92. // and do some bookkeeping at a safe point there. See HasRecordingActuallyStarted()
  93. m_nStartTick = -1;
  94. m_nLastWrittenTick = -1;
  95. m_bIsRecording = true;
  96. m_nFrameCount = 0;
  97. m_SequenceInfo = 1;
  98. m_nDeltaTick = -1;
  99. }
  100. bool CHLTVDemoRecorder::IsRecording()
  101. {
  102. return m_bIsRecording;
  103. }
  104. void CHLTVDemoRecorder::StopRecording( const CGameInfo *pGameInfo )
  105. {
  106. if ( !m_bIsRecording )
  107. return;
  108. // Demo playback should read this as an incoming message.
  109. m_DemoFile.WriteCmdHeader( dem_stop, GetRecordingTick(), 0 );
  110. // update demo header info
  111. m_DemoFile.m_DemoHeader.playback_ticks = GetRecordingTick();
  112. m_DemoFile.m_DemoHeader.playback_time = host_state.interval_per_tick * GetRecordingTick();
  113. m_DemoFile.m_DemoHeader.playback_frames = m_nFrameCount;
  114. // write updated version
  115. m_DemoFile.WriteDemoHeader();
  116. m_DemoFile.Close();
  117. m_bIsRecording = false;
  118. // clear writing data buffer
  119. if ( m_MessageData.GetBasePointer() )
  120. {
  121. delete [] m_MessageData.GetBasePointer();
  122. m_MessageData.StartWriting( NULL, 0 );
  123. }
  124. ConMsg("Completed GOTV demo \"%s\", recording time %.1f\n",
  125. m_DemoFile.m_szFileName,
  126. m_DemoFile.m_DemoHeader.playback_time );
  127. }
  128. int CHLTVDemoRecorder::GetRecordingTick( void )
  129. {
  130. return m_nLastWrittenTick - m_nStartTick;
  131. }
  132. void CHLTVDemoRecorder::WriteServerInfo()
  133. {
  134. Assert( HasRecordingActuallyStarted() );
  135. byte buffer[ NET_MAX_PAYLOAD ];
  136. bf_write msg( "CHLTVDemoRecorder::WriteServerInfo", buffer, sizeof( buffer ) );
  137. // on the master demos are using sv object, on relays hltv
  138. CBaseServer *pServer = hltv->IsMasterProxy()?(CBaseServer*)(&sv):(CBaseServer*)(hltv);
  139. CSVCMsg_ServerInfo_t serverinfo;
  140. hltv->FillServerInfo( serverinfo ); // fill rest of info message
  141. serverinfo.WriteToBuffer( msg );
  142. // send first tick
  143. CNETMsg_Tick_t signonTick( m_nStartTick, 0, 0, 0 );
  144. signonTick.WriteToBuffer( msg );
  145. // Write replicated ConVars to non-listen server clients only
  146. CNETMsg_SetConVar_t convars;
  147. // build a list of all replicated convars
  148. Host_BuildConVarUpdateMessage( convars.mutable_convars(), FCVAR_REPLICATED, true );
  149. if ( hltv->IsMasterProxy() )
  150. {
  151. // for SourceTV server demos write set "tv_transmitall 1" even
  152. // if it's off for the real broadcast
  153. convars.AddToTail( "tv_transmitall", "1" );
  154. hltv->FixupConvars( convars );
  155. }
  156. // write convars to demo
  157. convars.WriteToBuffer( msg );
  158. // write stringtable baselines
  159. #ifndef SHARED_NET_STRING_TABLES
  160. hltv->m_StringTables->WriteBaselines( pServer->GetMapName(), msg );
  161. #endif
  162. // send signon state
  163. CNETMsg_SignonState_t signonMsg( SIGNONSTATE_NEW, pServer->GetSpawnCount() );
  164. signonMsg.WriteToBuffer( msg );
  165. WriteMessages( dem_signon, msg );
  166. }
  167. void CHLTVDemoRecorder::RecordPlayerAvatar( const CNETMsg_PlayerAvatarData_t* hltvPlayerAvatar )
  168. {
  169. // If we haven't actually 'signed on' yet, don't record; we'll capture this in our signon data
  170. if ( !HasRecordingActuallyStarted() )
  171. return;
  172. // If we are already recording then append this player's avatar data into the demo
  173. byte buffer[NET_MAX_PAYLOAD];
  174. bf_write bfWrite( "CHLTVDemo::NETMsg_PlayerAvatarData", buffer, sizeof( buffer ) );
  175. hltvPlayerAvatar->WriteToBuffer( bfWrite );
  176. WriteMessages( dem_signon, bfWrite );
  177. }
  178. void CHLTVDemoRecorder::RecordCommand( const char *cmdstring )
  179. {
  180. if ( !HasRecordingActuallyStarted() )
  181. return;
  182. if ( !cmdstring || !cmdstring[0] )
  183. return;
  184. m_DemoFile.WriteConsoleCommand( cmdstring, GetRecordingTick(), 0 );
  185. }
  186. void CHLTVDemoRecorder::RecordServerClasses( ServerClass *pClasses )
  187. {
  188. // We should be in the middle of recording the sign-on state at this point
  189. // which means we will have a valid nStartTick.
  190. //
  191. // This assert is to make sure that this doesn't randomly get called via
  192. // IDemoRecorder; in that case we should probably just return.
  193. Assert( HasRecordingActuallyStarted() );
  194. // to big for stack
  195. CUtlBuffer bigBuff;
  196. bigBuff.EnsureCapacity( DEMO_RECORD_BUFFER_SIZE );
  197. bf_write buf( bigBuff.Base(), DEMO_RECORD_BUFFER_SIZE );
  198. // Send SendTable info.
  199. DataTable_WriteSendTablesBuffer( pClasses, &buf );
  200. // Send class descriptions.
  201. DataTable_WriteClassInfosBuffer( pClasses, &buf );
  202. if ( buf.GetNumBitsLeft() <= 0 )
  203. {
  204. Sys_Error( "unable to record server classes\n" );
  205. }
  206. // Now write the buffer into the demo file
  207. m_DemoFile.WriteNetworkDataTables( &buf, GetRecordingTick() );
  208. }
  209. void CHLTVDemoRecorder::RecordCustomData( int iCallbackIndex, const void *pData, size_t iDataLength )
  210. {
  211. if ( !HasRecordingActuallyStarted() )
  212. return;
  213. m_DemoFile.WriteCustomData( iCallbackIndex, pData, iDataLength, GetRecordingTick() );
  214. }
  215. void CHLTVDemoRecorder::RecordStringTables()
  216. {
  217. Assert( HasRecordingActuallyStarted() );
  218. void *data = malloc( DEMO_RECORD_BUFFER_SIZE );
  219. bf_write buf( data, DEMO_RECORD_BUFFER_SIZE );
  220. networkStringTableContainerServer->WriteStringTables( buf );
  221. // Now write the buffer into the demo file
  222. m_DemoFile.WriteStringTables( &buf, GetRecordingTick() );
  223. free( data );
  224. }
  225. int CHLTVDemoRecorder::WriteSignonData()
  226. {
  227. Assert( HasRecordingActuallyStarted() );
  228. int start = m_DemoFile.GetCurPos( false );
  229. // on the master demos are using sv object, on relays hltv
  230. CBaseServer *pServer = hltv->IsMasterProxy()?(CBaseServer*)(&sv):(CBaseServer*)(hltv);
  231. WriteServerInfo();
  232. RecordServerClasses( serverGameDLL->GetAllServerClasses() );
  233. RecordStringTables();
  234. byte buffer[ NET_MAX_PAYLOAD ];
  235. bf_write msg( "CHLTVDemo::WriteSignonData", buffer, sizeof( buffer ) );
  236. // use your class infos, CRC is correct
  237. // use your class infos, CRC is correct
  238. CSVCMsg_ClassInfo_t classmsg;
  239. classmsg.set_create_on_client( true );
  240. classmsg.WriteToBuffer( msg );
  241. // Write the regular signon now
  242. msg.WriteBits( hltv->m_Signon.GetData(), hltv->m_Signon.GetNumBitsWritten() );
  243. // write new state
  244. CNETMsg_SignonState_t signonMsg1( SIGNONSTATE_PRESPAWN, pServer->GetSpawnCount() );
  245. signonMsg1.WriteToBuffer( msg );
  246. WriteMessages( dem_signon, msg );
  247. msg.Reset();
  248. // Dump all accumulated avatar data messages into signon portion of the demo file
  249. FOR_EACH_MAP_FAST( hltv->m_mapPlayerAvatarData, iData )
  250. {
  251. CNETMsg_PlayerAvatarData_t &msgPlayerAvatarData = * hltv->m_mapPlayerAvatarData.Element( iData );
  252. msgPlayerAvatarData.WriteToBuffer( msg );
  253. WriteMessages( dem_signon, msg );
  254. msg.Reset();
  255. }
  256. // set view entity
  257. // set view entity
  258. CSVCMsg_SetView_t viewent;
  259. viewent.set_entity_index( hltv->m_nViewEntity );
  260. viewent.WriteToBuffer( msg );
  261. // Spawned into server, not fully active, though
  262. CNETMsg_SignonState_t signonMsg2( SIGNONSTATE_SPAWN, pServer->GetSpawnCount() );
  263. signonMsg2.WriteToBuffer( msg );
  264. WriteMessages( dem_signon, msg );
  265. return m_DemoFile.GetCurPos( false ) - start;
  266. }
  267. void CHLTVDemoRecorder::WriteFrame( CHLTVFrame *pFrame, bf_write *additionaldata )
  268. {
  269. Assert( hltv->IsMasterProxy() ); // this works only on the master since we use sv.
  270. Assert( pFrame->tick_count >= 0 );
  271. byte buffer[ NET_MAX_PAYLOAD ];
  272. bf_write msg( "CHLTVDemo::RecordFrame", buffer, sizeof( buffer ) );
  273. // Handle first frame of demo recording here
  274. if ( m_nStartTick == -1 )
  275. {
  276. // Synchronize start tick with frame.
  277. //
  278. // THIS IS THE POINT WHERE HasRecordingActuallyStarted() STARTS TO RETURN "true"!
  279. //
  280. // WriteSignonData() relies on this being the correct value for the server, matching
  281. // the string tables timestamp in the HLTV server
  282. m_nStartTick = pFrame->tick_count;
  283. // Write out the current state of string tables, etc.
  284. m_DemoFile.m_DemoHeader.signonlength = WriteSignonData();
  285. // Demo playback should read this as an incoming message.
  286. // Write the client's realtime value out so we can synchronize the reads.
  287. m_DemoFile.WriteCmdHeader( dem_synctick, 0, 0 );
  288. }
  289. m_nLastWrittenTick = pFrame->tick_count;
  290. //first write reliable data
  291. bf_write *data = &pFrame->m_Messages[HLTV_BUFFER_RELIABLE];
  292. if ( data->GetNumBitsWritten() )
  293. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  294. //now send snapshot data
  295. // send tick time
  296. CNETMsg_Tick_t tickmsg( pFrame->tick_count, host_frameendtime_computationduration, host_frametime_stddeviation, host_framestarttime_stddeviation );
  297. tickmsg.WriteToBuffer( msg );
  298. #ifndef SHARED_NET_STRING_TABLES
  299. // Update shared client/server string tables. Must be done before sending entities
  300. hltv->m_StringTables->WriteUpdateMessage( NULL, MAX( m_nStartTick, m_nDeltaTick ), msg );
  301. #endif
  302. // get delta frame
  303. CClientFrame *deltaFrame = hltv->GetClientFrame( m_nDeltaTick ); // NULL if delta_tick is not found or -1
  304. // send entity update, delta compressed if deltaFrame != NULL
  305. CSVCMsg_PacketEntities_t packetmsg;
  306. sv.WriteDeltaEntities( hltv->m_MasterClient, pFrame, deltaFrame, packetmsg );
  307. packetmsg.WriteToBuffer( msg );
  308. // send all unreliable temp ents between last and current frame
  309. CSVCMsg_TempEntities_t tempentsmsg;
  310. CFrameSnapshot * fromSnapshot = deltaFrame?deltaFrame->GetSnapshot():NULL;
  311. sv.WriteTempEntities( hltv->m_MasterClient, pFrame->GetSnapshot(), fromSnapshot, tempentsmsg, 255 );
  312. if ( tempentsmsg.num_entries() )
  313. {
  314. tempentsmsg.WriteToBuffer( msg );
  315. }
  316. // write sound data
  317. data = &pFrame->m_Messages[HLTV_BUFFER_SOUNDS];
  318. if ( data->GetNumBitsWritten() )
  319. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  320. // write voice data
  321. data = &pFrame->m_Messages[HLTV_BUFFER_VOICE];
  322. if ( data->GetNumBitsWritten() )
  323. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  324. // last write unreliable data
  325. data = &pFrame->m_Messages[HLTV_BUFFER_UNRELIABLE];
  326. if ( data->GetNumBitsWritten() )
  327. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  328. if ( additionaldata && additionaldata->GetNumBitsWritten() )
  329. {
  330. msg.WriteBits( additionaldata->GetBasePointer(), additionaldata->GetNumBitsWritten() );
  331. }
  332. // update delta tick just like fakeclients do
  333. m_nDeltaTick = pFrame->tick_count;
  334. // write packet to demo file
  335. WriteMessages( dem_packet, msg );
  336. }
  337. void CHLTVDemoRecorder::WriteMessages( unsigned char cmd, bf_write &message )
  338. {
  339. Assert( HasRecordingActuallyStarted() );
  340. int len = message.GetNumBytesWritten();
  341. if (len <= 0)
  342. return;
  343. // fill last bits in last byte with NOP if necessary
  344. int nRemainingBits = message.GetNumBitsWritten() % 8;
  345. if ( nRemainingBits > 0 && nRemainingBits <= (8-NETMSG_TYPE_BITS) )
  346. {
  347. CNETMsg_NOP_t nop;
  348. nop.WriteToBuffer( message );
  349. }
  350. Assert( len < NET_MAX_MESSAGE );
  351. // if signondata read as fast as possible, no rewind
  352. // and wait for packet time
  353. // byte cmd = (m_pDemoFileHeader != NULL) ? dem_signon : dem_packet;
  354. if ( cmd == dem_packet )
  355. {
  356. m_nFrameCount++;
  357. }
  358. // write command & time
  359. m_DemoFile.WriteCmdHeader( cmd, GetRecordingTick(), 0 );
  360. // write NULL democmdinfo just to keep same format as client demos
  361. democmdinfo_t info;
  362. Q_memset( &info, 0, sizeof( info ) );
  363. m_DemoFile.WriteCmdInfo( info );
  364. // write continously increasing sequence numbers
  365. m_DemoFile.WriteSequenceInfo( m_SequenceInfo, m_SequenceInfo );
  366. m_SequenceInfo++;
  367. // Output the buffer. Skip the network packet stuff.
  368. m_DemoFile.WriteRawData( (char*)message.GetBasePointer(), len );
  369. if ( tv_debug.GetInt() > 1 )
  370. {
  371. Msg( "Writing GOTV demo message %i bytes at file pos %i\n", len, m_DemoFile.GetCurPos( false ) );
  372. }
  373. }
  374. void CHLTVDemoRecorder::RecordMessages(bf_read &data, int bits)
  375. {
  376. if ( !HasRecordingActuallyStarted() )
  377. return;
  378. // create buffer if not there yet
  379. if ( m_MessageData.GetBasePointer() == NULL )
  380. {
  381. m_MessageData.StartWriting( new unsigned char[NET_MAX_PAYLOAD], NET_MAX_PAYLOAD );
  382. }
  383. if ( bits>0 )
  384. {
  385. m_MessageData.WriteBitsFromBuffer( &data, bits );
  386. Assert( !m_MessageData.IsOverflowed() );
  387. }
  388. }
  389. void CHLTVDemoRecorder::RecordPacket()
  390. {
  391. if ( !HasRecordingActuallyStarted() )
  392. return;
  393. if( m_MessageData.GetBasePointer() )
  394. {
  395. WriteMessages( dem_packet, m_MessageData );
  396. m_MessageData.Reset(); // clear message buffer
  397. }
  398. }