Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

274 lines
7.0 KiB

//========= 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;
}
//------------------------------------------------------------------------------------------------------------------------