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.

208 lines
6.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #include "replaysystem.h"
  5. #include "sv_sessionrecorder.h"
  6. #include "utlbuffer.h"
  7. #include "sessioninfoheader.h"
  8. #include "sv_fileservercleanup.h"
  9. #include "sv_publishtest.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //----------------------------------------------------------------------------------------
  13. #define ENSURE_DEDICATED() if ( !g_pEngine->IsDedicated() ) return;
  14. //----------------------------------------------------------------------------------------
  15. CON_COMMAND( replay_record, "Starts Replay demo recording." )
  16. {
  17. ENSURE_DEDICATED();
  18. SV_GetSessionRecorder()->StartRecording();
  19. }
  20. //----------------------------------------------------------------------------------------
  21. CON_COMMAND( replay_stoprecord, "Stop Replay demo recording." )
  22. {
  23. ENSURE_DEDICATED();
  24. g_pReplay->SV_EndRecordingSession();
  25. }
  26. //----------------------------------------------------------------------------------------
  27. CON_COMMAND( replay_docleanup, "Deletes stale session data from the fileserver. \"replay_docleanup force\" will remove all replay session data." )
  28. {
  29. ENSURE_DEDICATED();
  30. bool bForceCleanAll = false;
  31. if ( args.ArgC() == 2 )
  32. {
  33. if ( !V_stricmp( args[1], "force" ) )
  34. {
  35. bForceCleanAll = true;
  36. }
  37. else
  38. {
  39. ConMsg( "\n ** ERROR: '%s' is not a valid paramter - use 'force' to force clean all replay session data.\n\n", args[1] );
  40. return;
  41. }
  42. }
  43. if ( !SV_DoFileserverCleanup( bForceCleanAll, g_pBlockSpewer ) )
  44. {
  45. Msg( "No demos were deleted.\n" );
  46. }
  47. }
  48. //----------------------------------------------------------------------------------------
  49. CON_COMMAND_F( replay_dopublishtest, "Do a replay publish test using the current setup.", FCVAR_DONTRECORD )
  50. {
  51. ENSURE_DEDICATED();
  52. g_pBlockSpewer->PrintBlockStart();
  53. SV_DoTestPublish();
  54. g_pBlockSpewer->PrintBlockEnd();
  55. }
  56. //----------------------------------------------------------------------------------------
  57. void PrintSessionInfo( const char *pFilename )
  58. {
  59. CUtlBuffer buf;
  60. if ( !g_pFullFileSystem->ReadFile( pFilename, NULL, buf ) )
  61. {
  62. Msg( "Failed to read file, \"%s\"\n", pFilename );
  63. return;
  64. }
  65. int nFileSize = buf.TellPut();
  66. SessionInfoHeader_t header;
  67. if ( !ReadSessionInfoHeader( buf.Base(), nFileSize, header ) )
  68. {
  69. Msg( "Failed to read header information.\n" );
  70. return;
  71. }
  72. char szDigestStr[33];
  73. V_binarytohex( header.m_aHash, sizeof( header.m_aHash ), szDigestStr, sizeof( szDigestStr ) );
  74. Msg( "\n\theader:\n" );
  75. Msg( "\n" );
  76. Msg( "\t%27s: %u\n", "version", header.m_uVersion );
  77. Msg( "\t%27s: %s\n", "session name", header.m_szSessionName );
  78. Msg( "\t%27s: %s\n", "currently recording?", header.m_bRecording ? "yes" : "no" );
  79. Msg( "\t%27s: %i\n", "# blocks", header.m_nNumBlocks );
  80. Msg( "\t%27s: %s\n", "compressor", GetCompressorNameSafe( header.m_nCompressorType ) );
  81. Msg( "\t%27s: %s\n", "md5 digest", szDigestStr );
  82. Msg( "\t%27s: %u bytes\n", "payload size (compressed)", header.m_uPayloadSize );
  83. Msg( "\t%27s: %u bytes\n", "payload size (uncompressed)", header.m_uPayloadSizeUC );
  84. Msg( "\n" );
  85. const uint8 *pPayload = (uint8 *)buf.Base() + sizeof( SessionInfoHeader_t );
  86. uint32 uUncompressedPayloadSize = header.m_uPayloadSizeUC;
  87. if ( !g_pEngine->MD5_HashBuffer( header.m_aHash, (const uint8 *)pPayload, header.m_uPayloadSize, NULL ) )
  88. {
  89. Msg( "Data validation failed.\n" );
  90. return;
  91. }
  92. const uint8 *pUncompressedPayload;
  93. bool bFreeUncompressedPayload = true;
  94. if ( header.m_nCompressorType == COMPRESSORTYPE_INVALID )
  95. {
  96. // The payload is already uncompressed - don't free, since this buffer was allocated by the CUtlBuffer "buf"
  97. pUncompressedPayload = pPayload;
  98. bFreeUncompressedPayload = false;
  99. }
  100. else
  101. {
  102. if ( uUncompressedPayloadSize != header.m_uPayloadSizeUC )
  103. {
  104. Msg( "Decompressed to a different size (%u) than specified by header (%u)\n", uUncompressedPayloadSize, header.m_uPayloadSizeUC );
  105. return;
  106. }
  107. ICompressor *pCompressor = CreateCompressor( header.m_nCompressorType );
  108. if ( !pCompressor )
  109. {
  110. Msg( "Failed to create compressor.\n" );
  111. return;
  112. }
  113. pUncompressedPayload = new uint8[ uUncompressedPayloadSize ];
  114. if ( !pUncompressedPayload )
  115. {
  116. Msg( "Failed to allocate uncompressed payload.\n" );
  117. delete [] pCompressor;
  118. return;
  119. }
  120. pCompressor->Decompress( (char *)pUncompressedPayload, &uUncompressedPayloadSize, (const char *)pPayload, header.m_uPayloadSize );
  121. delete pCompressor;
  122. }
  123. if ( uUncompressedPayloadSize <= MIN_SESSION_INFO_PAYLOAD_SIZE )
  124. {
  125. Msg( "Uncompressed payload not large enough to read a single block.\n" );
  126. }
  127. else
  128. {
  129. RecordingSessionBlockSpec_t DummyBlock;
  130. CUtlBuffer bufPayload( pUncompressedPayload, uUncompressedPayloadSize, CUtlBuffer::READ_ONLY );
  131. Msg( "\n\tblocks:\n\n" );
  132. Msg( "\t index status MD5 compressor size (uncompressed) size (compressed)\n" );
  133. bool bBlockReadFailed = false;
  134. for ( int i = 0; i < header.m_nNumBlocks; ++i )
  135. {
  136. // Attempt to read the current block from the buffer
  137. bufPayload.Get( &DummyBlock, sizeof( DummyBlock ) );
  138. if ( !bufPayload.IsValid() )
  139. {
  140. bBlockReadFailed = true;
  141. break;
  142. }
  143. V_binarytohex( DummyBlock.m_aHash, sizeof( DummyBlock.m_aHash ), szDigestStr, sizeof( szDigestStr ) );
  144. Msg( "\t %5i", DummyBlock.m_iReconstruction );
  145. Msg( "%20s", CBaseRecordingSessionBlock::GetRemoteStatusStringSafe( (CBaseRecordingSessionBlock::RemoteStatus_t)DummyBlock.m_uRemoteStatus ) );
  146. Msg( "%35s", szDigestStr );
  147. Msg( "%8s", GetCompressorNameSafe( (CompressorType_t)DummyBlock.m_nCompressorType ) );
  148. Msg( "%20u", DummyBlock.m_uFileSize );
  149. Msg( "%20u", DummyBlock.m_uUncompressedSize );
  150. Msg( "\n" );
  151. }
  152. }
  153. Msg( "\n" );
  154. if ( bFreeUncompressedPayload )
  155. {
  156. delete [] pUncompressedPayload;
  157. }
  158. }
  159. CON_COMMAND_F( replay_printsessioninfo, "Print session info", FCVAR_DONTRECORD )
  160. {
  161. ENSURE_DEDICATED();
  162. if ( args.ArgC() != 2 )
  163. {
  164. Msg( "Usage: replay_printsessioninfo <full path and filename>\n" );
  165. return;
  166. }
  167. PrintSessionInfo( args[1] );
  168. }
  169. //----------------------------------------------------------------------------------------