//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "serverdemo_system.h" #include "serverdemo_types.h" #include "vstdlib/IKeyValuesSystem.h" #include "tier1/circularbuffer.h" #include "toolutils/enginetools_int.h" #include "toolframework/itoolframework.h" #include "toolframework/iserverenginetools.h" #include "serverdemo.h" //------------------------------------------------------------------------------------------------------------------------ bool g_bAllowServerDemoWrite = true; //------------------------------------------------------------------------------------------------------------------------ ConVar sv_demo_entity_record_rate( "sv_demo_entity_record_rate", "30", FCVAR_GAMEDLL | FCVAR_SPONLY | FCVAR_CHEAT, "Set the server demo record rate for entities." ); //------------------------------------------------------------------------------------------------------------------------ class CServerDemoSystem : public IServerDemoSystem // Implementation { public: CServerDemoSystem(); bool Init(); void Shutdown(); virtual void WriteDemoToDiskForClient( int iClient, char const* pFilename ); virtual void PostRecordingMessage( KeyValues* pMsg ); virtual void Think(); virtual void OnInitLevel( char const* pMapName ); virtual void OnShutdownLevel(); bool CreateDemo( char const* pMapName ); void FreeDemo(); CServerDemo* m_pDemo; float m_flLastEntRecordTime; int m_nLastRecordSecond; int m_nFrameCount; }; //------------------------------------------------------------------------------------------------------------------------ CServerDemoSystem::CServerDemoSystem() { Init(); } bool CServerDemoSystem::Init() { m_pDemo = NULL; m_flLastEntRecordTime = 0.0f; m_nFrameCount = 0; m_nLastRecordSecond = 0; return true; } void CServerDemoSystem::Shutdown() { if ( !m_pDemo ) return; FreeDemo(); } void CServerDemoSystem::FreeDemo() { delete m_pDemo; m_pDemo = NULL; } bool CServerDemoSystem::CreateDemo( char const* pMapName ) { m_pDemo = new CServerDemo(); if ( !m_pDemo ) return false; return m_pDemo->Init( pMapName, gpGlobals->curtime ); } void CServerDemoSystem::PostRecordingMessage( KeyValues* pMsg ) { if ( !m_pDemo ) return; m_pDemo->PostRecordingMessage( pMsg, gpGlobals->curtime ); } void CServerDemoSystem::Think() { if ( !g_bAllowServerDemoWrite ) return; if ( !m_pDemo || !m_pDemo->m_pBuffer ) return; // Write how much of buffer has been used engine->Con_NPrintf( 0, "%% circular buffer used: %2f", (float)(m_pDemo->m_pBuffer->GetSize() - m_pDemo->m_pBuffer->GetWriteAvailable()) / m_pDemo->m_pBuffer->GetSize() ); // Write entities? float flRecordRate = MAX( 20.0f, MIN( 60.0f, sv_demo_entity_record_rate.GetFloat() ) ); if ( gpGlobals->curtime - m_flLastEntRecordTime < 1.0f/flRecordRate ) return; int const iCurSecond = (int)gpGlobals->curtime; if ( iCurSecond != m_nLastRecordSecond ) { // DevMsg( "%d: frames record: %d\n", m_lastRecordSecond, m_frameCount ); m_nLastRecordSecond = iCurSecond; m_nFrameCount = 0; } else { ++m_nFrameCount; } for ( CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity != NULL; pEntity = gEntList.NextEnt(pEntity) ) { if ( !pEntity ) continue; KeyValues* pMsg = new KeyValues( "entity" ); // Store server demo ptr pMsg->SetPtr( "serverdemo", m_pDemo ); // Fill msg with state data - only post message if state changed if ( pEntity->GetDemoRecordingState( pMsg ) ) { ServerDemoPacket_BaseEntity* pBaseEntPacket = (ServerDemoPacket_BaseEntity*)pMsg->GetPtr( "baseentity" ); if ( pBaseEntPacket ) { matrix3x4_t m; AngleMatrix( pEntity->GetAbsAngles(), pEntity->GetAbsOrigin(), m ); ServerDemoPacket_BaseAnimating* pBaseAnimatingPacket = (ServerDemoPacket_BaseAnimating*)pMsg->GetPtr( "baseanimating" ); ServerDemoPacket_BaseAnimatingOverlay* pBaseAnimatingPacketOverlay = (ServerDemoPacket_BaseAnimatingOverlay*)pMsg->GetPtr( "baseanimatingoverlay" ); if ( ( pBaseEntPacket->m_fModified != 0 ) || ( pBaseAnimatingPacket && pBaseAnimatingPacket->m_fModified ) || ( pBaseAnimatingPacketOverlay && pBaseAnimatingPacketOverlay->m_fModified ) ) { debugoverlay->AddCoordFrameOverlay( m, 25 ); pBaseEntPacket->AddTextOverlaysForModifiedFields( pEntity->GetAbsOrigin() ); // Add BaseAnimatin text if ( pBaseAnimatingPacket ) { pBaseAnimatingPacket->AddTextOverlaysForModifiedFields( pEntity->GetAbsOrigin() ); } // Add BaseAnimatinOverlay text if ( pBaseAnimatingPacketOverlay ) { pBaseAnimatingPacketOverlay->AddTextOverlaysForModifiedFields( pEntity->GetAbsOrigin() ); } } } // Post a message to the demo system g_pServerDemoSystem->PostRecordingMessage( pMsg ); // No longer first frame pEntity->m_bFirstRecordingFrame = false; } pMsg->deleteThis(); } // Stamp record time m_flLastEntRecordTime = gpGlobals->curtime; } void CServerDemoSystem::WriteDemoToDiskForClient( int iClient, char const* pFilename ) { // TODO: Send the circular buffer to SFM for save to file if ( !serverenginetools->SFM_WriteServerDemoFile( pFilename, m_pDemo ) ) { Warning( "Failed to write server demo file, %s\n", pFilename ); } } void CServerDemoSystem::OnInitLevel( char const* pMapName ) { g_bAllowServerDemoWrite = true; FreeDemo(); Init(); if ( !CreateDemo( pMapName ) ) { Warning( "Failed to create server demo\n" ); FreeDemo(); } } void CServerDemoSystem::OnShutdownLevel() { g_bAllowServerDemoWrite = false; FreeDemo(); } //------------------------------------------------------------------------------------------------------------------------ IServerDemoSystem* g_pServerDemoSystem = NULL; static CServerDemoSystem g_serverDemoSystem; //------------------------------------------------------------------------------------------------------------------------ bool ServerDemoSystem_Init() { Assert( !g_pServerDemoSystem ); // Setup the interface g_pServerDemoSystem = &g_serverDemoSystem; // Init system return g_serverDemoSystem.Init(); // TODO: Should be passed in or accessed from command line, etc. } void ServerDemoSystem_Shutdown() { if ( g_pServerDemoSystem ) { g_serverDemoSystem.Shutdown(); } } //------------------------------------------------------------------------------------------------------------------------ CON_COMMAND( dump_server_demo, "dump_sever_demo " ) { if ( !g_bAllowServerDemoWrite ) { DevMsg( "Server demo not allowed now.\n" ); return; } if ( args.ArgC() != 2 ) { DevMsg( "Please specify an output filename.\n" ); return; } if ( !g_pServerDemoSystem ) { DevMsg( "Server demo system not initialized!\n" ); return; } g_bAllowServerDemoWrite = false; // Use dummy client id for now. g_pServerDemoSystem->WriteDemoToDiskForClient( -1, args[1] ); g_bAllowServerDemoWrite = true; } //------------------------------------------------------------------------------------------------------------------------