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.

211 lines
7.5 KiB

  1. //========= Copyright (c) 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. //=======================================================================================//
  4. #if defined( REPLAY_ENABLED )
  5. #include "replay.h"
  6. #include "convar.h"
  7. #include "cmd.h"
  8. #include "qlimits.h"
  9. #include "client.h"
  10. #include "server.h"
  11. #include "enginesingleuserfilter.h"
  12. #include "cdll_engine_int.h"
  13. #include "filesystem.h"
  14. #include "replayhistorymanager.h"
  15. #include "toolframework/itoolframework.h"
  16. #include "replayserver.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. //----------------------------------------------------------------------------------------
  20. CON_COMMAND_F( loadsfm, "Test -- Loads the SFM", 0 ) // TEST
  21. {
  22. toolframework->LoadFilmmaker();
  23. }
  24. //----------------------------------------------------------------------------------------
  25. ConVar replay_enable( "replay_enable", "0", FCVAR_REPLICATED, "Enable Replay recording on server" );
  26. ConVar replay_snapshotrate("replay_snapshotrate", "16", 0, "Snapshots broadcasted per second" );
  27. static ConVar replay_autoretry( "replay_autoretry", "1", 0, "Relay proxies retry connection after network timeout" );
  28. static ConVar replay_timeout( "replay_timeout", "30", 0, "SourceTV connection timeout in seconds." );
  29. //----------------------------------------------------------------------------------------
  30. bool Replay_IsEnabled()
  31. {
  32. return replay_enable.GetInt() != 0;
  33. }
  34. //----------------------------------------------------------------------------------------
  35. void Replay_OnFileSendComplete( const char *pFilename, int nSize )
  36. {
  37. if ( !replay || !replay->IsActive() )
  38. {
  39. AssertMsg( 0, "Calling Replay_OnFileSendComplete() with inactive Replay!" );
  40. return;
  41. }
  42. // TODO - how can we get a client index here? Do we already have the state in each CNetChan
  43. // and not need to duplicate it in a bitvec?
  44. // Clear client's download bit
  45. // replay->m_vecClientsDownloading.Set( nClientSlot, false );
  46. }
  47. //----------------------------------------------------------------------------------------
  48. CON_COMMAND_F( request_replay_demo, "Request a replay demo from the server.", FCVAR_GAMEDLL )
  49. {
  50. if ( !Replay_IsEnabled() )
  51. {
  52. Msg( "Replay is not enabled.\n" );
  53. return;
  54. }
  55. //------------------------- SERVER ONLY -------------------------
  56. //
  57. // TODO: There should be two paths through this function - one for when a user requests a SPECIFIC FILE,
  58. // and one where the user wants file based on a dump of whatever was recorded in the last N seconds.
  59. //
  60. if ( !replay || !replay->m_DemoRecorder.IsRecording() )
  61. {
  62. SVC_Print msg( "The server is not currently recording replay demos.\n" );
  63. CEngineSingleUserFilter filter( cmd_clientslot + 1, true );
  64. sv.BroadcastMessage( msg, filter );
  65. return;
  66. }
  67. // Make sure client slot is valid
  68. if ( cmd_clientslot < 0 )
  69. {
  70. AssertMsg( 0, "request_replay_demo: Bad client slot." );
  71. Warning( "request_replay_demo: Bad client slot, %d.", cmd_clientslot );
  72. return;
  73. }
  74. // Already downloading a file?
  75. // TODO: need to keep track of a user who is constantly requesting or is that automatic behavior?
  76. if ( replay->m_vecClientsDownloading.IsBitSet( cmd_clientslot ) )
  77. {
  78. SVC_Print msg( "Request denied. You are already downloading.\n" );
  79. CEngineSingleUserFilter filter( cmd_clientslot + 1, true );
  80. sv.BroadcastMessage( msg, filter );
  81. return;
  82. }
  83. // Set the download bit
  84. // TODO
  85. // replay->m_vecClientsDownloading.Set( cmd_clientslot, true );
  86. // Dump current demo buffer to a .dem file for the client
  87. char szFilename[MAX_OSPATH];
  88. replay->m_DemoRecorder.GetUniqueDemoFilename( szFilename, sizeof(szFilename) );
  89. replay->m_DemoRecorder.DumpToFile( szFilename );
  90. // Add to history
  91. // TODO: Pass in proper demo length here
  92. // TODO: Write me.
  93. extern ConVar replay_demolifespan;
  94. CServerReplayHistoryEntryData *pNewServerEntry = new CServerReplayHistoryEntryData();
  95. tm now;
  96. Plat_GetLocalTime( &now );
  97. time_t now_time_t = mktime( &now );
  98. pNewServerEntry->m_nRecordTime = static_cast< uint64 >( now_time_t );
  99. pNewServerEntry->m_nLifeSpan = replay_demolifespan.GetInt() * 24 * 3600;
  100. pNewServerEntry->m_DemoLength.SetSeconds( 0 );
  101. V_strcpy( pNewServerEntry->m_szFilename, szFilename );
  102. V_strcpy( pNewServerEntry->m_szMapName, sv.GetMapName() );
  103. pNewServerEntry->m_uClientSteamId = sv.Client( cmd_clientslot )->m_SteamID.ConvertToUint64();
  104. pNewServerEntry->m_nBytesTransferred = 0;
  105. pNewServerEntry->m_bTransferComplete = false;
  106. pNewServerEntry->m_nSize = g_pFullFileSystem->Size( szFilename );
  107. pNewServerEntry->m_bTransferring = false;
  108. pNewServerEntry->m_nTransferId = -1;
  109. pNewServerEntry->m_nFileStatus = CServerReplayHistoryEntryData::FILESTATUS_EXISTS;
  110. g_pServerReplayHistoryManager->RecordEntry( pNewServerEntry );
  111. // Extract the file stem
  112. char szDemoStem[260];
  113. Q_StripExtension( szFilename, szDemoStem, sizeof(szDemoStem) );
  114. char szCommand[288];
  115. Assert( replay->m_DemoRecorder.m_nStartTick >= 0 );
  116. V_sprintf_safe(
  117. szCommand, "replay_cache_ragdolls %s %d %d",
  118. szDemoStem,
  119. (int)replay->m_DemoRecorder.m_nStartTick,
  120. g_pFullFileSystem->Size( szFilename )
  121. ); // NOTE: m_nStartTick is updated as the "oldest" frame's tick count
  122. // Setup a message
  123. CNETMsg_StringCmd_t msg( szCommand );
  124. // Send the file stem back to the client for merging ragdoll/etc. with demo file
  125. CEngineSingleUserFilter filter( cmd_clientslot + 1, true );
  126. sv.BroadcastMessage( msg, filter );
  127. Msg( "Wrote Replay demo file to disk, %s.\n", szFilename );
  128. }
  129. //----------------------------------------------------------------------------------------
  130. CON_COMMAND_F( replay_cache_ragdolls, "Cache ragdolls to disk", FCVAR_HIDDEN | FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE )
  131. {
  132. if ( args.ArgC() != 4 )
  133. {
  134. AssertMsg( 0, "Bad number of arguments to replay_cache_ragdolls!\n" );
  135. Warning( "Bad number of arguments to replay_cache_ragdolls!\n" );
  136. return;
  137. }
  138. // Tell client to cache ragdolls
  139. char szRagdollCacheFilename[MAX_OSPATH];
  140. const char *pBaseFilename = args[1];
  141. V_snprintf( szRagdollCacheFilename, sizeof( szRagdollCacheFilename ), "%s.dmx", pBaseFilename );
  142. int nStartTick = V_atoi( args[2] );
  143. g_ClientDLL->CacheReplayRagdolls( szRagdollCacheFilename, nStartTick );
  144. char szDemoFilename[MAX_OSPATH];
  145. V_snprintf( szDemoFilename, sizeof( szDemoFilename ), "%s.dem", pBaseFilename );
  146. // Record in client history
  147. extern ConVar replay_demolifespan;
  148. CClientReplayHistoryEntryData *pNewEntry = new CClientReplayHistoryEntryData();
  149. if ( !pNewEntry )
  150. return;
  151. tm now;
  152. Plat_GetLocalTime( &now );
  153. time_t now_time_t = mktime( &now );
  154. pNewEntry->m_nRecordTime = static_cast< int >( now_time_t );
  155. pNewEntry->m_nLifeSpan = replay_demolifespan.GetInt() * 24 * 3600;
  156. pNewEntry->m_DemoLength.SetSeconds( 0 );
  157. V_strcpy( pNewEntry->m_szFilename, szDemoFilename );
  158. V_strcpy( pNewEntry->m_szMapName, GetBaseLocalClient().m_szLevelName );
  159. V_strcpy( pNewEntry->m_szServerAddress, GetBaseLocalClient().m_NetChannel->GetAddress() );
  160. pNewEntry->m_nBytesTransferred = 0;
  161. pNewEntry->m_bTransferComplete = false;
  162. pNewEntry->m_nSize = atoi( args[3] );
  163. pNewEntry->m_bTransferring = false;
  164. pNewEntry->m_nTransferId = -1;
  165. if ( !g_pClientReplayHistoryManager->RecordEntry( pNewEntry ) )
  166. {
  167. Warning( "Replay: Failed to record entry.\n" );
  168. return;
  169. }
  170. // Attempt to download immediately
  171. pNewEntry->BeginDownload();
  172. DevMsg( "Requesting file %s from %s ( %s ).\n", szDemoFilename, GetBaseLocalClient().m_NetChannel->GetName(), GetBaseLocalClient().m_NetChannel->GetAddress() );
  173. }
  174. //----------------------------------------------------------------------------------------
  175. #endif