diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /replay/sv_replaycontext.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'replay/sv_replaycontext.cpp')
| -rw-r--r-- | replay/sv_replaycontext.cpp | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/replay/sv_replaycontext.cpp b/replay/sv_replaycontext.cpp new file mode 100644 index 0000000..8c627fe --- /dev/null +++ b/replay/sv_replaycontext.cpp @@ -0,0 +1,326 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +//=======================================================================================// + +#include "sv_replaycontext.h" +#include "replay/shared_defs.h" // BUILD_CURL defined here +#include "sv_sessionrecorder.h" +#include "sv_fileservercleanup.h" +#include "sv_recordingsession.h" +#include "sv_publishtest.h" +#include "replaysystem.h" +#include "icommandline.h" + +#if BUILD_CURL +#include "curl/curl.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//---------------------------------------------------------------------------------------- + +#undef CreateEvent + +//---------------------------------------------------------------------------------------- + +CServerReplayContext::CServerReplayContext() +: m_pSessionRecorder( NULL ), + m_pFileserverCleaner( NULL ), + m_bShouldAbortRecording( false ), + m_flConVarSanityCheckTime( 0.0f ) +{ +} + +CServerReplayContext::~CServerReplayContext() +{ + delete m_pSessionRecorder; + delete m_pFileserverCleaner; +} + +bool CServerReplayContext::Init( CreateInterfaceFn fnFactory ) +{ +#if BUILD_CURL + // Initialize cURL - in windows, this will init the winsock stuff. + curl_global_init( CURL_GLOBAL_ALL ); +#endif + + m_pShared = new CSharedReplayContext( this ); + + m_pShared->m_strSubDir = GetServerSubDirName(); + m_pShared->m_pRecordingSessionManager = new CServerRecordingSessionManager( this ); + m_pShared->m_pRecordingSessionBlockManager = new CServerRecordingSessionBlockManager( this ); + m_pShared->m_pErrorSystem = new CErrorSystem( this ); + + m_pShared->Init( fnFactory ); + + // Create directory for temp files + CFmtStr fmtTmpDir( "%s%s", SV_GetBasePath(), SUBDIR_TMP ); + g_pFullFileSystem->CreateDirHierarchy( fmtTmpDir.Access() ); + + // Remove any extraneous files from the temp directory + CleanTmpDir(); + + m_pSessionRecorder = new CSessionRecorder(); + m_pSessionRecorder->Init(); + + m_pFileserverCleaner = new CFileserverCleaner(); + + return true; +} + +void CServerReplayContext::CleanTmpDir() +{ + int nFilesRemoved = 0; + + Log( "Cleaning files from temp dir, \"%s\" ...", SV_GetTmpDir() ); + + FileFindHandle_t hFind; + CFmtStr fmtPath( "%s*", SV_GetTmpDir() ); + const char *pFilename = g_pFullFileSystem->FindFirst( fmtPath.Access(), &hFind ); + while ( pFilename ) + { + if ( pFilename[0] != '.' ) + { + // Remove the file + CFmtStr fmtFullFilename( "%s%s", SV_GetTmpDir(), pFilename ); + g_pFullFileSystem->RemoveFile( fmtFullFilename.Access() ); + + ++nFilesRemoved; + } + + // Get next file + pFilename = g_pFullFileSystem->FindNext( hFind ); + } + + if ( nFilesRemoved ) + { + Log( "%i %s removed.\n", nFilesRemoved, nFilesRemoved == 1 ? "file" : "files" ); + } + else + { + Log( "no files removed.\n" ); + } +} + +void CServerReplayContext::Shutdown() +{ + m_pShared->Shutdown(); + +#if BUILD_CURL + // Shutdown cURL + curl_global_cleanup(); +#endif +} + +void CServerReplayContext::Think() +{ + ConVarSanityThink(); + + if ( !g_pReplay->IsReplayEnabled() ) + return; + + if ( m_bShouldAbortRecording ) + { + g_pBlockSpewer->PrintBlockStart(); + g_pBlockSpewer->PrintMsg( "Replay recording shutting down due to publishing error! Recording will begin" ); + g_pBlockSpewer->PrintMsg( "at the beginning of the next round, but may fail again." ); + g_pBlockSpewer->PrintBlockEnd(); + + // Shutdown recording + m_pSessionRecorder->AbortCurrentSessionRecording(); + + m_bShouldAbortRecording = false; + } + + m_pShared->Think(); +} + +void CServerReplayContext::ConVarSanityThink() +{ + if ( m_flConVarSanityCheckTime == 0.0f ) + return; + + DoSanityCheckNow(); +} + +void CServerReplayContext::UpdateFileserverIPFromHostname( const char *pHostname ) +{ + if ( !g_pEngine->NET_GetHostnameAsIP( pHostname, m_szFileserverIP, sizeof( m_szFileserverIP ) ) ) + { + V_strcpy( m_szFileserverIP, "0.0.0.0" ); + Log( "ERROR: Could not resolve fileserver hostname \"%s\" !\n", pHostname ); + return; + } + + Log( "Cached resolved fileserver hostname to IP address: \"%s\" -> \"%s\"\n", pHostname, m_szFileserverIP ); +} + +void CServerReplayContext::UpdateFileserverProxyIPFromHostname( const char *pHostname ) +{ + if ( !g_pEngine->NET_GetHostnameAsIP( pHostname, m_szFileserverProxyIP, sizeof( m_szFileserverProxyIP ) ) ) + { + V_strcpy( m_szFileserverProxyIP, "0.0.0.0" ); + Log( "ERROR: Could not resolve fileserver proxy hostname \"%s\" !\n", pHostname ); + return; + } + + Log( "Cached resolved fileserver proxy hostname to IP address: \"%s\" -> \"%s\"\n", pHostname, m_szFileserverProxyIP ); +} + +void CServerReplayContext::DoSanityCheckNow() +{ + // Check now? + if ( m_flConVarSanityCheckTime <= g_pEngine->GetHostTime() ) + { + // Reset + m_flConVarSanityCheckTime = 0.0f; + + g_pBlockSpewer->PrintBlockStart(); + + extern ConVar replay_enable; + if ( replay_enable.GetBool() ) + { + // Test publish + const bool bPublishResult = SV_DoTestPublish(); + + g_pBlockSpewer->PrintEmptyLine(); + + if ( bPublishResult ) + { + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintMsg( "SUCCESS - REPLAY IS ENABLED!" ); + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintMsg( "A 'changelevel' or 'map' is required - recording will" ); + g_pBlockSpewer->PrintMsg( "begin at the start of the next round." ); + g_pBlockSpewer->PrintEmptyLine(); + } + else + { + replay_enable.SetValue( 0 ); + + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintMsg( "FAILURE - REPLAY DISABLED! \"replay_enable\" is now 0." ); + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintEmptyLine(); + g_pBlockSpewer->PrintMsg( "Address any failures above and re-exec replay.cfg." ); + } + } + + g_pBlockSpewer->PrintBlockEnd(); + } +} + +void CServerReplayContext::FlagForConVarSanityCheck() +{ + m_flConVarSanityCheckTime = g_pEngine->GetHostTime() + 0.2f; +} + +IGameEvent *CServerReplayContext::CreateReplaySessionInfoEvent() +{ + IGameEvent *pEvent = g_pGameEventManager->CreateEvent( "replay_sessioninfo", true ); + if ( !pEvent ) + return NULL; + + extern ConVar replay_block_dump_interval; + + // Fill event + pEvent->SetString( "sn", m_pShared->m_pRecordingSessionManager->GetCurrentSessionName() ); + pEvent->SetInt( "di", replay_block_dump_interval.GetInt() ); + pEvent->SetInt( "cb", m_pShared->m_pRecordingSessionManager->GetCurrentSessionBlockIndex() ); + pEvent->SetInt( "st", m_pSessionRecorder->GetCurrentRecordingStartTick() ); + + return pEvent; +} + +IReplaySessionRecorder *CServerReplayContext::GetSessionRecorder() +{ + return g_pServerReplayContext->m_pSessionRecorder; +} + +const char *CServerReplayContext::GetLocalFileServerPath() const +{ + static char s_szBuf[MAX_OSPATH]; + extern ConVar replay_local_fileserver_path; + + // Fix up the path name - NOTE: We intentionally avoid calling V_FixupPathName(), which + // pushes the entire output string to lower case. + V_strncpy( s_szBuf, replay_local_fileserver_path.GetString(), sizeof( s_szBuf ) ); + V_FixSlashes( s_szBuf ); + V_RemoveDotSlashes( s_szBuf ); + V_FixDoubleSlashes( s_szBuf ); + + V_StripTrailingSlash( s_szBuf ); + V_AppendSlash( s_szBuf, sizeof( s_szBuf ) ); + return s_szBuf; +} + +void CServerReplayContext::CreateSessionOnClient( int nClientSlot ) +{ + // If we have a session (i.e. if we're recording) + if ( SV_GetRecordingSessionInProgress() ) + { + // Create the session on the client + IGameEvent *pSessionInfoEvent = CreateReplaySessionInfoEvent(); + g_pReplay->SV_SendReplayEvent( pSessionInfoEvent, nClientSlot ); + } +} + +const char *CServerReplayContext::GetServerSubDirName() const +{ + const char *pSubDirName = CommandLine()->ParmValue( "-replayserverdir" ); + if ( !pSubDirName || !pSubDirName[0] ) + { + Msg( "No '-replayserverdir' parameter found - using default replay folder.\n" ); + return SUBDIR_SERVER; + } + + Msg( "\n** Using custom replay dir name: \"%s%c%s\"\n\n", SUBDIR_REPLAY, CORRECT_PATH_SEPARATOR, pSubDirName ); + + return pSubDirName; +} + +void CServerReplayContext::ReportErrorsToUser( wchar_t *pErrorText ) +{ + char szErrorText[4096]; + g_pVGuiLocalize->ConvertUnicodeToANSI( pErrorText, szErrorText, sizeof( szErrorText ) ); + + static Color s_clrRed( 255, 0, 0 ); + Warning( "\n-----------------------------------------------\n" ); + Warning( "%s", szErrorText ); + Warning( "-----------------------------------------------\n\n" ); +} + +void CServerReplayContext::OnPublishFailed() +{ + // Don't report publish failure and shutdown publishing more than once per session. + if ( !m_pSessionRecorder->RecordingAborted() ) + { + m_bShouldAbortRecording = true; + } +} + +//---------------------------------------------------------------------------------------- + +CServerRecordingSession *SV_GetRecordingSessionInProgress() +{ + return SV_CastSession( SV_GetRecordingSessionManager()->GetRecordingSessionInProgress() ); +} + +const char *SV_GetTmpDir() +{ + return Replay_va( "%s%s%c", SV_GetBasePath(), SUBDIR_TMP, CORRECT_PATH_SEPARATOR ); +} + +bool SV_IsOffloadingEnabled() +{ + return false; +} + +bool SV_RunJobToCompletion( CJob *pJob ) +{ + return RunJobToCompletion( SV_GetThreadPool(), pJob ); +} + +//---------------------------------------------------------------------------------------- |