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.

446 lines
13 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #if defined( REPLAY_ENABLED )
  7. #include <tier1/strtools.h>
  8. #include <eiface.h>
  9. #include <bitbuf.h>
  10. #include <time.h>
  11. #include "replaydemo.h"
  12. #include "replayserver.h"
  13. #include "demo.h"
  14. #include "host_cmd.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. static ConVar replay_record_directly_to_disk( "replay_record_directly_to_disk", "0", FCVAR_HIDDEN );
  26. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  27. extern CGlobalVars g_ServerGlobalVariables;
  28. //////////////////////////////////////////////////////////////////////
  29. // Construction/Destruction
  30. //////////////////////////////////////////////////////////////////////
  31. CReplayDemoRecorder::CReplayDemoRecorder( CReplayServer* pServer )
  32. {
  33. m_bIsRecording = false;
  34. Assert( pServer );
  35. m_pReplayServer = pServer;
  36. m_nStartTick = -1;
  37. }
  38. CReplayDemoRecorder::~CReplayDemoRecorder()
  39. {
  40. StopRecording();
  41. }
  42. void CReplayDemoRecorder::GetUniqueDemoFilename( char* pOut, int nLength )
  43. {
  44. Assert( pOut );
  45. tm today;
  46. Plat_GetLocalTime( &today );
  47. Q_snprintf( pOut, nLength, "replay-%04i%02i%02i-%02i%02i%02i-%s.dem",
  48. 1900 + today.tm_year, today.tm_mon+1, today.tm_mday,
  49. today.tm_hour, today.tm_min, today.tm_sec, m_pReplayServer->GetMapName() );
  50. }
  51. void CReplayDemoRecorder::StartAutoRecording()
  52. {
  53. char fileName[MAX_OSPATH];
  54. GetUniqueDemoFilename( fileName, sizeof(fileName) );
  55. StartRecording( fileName, false );
  56. }
  57. void CReplayDemoRecorder::StartRecording( const char *filename, bool bContinuously )
  58. {
  59. StopRecording(); // stop if we're already recording
  60. if ( !m_DemoFile.Open( filename, false, !replay_record_directly_to_disk.GetInt() ) )
  61. {
  62. ConMsg ("StartRecording: couldn't open demo file %s.\n", filename );
  63. return;
  64. }
  65. ConMsg ("Recording Replay demo to %s...\n", filename);
  66. demoheader_t *dh = &m_DemoFile.m_DemoHeader;
  67. // open demo header file containing sigondata
  68. Q_memset( dh, 0, sizeof(demoheader_t) );
  69. Q_strncpy( dh->demofilestamp, DEMO_HEADER_ID, sizeof(dh->demofilestamp) );
  70. dh->demoprotocol = DEMO_PROTOCOL;
  71. dh->networkprotocol = GetHostVersion();
  72. Q_strncpy( dh->mapname, m_pReplayServer->GetMapName(), sizeof( dh->mapname ) );
  73. char szGameDir[MAX_OSPATH];
  74. Q_strncpy(szGameDir, com_gamedir, sizeof( szGameDir ) );
  75. Q_FileBase ( szGameDir, dh->gamedirectory, sizeof( dh->gamedirectory ) );
  76. Q_strncpy( dh->servername, host_name.GetString(), sizeof( dh->servername ) );
  77. Q_strncpy( dh->clientname, "Replay Demo", sizeof( dh->servername ) );
  78. // write demo file header info
  79. m_DemoFile.WriteDemoHeader();
  80. dh->signonlength = WriteSignonData(); // demoheader will be written when demo is closed
  81. m_nFrameCount = 0;
  82. // Using this tickcount allows us to sync up client-side recorded ragdolls later with replay demos on clients
  83. m_nStartTick = g_ServerGlobalVariables.tickcount;
  84. // Demo playback should read this as an incoming message.
  85. // Write the client's realtime value out so we can synchronize the reads.
  86. m_DemoFile.WriteCmdHeader( dem_synctick, 0, 0 );
  87. // This tells the demo buffer (see demobuffer.cpp) that we are done writing all signon data, so that
  88. // it can maintain be maintained separately, so that we can write a demo after stale frames have been
  89. // removed.
  90. m_DemoFile.NotifySignonComplete();
  91. m_bIsRecording = true;
  92. m_SequenceInfo = 1;
  93. m_nDeltaTick = -1;
  94. }
  95. bool CReplayDemoRecorder::IsRecording()
  96. {
  97. return m_bIsRecording;
  98. }
  99. void CReplayDemoRecorder::StopRecording( const CGameInfo *pGameInfo )
  100. {
  101. if ( !m_bIsRecording )
  102. return;
  103. // Demo playback should read this as an incoming message.
  104. m_DemoFile.NotifyBeginFrame();
  105. m_DemoFile.WriteCmdHeader( dem_stop, GetRecordingTick(), 0 );
  106. m_DemoFile.NotifyEndFrame();
  107. // update demo header info
  108. // This stuff gets computed in demobuffer.cpp
  109. // m_DemoFile.m_DemoHeader.playback_ticks = GetRecordingTick();
  110. // m_DemoFile.m_DemoHeader.playback_time = host_state.interval_per_tick * GetRecordingTick();
  111. // m_DemoFile.m_DemoHeader.playback_frames = m_nFrameCount;
  112. // write updated version
  113. m_DemoFile.WriteDemoHeader();
  114. m_DemoFile.Close();
  115. m_bIsRecording = false;
  116. // clear writing data buffer
  117. if ( m_MessageData.GetBasePointer() )
  118. {
  119. delete [] m_MessageData.GetBasePointer();
  120. m_MessageData.StartWriting( NULL, 0 );
  121. }
  122. // ConMsg("Completed Replay demo \"%s\", recording time %.1f\n",
  123. // m_DemoFile.m_szFileName,
  124. // m_DemoFile.m_DemoHeader.playback_time );
  125. }
  126. void CReplayDemoRecorder::DumpToFile( char const *filename )
  127. {
  128. // No need to write header here, since the demo buffer's dump function writes the adjusted header
  129. m_DemoFile.DumpBufferToFile( filename, m_DemoFile.m_DemoHeader );
  130. }
  131. CDemoFile *CReplayDemoRecorder::GetDemoFile()
  132. {
  133. return &m_DemoFile;
  134. }
  135. int CReplayDemoRecorder::GetRecordingTick()
  136. {
  137. return g_ServerGlobalVariables.tickcount - m_nStartTick;
  138. }
  139. void CReplayDemoRecorder::WriteServerInfo()
  140. {
  141. byte buffer[ NET_MAX_PAYLOAD ];
  142. bf_write msg( "CReplayDemoRecorder::WriteServerInfo", buffer, sizeof( buffer ) );
  143. SVC_ServerInfo serverinfo; // create serverinfo message
  144. // on the master demos are using sv object, on relays replay
  145. CSVCMsg_ServerInfo_t serverinfo; // create serverinfo message
  146. m_pReplayServer->FillServerInfo( serverinfo ); // fill rest of info message
  147. serverinfo.WriteToBuffer( msg );
  148. // send first tick
  149. CNETMsg_Tick_t signonTick( m_nSignonTick, 0, 0 );
  150. signonTick.WriteToBuffer( msg );
  151. // Write replicated ConVars to non-listen server clients only
  152. NETMsg_SetConVar_t convars;
  153. // build a list of all replicated convars
  154. Host_BuildConVarUpdateMessage( convars.m_ConVars, FCVAR_REPLICATED, true );
  155. // for Replay server demos write set "replay_transmitall 1" even
  156. // if it's off for the real broadcast
  157. NetMessageCvar_t acvar;
  158. Q_strncpy( acvar.name, "replay_transmitall", MAX_OSPATH );
  159. Q_strncpy( acvar.value, "1", MAX_OSPATH );
  160. convars.m_ConVars.AddToTail( acvar );
  161. // write convars to demo
  162. convars.WriteToBuffer( msg );
  163. // write stringtable baselines
  164. #ifndef SHARED_NET_STRING_TABLES
  165. m_pReplayServer->m_StringTables->WriteBaselines( pServer->GetMapName(), msg );
  166. #endif
  167. // send signon state
  168. NET_SignonState signonMsg( SIGNONSTATE_NEW, pServer->GetSpawnCount() );
  169. signonMsg.WriteToBuffer( msg );
  170. WriteMessages( dem_signon, msg );
  171. }
  172. void CReplayDemoRecorder::RecordCommand( const char *cmdstring )
  173. {
  174. if ( !IsRecording() )
  175. return;
  176. if ( !cmdstring || !cmdstring[0] )
  177. return;
  178. m_DemoFile.WriteConsoleCommand( cmdstring, GetRecordingTick(), 0 );
  179. }
  180. void CReplayDemoRecorder::RecordServerClasses( ServerClass *pClasses )
  181. {
  182. byte data[NET_MAX_PAYLOAD];
  183. bf_write buf( data, sizeof(data) );
  184. // Send SendTable info.
  185. DataTable_WriteSendTablesBuffer( pClasses, &buf );
  186. // Send class descriptions.
  187. DataTable_WriteClassInfosBuffer( pClasses, &buf );
  188. // Now write the buffer into the demo file
  189. m_DemoFile.WriteNetworkDataTables( &buf, GetRecordingTick() );
  190. }
  191. void CReplayDemoRecorder::RecordCustomData( int iCallbackIndex, const void *pData, size_t iDataLength )
  192. {
  193. m_DemoFile.WriteCustomData( iCallbackIndex, pData, iDataLength, GetRecordingTick() );
  194. }
  195. void CReplayDemoRecorder::RecordStringTables()
  196. {
  197. byte data[256 * 1024];
  198. bf_write buf( data, sizeof(data) );
  199. networkStringTableContainerServer->WriteStringTables( buf );
  200. // Now write the buffer into the demo file
  201. m_DemoFile.WriteStringTables( &buf, GetRecordingTick() );
  202. }
  203. int CReplayDemoRecorder::WriteSignonData()
  204. {
  205. int start = m_DemoFile.GetCurPos( false );
  206. // on the master demos are using sv object, on relays replay
  207. CBaseServer *pServer = (CBaseServer*)&sv;
  208. m_nSignonTick = pServer->m_nTickCount;
  209. WriteServerInfo();
  210. RecordServerClasses( serverGameDLL->GetAllServerClasses() );
  211. RecordStringTables();
  212. byte buffer[ NET_MAX_PAYLOAD ];
  213. bf_write msg( "CReplayDemo::WriteSignonData", buffer, sizeof( buffer ) );
  214. // use your class infos, CRC is correct
  215. CSVCMsg_ClassInfo_t classmsg;
  216. classmsg.set_create_on_client( true );
  217. classmsg.WriteToBuffer( msg );
  218. // Write the regular signon now
  219. msg.WriteBits( m_pReplayServer->m_Signon.GetData(), m_pReplayServer->m_Signon.GetNumBitsWritten() );
  220. // write new state
  221. NET_SignonState signonMsg1( SIGNONSTATE_PRESPAWN, pServer->GetSpawnCount() );
  222. signonMsg1.WriteToBuffer( msg );
  223. WriteMessages( dem_signon, msg );
  224. msg.Reset();
  225. // set view entity
  226. CSVCMsg_SetView_t viewent;
  227. viewent.set_entity_index( m_pReplayServer->m_nViewEntity );
  228. viewent.WriteToBuffer( msg );
  229. // Spawned into server, not fully active, though
  230. NET_SignonState signonMsg2( SIGNONSTATE_SPAWN, pServer->GetSpawnCount() );
  231. signonMsg2.WriteToBuffer( msg );
  232. WriteMessages( dem_signon, msg );
  233. return m_DemoFile.GetCurPos( false ) - start;
  234. }
  235. void CReplayDemoRecorder::WriteFrame( CReplayFrame *pFrame )
  236. {
  237. byte buffer[ NET_MAX_PAYLOAD ];
  238. bf_write msg( "CReplayDemo::RecordFrame", buffer, sizeof( buffer ) );
  239. DevMsg( "CReplayDemoRecorder::WriteFrame, tick %d\n", GetRecordingTick() );
  240. //first write reliable data
  241. bf_write *data = &pFrame->m_Messages[REPLAY_BUFFER_RELIABLE];
  242. if ( data->GetNumBitsWritten() )
  243. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  244. //now send snapshot data
  245. // send tick time
  246. CNETMsg_Tick_t tickmsg( pFrame->tick_count, host_frametime_unbounded, host_frametime_stddeviation );
  247. tickmsg.WriteToBuffer( msg );
  248. #ifndef SHARED_NET_STRING_TABLES
  249. // Update shared client/server string tables. Must be done before sending entities
  250. sv.m_StringTables->WriteUpdateMessage( NULL, MAX( m_nSignonTick, m_nDeltaTick ), msg );
  251. #endif
  252. // get delta frame
  253. CClientFrame *deltaFrame = m_pReplayServer->GetClientFrame( m_nDeltaTick ); // NULL if m_nDeltaTick is not found or -1
  254. // CClientFrame *deltaFrame = NULL; // For full update
  255. // send entity update, delta compressed if deltaFrame != NULL
  256. sv.WriteDeltaEntities( m_pReplayServer->m_MasterClient, pFrame, deltaFrame, msg );
  257. // send all unreliable temp ents between last and current frame
  258. CFrameSnapshot * fromSnapshot = deltaFrame?deltaFrame->GetSnapshot():NULL;
  259. sv.WriteTempEntities( m_pReplayServer->m_MasterClient, pFrame->GetSnapshot(), fromSnapshot, msg, 255 );
  260. // write sound data
  261. data = &pFrame->m_Messages[REPLAY_BUFFER_SOUNDS];
  262. if ( data->GetNumBitsWritten() )
  263. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  264. // write voice data
  265. data = &pFrame->m_Messages[REPLAY_BUFFER_VOICE];
  266. if ( data->GetNumBitsWritten() )
  267. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  268. // last write unreliable data
  269. data = &pFrame->m_Messages[REPLAY_BUFFER_UNRELIABLE];
  270. if ( data->GetNumBitsWritten() )
  271. msg.WriteBits( data->GetBasePointer(), data->GetNumBitsWritten() );
  272. // update delta tick just like fakeclients do
  273. m_nDeltaTick = pFrame->tick_count;
  274. // write packet to demo file
  275. m_DemoFile.NotifyBeginFrame();
  276. WriteMessages( dem_packet, msg );
  277. m_DemoFile.NotifyEndFrame();
  278. }
  279. void CReplayDemoRecorder::WriteMessages( unsigned char cmd, bf_write &message )
  280. {
  281. int len = message.GetNumBytesWritten();
  282. if (len <= 0)
  283. return;
  284. // fill last bits in last byte with NOP if necessary
  285. int nRemainingBits = message.GetNumBitsWritten() % 8;
  286. if ( nRemainingBits > 0 && nRemainingBits <= (8-NETMSG_TYPE_BITS) )
  287. {
  288. message.WriteUBitLong( net_NOP, NETMSG_TYPE_BITS );
  289. }
  290. Assert( len < NET_MAX_MESSAGE );
  291. // if signondata read as fast as possible, no rewind
  292. // and wait for packet time
  293. // byte cmd = (m_pDemoFileHeader != NULL) ? dem_signon : dem_packet;
  294. if ( cmd == dem_packet )
  295. {
  296. m_nFrameCount++;
  297. }
  298. // write command & time
  299. m_DemoFile.WriteCmdHeader( cmd, GetRecordingTick(), 0 );
  300. // write NULL democmdinfo just to keep same format as client demos
  301. democmdinfo_t info;
  302. Q_memset( &info, 0, sizeof( info ) );
  303. m_DemoFile.WriteCmdInfo( info );
  304. // write continously increasing sequence numbers
  305. m_DemoFile.WriteSequenceInfo( m_SequenceInfo, m_SequenceInfo );
  306. m_SequenceInfo++;
  307. // Output the buffer. Skip the network packet stuff.
  308. m_DemoFile.WriteRawData( (char*)message.GetBasePointer(), len );
  309. if ( replay_debug.GetInt() > 1 )
  310. {
  311. Msg( "Writing Replay demo message %i bytes at file pos %i\n", len, m_DemoFile.GetCurPos( false ) );
  312. }
  313. }
  314. void CReplayDemoRecorder::RecordMessages(bf_read &data, int bits)
  315. {
  316. // create buffer if not there yet
  317. if ( m_MessageData.GetBasePointer() == NULL )
  318. {
  319. m_MessageData.StartWriting( new unsigned char[NET_MAX_PAYLOAD], NET_MAX_PAYLOAD );
  320. }
  321. if ( bits>0 )
  322. {
  323. m_MessageData.WriteBitsFromBuffer( &data, bits );
  324. Assert( !m_MessageData.IsOverflowed() );
  325. }
  326. }
  327. void CReplayDemoRecorder::RecordPacket()
  328. {
  329. Assert( !"Does this ever get called? I can't find anywhere where it does." );
  330. if( m_MessageData.GetBasePointer() )
  331. {
  332. WriteMessages( dem_packet, m_MessageData );
  333. m_MessageData.Reset(); // clear message buffer
  334. }
  335. }
  336. #endif