|
|
|
|
//========= Copyright <EFBFBD> 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 <filename>" )
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------
|