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.

456 lines
12 KiB

  1. //========= Copyright 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 "proto_version.h"
  15. #include "demofile/demoformat.h"
  16. #include "filesystem_engine.h"
  17. #include "net.h"
  18. #include "networkstringtable.h"
  19. #include "dt_common_eng.h"
  20. #include "host.h"
  21. #include "server.h"
  22. #include "networkstringtableclient.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  26. //////////////////////////////////////////////////////////////////////
  27. // Construction/Destruction
  28. //////////////////////////////////////////////////////////////////////
  29. CHLTVDemoRecorder::CHLTVDemoRecorder()
  30. {
  31. m_bIsRecording = false;
  32. }
  33. CHLTVDemoRecorder::~CHLTVDemoRecorder()
  34. {
  35. StopRecording();
  36. }
  37. void CHLTVDemoRecorder::StartAutoRecording()
  38. {
  39. char fileName[MAX_OSPATH];
  40. tm today; VCRHook_LocalTime( &today );
  41. Q_snprintf( fileName, sizeof(fileName), "auto-%04i%02i%02i-%02i%02i-%s.dem",
  42. 1900 + today.tm_year, today.tm_mon+1, today.tm_mday,
  43. today.tm_hour, today.tm_min, hltv->GetMapName() );
  44. StartRecording( fileName, false );
  45. }
  46. void CHLTVDemoRecorder::StartRecording( const char *filename, bool bContinuously )
  47. {
  48. StopRecording(); // stop if we're already recording
  49. if ( !m_DemoFile.Open( filename, false ) )
  50. {
  51. ConMsg ("StartRecording: couldn't open demo file %s.\n", filename );
  52. return;
  53. }
  54. ConMsg ("Recording SourceTV demo to %s...\n", filename);
  55. demoheader_t *dh = &m_DemoFile.m_DemoHeader;
  56. // open demo header file containing sigondata
  57. Q_memset( dh, 0, sizeof(demoheader_t));
  58. Q_strncpy( dh->demofilestamp, DEMO_HEADER_ID, sizeof(dh->demofilestamp) );
  59. dh->demoprotocol = DEMO_PROTOCOL;
  60. dh->networkprotocol = PROTOCOL_VERSION;
  61. Q_strncpy( dh->mapname, hltv->GetMapName(), sizeof( dh->mapname ) );
  62. char szGameDir[MAX_OSPATH];
  63. Q_strncpy(szGameDir, com_gamedir, sizeof( szGameDir ) );
  64. Q_FileBase ( szGameDir, dh->gamedirectory, sizeof( dh->gamedirectory ) );
  65. Q_strncpy( dh->servername, host_name.GetString(), sizeof( dh->servername ) );
  66. Q_strncpy( dh->clientname, "SourceTV Demo", sizeof( dh->servername ) );
  67. // write demo file header info
  68. m_DemoFile.WriteDemoHeader();
  69. dh->signonlength = WriteSignonData(); // demoheader will be written when demo is closed
  70. m_nFrameCount = 0;
  71. m_nStartTick = host_tickcount;
  72. // Demo playback should read this as an incoming message.
  73. // Write the client's realtime value out so we can synchronize the reads.
  74. m_DemoFile.WriteCmdHeader( dem_synctick, 0 );
  75. m_bIsRecording = true;
  76. m_SequenceInfo = 1;
  77. m_nDeltaTick = -1;
  78. }
  79. bool CHLTVDemoRecorder::IsRecording()
  80. {
  81. return m_bIsRecording;
  82. }
  83. void CHLTVDemoRecorder::StopRecording()
  84. {
  85. if ( !m_bIsRecording )
  86. return;
  87. // Demo playback should read this as an incoming message.
  88. m_DemoFile.WriteCmdHeader( dem_stop, GetRecordingTick() );
  89. // update demo header info
  90. m_DemoFile.m_DemoHeader.playback_ticks = GetRecordingTick();
  91. m_DemoFile.m_DemoHeader.playback_time = host_state.interval_per_tick * GetRecordingTick();
  92. m_DemoFile.m_DemoHeader.playback_frames = m_nFrameCount;
  93. // write updated version
  94. m_DemoFile.WriteDemoHeader();
  95. m_DemoFile.Close();
  96. m_bIsRecording = false;
  97. // clear writing data buffer
  98. if ( m_MessageData.GetBasePointer() )
  99. {
  100. delete [] m_MessageData.GetBasePointer();
  101. m_MessageData.StartWriting( NULL, 0 );
  102. }
  103. ConMsg("Completed SourceTV demo \"%s\", recording time %.1f\n",
  104. m_DemoFile.m_szFileName,
  105. m_DemoFile.m_DemoHeader.playback_time );
  106. }
  107. CDemoFile *CHLTVDemoRecorder::GetDemoFile()
  108. {
  109. return &m_DemoFile;
  110. }
  111. int CHLTVDemoRecorder::GetRecordingTick( void )
  112. {
  113. return host_tickcount - m_nStartTick;
  114. }
  115. void CHLTVDemoRecorder::WriteServerInfo()
  116. {
  117. ALIGN4 byte buffer[ NET_MAX_PAYLOAD ] ALIGN4_POST;
  118. bf_write msg( "CHLTVDemoRecorder::WriteServerInfo", buffer, sizeof( buffer ) );
  119. SVC_ServerInfo serverinfo; // create serverinfo message
  120. // on the master demos are using sv object, on relays hltv
  121. CBaseServer *pServer = hltv->IsMasterProxy()?(CBaseServer*)(&sv):(CBaseServer*)(hltv);
  122. hltv->FillServerInfo( serverinfo ); // fill rest of info message
  123. serverinfo.WriteToBuffer( msg );
  124. // send first tick
  125. NET_Tick signonTick( m_nSignonTick, 0, 0 );
  126. signonTick.WriteToBuffer( msg );
  127. // write stringtable baselines
  128. #ifndef SHARED_NET_STRING_TABLES
  129. pServer->m_StringTables->WriteBaselines( msg );
  130. #endif
  131. // Write replicated ConVars to non-listen server clients only
  132. NET_SetConVar convars;
  133. // build a list of all replicated convars
  134. Host_BuildConVarUpdateMessage( &convars, FCVAR_REPLICATED, true );
  135. if ( hltv->IsMasterProxy() )
  136. {
  137. // for SourceTV server demos write set "tv_transmitall 1" even
  138. // if it's off for the real broadcast
  139. NET_SetConVar::cvar_t acvar;
  140. Q_strncpy( acvar.name, "tv_transmitall", MAX_OSPATH );
  141. Q_strncpy( acvar.value, "1", MAX_OSPATH );
  142. convars.m_ConVars.AddToTail( acvar );
  143. }
  144. // write convars to demo
  145. convars.WriteToBuffer( msg );
  146. // send signon state
  147. NET_SignonState signonMsg( SIGNONSTATE_NEW, pServer->GetSpawnCount() );
  148. signonMsg.WriteToBuffer( msg );
  149. WriteMessages( dem_signon, msg );
  150. }
  151. void CHLTVDemoRecorder::RecordCommand( const char *cmdstring )
  152. {
  153. if ( !IsRecording() )
  154. return;
  155. if ( !cmdstring || !cmdstring[0] )
  156. return;
  157. m_DemoFile.WriteConsoleCommand( cmdstring, GetRecordingTick() );
  158. }
  159. void CHLTVDemoRecorder::RecordServerClasses( ServerClass *pClasses )
  160. {
  161. char *pBigBuffer;
  162. CUtlBuffer bigBuff;
  163. int buffSize = 256*1024;
  164. if ( !IsX360() )
  165. {
  166. pBigBuffer = (char*)stackalloc( buffSize );
  167. }
  168. else
  169. {
  170. // keep temp large allocations off of stack
  171. bigBuff.EnsureCapacity( buffSize );
  172. pBigBuffer = (char*)bigBuff.Base();
  173. }
  174. bf_write buf( pBigBuffer, buffSize );
  175. // Send SendTable info.
  176. DataTable_WriteSendTablesBuffer( pClasses, &buf );
  177. // Send class descriptions.
  178. DataTable_WriteClassInfosBuffer( pClasses, &buf );
  179. // Now write the buffer into the demo file
  180. m_DemoFile.WriteNetworkDataTables( &buf, GetRecordingTick() );
  181. }
  182. void CHLTVDemoRecorder::RecordStringTables()
  183. {
  184. // !KLUDGE! It would be nice if the bit buffer could write into a stream
  185. // with the power to grow itself. But it can't. Hence this really bad
  186. // kludge
  187. void *data = NULL;
  188. int dataLen = 512 * 1024;
  189. while ( dataLen <= DEMO_FILE_MAX_STRINGTABLE_SIZE )
  190. {
  191. data = realloc( data, dataLen );
  192. bf_write buf( data, dataLen );
  193. buf.SetDebugName("CHLTVDemoRecorder_StringTables");
  194. buf.SetAssertOnOverflow( false ); // Doesn't turn off all the spew / asserts, but turns off one
  195. networkStringTableContainerServer->WriteStringTables( buf );
  196. // Did we fit?
  197. if ( !buf.IsOverflowed() )
  198. {
  199. // Now write the buffer into the demo file
  200. m_DemoFile.WriteStringTables( &buf, GetRecordingTick() );
  201. break;
  202. }
  203. // Didn't fit. Try doubling the size of the buffer
  204. dataLen *= 2;
  205. }
  206. if ( dataLen > DEMO_FILE_MAX_STRINGTABLE_SIZE )
  207. {
  208. Warning( "Failed to RecordStringTables. Trying to record string table that's bigger than max string table size\n" );
  209. }
  210. free(data);
  211. }
  212. int CHLTVDemoRecorder::WriteSignonData()
  213. {
  214. int start = m_DemoFile.GetCurPos( false );
  215. // on the master demos are using sv object, on relays hltv
  216. CBaseServer *pServer = hltv->IsMasterProxy()?(CBaseServer*)(&sv):(CBaseServer*)(hltv);
  217. m_nSignonTick = pServer->m_nTickCount;
  218. WriteServerInfo();
  219. RecordServerClasses( serverGameDLL->GetAllServerClasses() );
  220. RecordStringTables();
  221. ALIGN4 byte buffer[ NET_MAX_PAYLOAD ] ALIGN4_POST;
  222. bf_write msg( "CHLTVDemo::WriteSignonData", buffer, sizeof( buffer ) );
  223. // use your class infos, CRC is correct
  224. SVC_ClassInfo classmsg( true, pServer->serverclasses );
  225. classmsg.WriteToBuffer( msg );
  226. // Write the regular signon now
  227. msg.WriteBits( hltv->m_Signon.GetData(), hltv->m_Signon.GetNumBitsWritten() );
  228. // write new state
  229. NET_SignonState signonMsg1( SIGNONSTATE_PRESPAWN, pServer->GetSpawnCount() );
  230. signonMsg1.WriteToBuffer( msg );
  231. WriteMessages( dem_signon, msg );
  232. msg.Reset();
  233. // set view entity
  234. SVC_SetView viewent( hltv->m_nViewEntity );
  235. viewent.WriteToBuffer( msg );
  236. // Spawned into server, not fully active, though
  237. NET_SignonState signonMsg2( SIGNONSTATE_SPAWN, pServer->GetSpawnCount() );
  238. signonMsg2.WriteToBuffer( msg );
  239. WriteMessages( dem_signon, msg );
  240. return m_DemoFile.GetCurPos( false ) - start;
  241. }
  242. void CHLTVDemoRecorder::WriteFrame( CHLTVFrame *pFrame )
  243. {
  244. ALIGN4 byte buffer[ NET_MAX_PAYLOAD ] ALIGN4_POST;
  245. bf_write msg( "CHLTVDemo::RecordFrame", buffer, sizeof( buffer ) );
  246. assert( hltv->IsMasterProxy() ); // this works only on the master since we use sv.
  247. //first write reliable data
  248. bf_write *data = &pFrame->m_Messages[HLTV_BUFFER_RELIABLE];
  249. if ( data->GetNumBitsWritten() )
  250. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  251. //now send snapshot data
  252. // send tick time
  253. NET_Tick tickmsg( pFrame->tick_count, host_frametime_unbounded, host_frametime_stddeviation );
  254. tickmsg.WriteToBuffer( msg );
  255. #ifndef SHARED_NET_STRING_TABLES
  256. // Update shared client/server string tables. Must be done before sending entities
  257. sv.m_StringTables->WriteUpdateMessage( NULL, max( m_nSignonTick, m_nDeltaTick ), msg );
  258. #endif
  259. // get delta frame
  260. CClientFrame *deltaFrame = hltv->GetClientFrame( m_nDeltaTick ); // NULL if delta_tick is not found or -1
  261. // send entity update, delta compressed if deltaFrame != NULL
  262. sv.WriteDeltaEntities( hltv->m_MasterClient, pFrame, deltaFrame, msg );
  263. // send all unreliable temp ents between last and current frame
  264. CFrameSnapshot * fromSnapshot = deltaFrame?deltaFrame->GetSnapshot():NULL;
  265. sv.WriteTempEntities( hltv->m_MasterClient, pFrame->GetSnapshot(), fromSnapshot, msg, 255 );
  266. // write sound data
  267. data = &pFrame->m_Messages[HLTV_BUFFER_SOUNDS];
  268. if ( data->GetNumBitsWritten() )
  269. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  270. // write voice data
  271. data = &pFrame->m_Messages[HLTV_BUFFER_VOICE];
  272. if ( data->GetNumBitsWritten() )
  273. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  274. // last write unreliable data
  275. data = &pFrame->m_Messages[HLTV_BUFFER_UNRELIABLE];
  276. if ( data->GetNumBitsWritten() )
  277. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  278. // update delta tick just like fakeclients do
  279. m_nDeltaTick = pFrame->tick_count;
  280. // write packet to demo file
  281. WriteMessages( dem_packet, msg );
  282. }
  283. void CHLTVDemoRecorder::WriteMessages( unsigned char cmd, bf_write &message )
  284. {
  285. int len = message.GetNumBytesWritten();
  286. if (len <= 0)
  287. return;
  288. // fill last bits in last byte with NOP if necessary
  289. int nRemainingBits = message.GetNumBitsWritten() % 8;
  290. if ( nRemainingBits > 0 && nRemainingBits <= (8-NETMSG_TYPE_BITS) )
  291. {
  292. message.WriteUBitLong( net_NOP, NETMSG_TYPE_BITS );
  293. }
  294. Assert( len < NET_MAX_MESSAGE );
  295. // if signondata read as fast as possible, no rewind
  296. // and wait for packet time
  297. // byte cmd = (m_pDemoFileHeader != NULL) ? dem_signon : dem_packet;
  298. if ( cmd == dem_packet )
  299. {
  300. m_nFrameCount++;
  301. }
  302. // write command & time
  303. m_DemoFile.WriteCmdHeader( cmd, GetRecordingTick() );
  304. // write NULL democmdinfo just to keep same format as client demos
  305. democmdinfo_t info;
  306. Q_memset( &info, 0, sizeof( info ) );
  307. m_DemoFile.WriteCmdInfo( info );
  308. // write continously increasing sequence numbers
  309. m_DemoFile.WriteSequenceInfo( m_SequenceInfo, m_SequenceInfo );
  310. m_SequenceInfo++;
  311. // Output the buffer. Skip the network packet stuff.
  312. m_DemoFile.WriteRawData( (char*)message.GetBasePointer(), len );
  313. if ( tv_debug.GetInt() > 1 )
  314. {
  315. Msg( "Writing SourceTV demo message %i bytes at file pos %i\n", len, m_DemoFile.GetCurPos( false ) );
  316. }
  317. }
  318. void CHLTVDemoRecorder::RecordMessages(bf_read &data, int bits)
  319. {
  320. // create buffer if not there yet
  321. if ( m_MessageData.GetBasePointer() == NULL )
  322. {
  323. m_MessageData.StartWriting( new unsigned char[NET_MAX_PAYLOAD], NET_MAX_PAYLOAD );
  324. }
  325. if ( bits>0 )
  326. {
  327. m_MessageData.WriteBitsFromBuffer( &data, bits );
  328. Assert( !m_MessageData.IsOverflowed() );
  329. }
  330. }
  331. void CHLTVDemoRecorder::RecordPacket()
  332. {
  333. if( m_MessageData.GetBasePointer() )
  334. {
  335. WriteMessages( dem_packet, m_MessageData );
  336. m_MessageData.Reset(); // clear message buffer
  337. }
  338. }