//============ Copyright (c) Valve Corporation, All rights reserved. ============ // // work in progress // //=============================================================================== #include "cbase.h" #include "global_event_log.h" #include "filesystem.h" #include "utlbuffer.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifdef _PS3 #define _vscprintf vprintf #define vsprintf_s vsnprintf #endif CGlobalEventLog GlobalEventLog; static CUtlSymbolTable EventSymbols; static ConVar global_event_log_enabled( "global_event_log_enabled", "0", FCVAR_CHEAT, "Enables the global event log system" ); class CGlobalEventLine { public: CGlobalEventLine( ); ~CGlobalEventLine( ); void Clear( ); bool SetStaticText( const char *pszValue ); bool SetVaryingText( const char *pszValue ); bool IsDirty( ) { return m_bDirty; } void Write( CUtlBuffer *pBuffer ); void ClearDirty( ); private: CUtlSymbol m_ValueSymbol; char *m_pszValue; bool m_bDirty; }; CGlobalEventLine::CGlobalEventLine( ) { m_ValueSymbol = UTL_INVAL_SYMBOL; m_pszValue = NULL; m_bDirty = true; } CGlobalEventLine::~CGlobalEventLine( ) { Clear(); } void CGlobalEventLine::Clear( ) { m_ValueSymbol = UTL_INVAL_SYMBOL; if ( m_pszValue != NULL ) { delete m_pszValue; m_pszValue = NULL; } m_bDirty = true; } bool CGlobalEventLine::SetStaticText( const char *pszValue ) { if ( m_ValueSymbol != UTL_INVAL_SYMBOL && m_ValueSymbol == EventSymbols.Find( pszValue ) ) { return false; } Clear(); m_ValueSymbol = EventSymbols.AddString( pszValue ); return true; } bool CGlobalEventLine::SetVaryingText( const char *pszValue ) { if ( m_pszValue && strcmpi( m_pszValue, pszValue ) == 0 ) { // no change return false; } Clear(); m_pszValue = ( char * )malloc( strlen( pszValue ) + 1 ); strcpy( m_pszValue, pszValue ); return true; } void CGlobalEventLine::Write( CUtlBuffer *pBuffer ) { const char *pszValue; if ( m_pszValue != NULL ) { pszValue = m_pszValue; } else { pszValue = EventSymbols.String( m_ValueSymbol ); } if ( strchr( pszValue, ' ' ) != NULL ) { pBuffer->Printf( "\"%s\"\n", pszValue ); } else { pBuffer->Printf( "%s\n", pszValue ); } } void CGlobalEventLine::ClearDirty( ) { m_bDirty = false; } class CGlobalEvent { public: CGlobalEvent( const char *pszName, unsigned int nID, bool bIsHighLevel, CGlobalEvent *pParent = NULL ); bool AddValue( bool bVarying, const char *pszKey, const char *pszValue ); unsigned int GetID( ) { return m_nID; } bool IsDirty( ) { return m_bDirty; } void Write( CUtlBuffer *pBuffer ); void ClearDirty( ); private: unsigned int m_nID; CUtlSymbol m_Name; float m_flTime; bool m_bIsHighLevel; bool m_bFullUpdate; bool m_bDirty; CGlobalEvent *m_pParent; CUtlMap< CUtlSymbol, CGlobalEventLine * > m_EventLines; }; CGlobalEvent::CGlobalEvent( const char *pszName, unsigned int nID, bool bIsHighLevel, CGlobalEvent *pParent ) : m_EventLines( DefLessFunc( const CUtlSymbol ) ) { m_Name = EventSymbols.AddString( pszName ); m_nID = nID; m_bIsHighLevel = bIsHighLevel; m_pParent = pParent; m_bFullUpdate = true; m_flTime = gpGlobals->curtime; } bool CGlobalEvent::AddValue( bool bVarying, const char *pszKey, const char *pszValue ) { CUtlSymbol KeyID; CGlobalEventLine *pEvent; int nIndex; bool bResult; KeyID = EventSymbols.AddString( pszKey ); nIndex = m_EventLines.Find( KeyID ); if ( nIndex == m_EventLines.InvalidIndex() ) { pEvent = new CGlobalEventLine(); m_EventLines.Insert( KeyID, pEvent ); } else { pEvent = m_EventLines.Element( nIndex ); } if ( bVarying ) { bResult = pEvent->SetVaryingText( pszValue ); } else { bResult = pEvent->SetStaticText( pszValue ); } if ( bResult == true ) { m_bDirty = true; m_flTime = gpGlobals->curtime; } return bResult; } void CGlobalEvent::Write( CUtlBuffer *pBuffer ) { pBuffer->Printf( "event %u\n", m_nID ); pBuffer->Printf( "{\n" ); if ( m_bFullUpdate ) { const char *pszName = EventSymbols.String( m_Name ); if ( strchr( pszName, ' ' ) != NULL ) { pBuffer->Printf( "\tName\t\"%s\"\n", pszName ); } else { pBuffer->Printf( "\tName\t%s\n", pszName ); } if ( m_pParent != NULL ) { pBuffer->Printf( "\tParent_ID\t%u\n", m_pParent->GetID() ); } if ( m_bIsHighLevel == true ) { pBuffer->Printf( "\tHighLevel\t1\n" ); } } pBuffer->Printf( "\tTime\t%g\n", m_flTime ); for( unsigned i = 0; i < m_EventLines.Count(); i++ ) { if ( m_EventLines.Element( i )->IsDirty() ) { const char *pszKey = EventSymbols.String( m_EventLines.Key( i ) ); if ( strchr( pszKey, ' ' ) != NULL ) { pBuffer->Printf( "\t\"%s\"\t", pszKey ); } else { pBuffer->Printf( "\t%s\t", pszKey ); } m_EventLines.Element( i )->Write( pBuffer ); } } pBuffer->Printf( "}\n" ); } void CGlobalEvent::ClearDirty( ) { m_bFullUpdate = false; m_bDirty = false; for( unsigned i = 0; i < m_EventLines.Count(); i++ ) { m_EventLines.Element( i )->ClearDirty(); } } CGlobalEventLog::CGlobalEventLog( ) { m_nNextID = 1; } CGlobalEvent *CGlobalEventLog::GetGlobalEvent( EGlobalEvent GlobalEvent ) { return m_pGlobalEvents[ GlobalEvent ]; } CGlobalEvent *CGlobalEventLog::CreateEvent( const char *pszName, bool bIsHighLevel, CGlobalEvent *pParent ) { CGlobalEvent *pEvent = new CGlobalEvent( pszName, m_nNextID, bIsHighLevel, pParent ); m_Events.AddToTail( pEvent ); m_DirtyEvents.AddToTail( pEvent ); m_nNextID++; return pEvent; } CGlobalEvent *CGlobalEventLog::CreateTempEvent( const char *pszName, CGlobalEvent *pParent ) { CGlobalEvent *pEvent = new CGlobalEvent( pszName, m_nNextID, false, pParent ); m_TempEvents.AddToTail( pEvent ); m_DirtyEvents.AddToTail( pEvent ); m_nNextID++; return pEvent; } void CGlobalEventLog::RemoveEvent( CGlobalEvent *pEvent ) { if ( m_Events.FindAndRemove( pEvent ) == true && m_TempEvents.Find( pEvent ) == -1 ) { m_TempEvents.AddToTail( pEvent ); } } void CGlobalEventLog::AddKeyValue( CGlobalEvent *pEvent, bool bVarying, const char *pszKey, const char *pszValueFormat, ... ) { va_list Args; int nLen; char *pszBuffer; CUtlSymbol KeyID; bool bResult; va_start( Args, pszValueFormat ); #if defined(_WIN32) || defined(_PS3) nLen = _vscprintf( pszValueFormat, Args ) + 1; #else nLen = vsnprintf( NULL, 0, pszValueFormat, Args ) + 1; #endif pszBuffer = ( char * )stackalloc( nLen * sizeof( char ) ); V_vsnprintf( pszBuffer, nLen, pszValueFormat, Args ); bResult = pEvent->AddValue( bVarying, pszKey, pszBuffer ); if ( bResult == true && m_DirtyEvents.Find( pEvent ) == -1 ) { m_DirtyEvents.AddToTail( pEvent ); } } void CGlobalEventLog::SendUpdate( ) { if ( m_DirtyEvents.Count() == 0 ) { return; } if ( global_event_log_enabled.GetBool() == true ) { FileHandle_t fh = g_pFullFileSystem->Open( "c:\\o.events", "a" ); CUtlBuffer *pBuffer = new CUtlBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER ); pBuffer->Clear(); for( int i = 0; i < m_DirtyEvents.Count(); i++ ) { m_DirtyEvents[ i ]->Write( pBuffer ); g_pFullFileSystem->Write( pBuffer->Base(), pBuffer->TellPut(), fh ); pBuffer->Clear(); } g_pFullFileSystem->Close( fh ); } for( int i = 0; i < m_DirtyEvents.Count(); i++ ) { m_DirtyEvents[ i ]->ClearDirty(); } m_DirtyEvents.Purge(); for( int i = 0; i < m_TempEvents.Count(); i++ ) { delete m_TempEvents[ i ]; } m_TempEvents.Purge(); } void CGlobalEventLog::PostInit( ) { m_pGlobalEvents[ GLOBAL_EVENT_NPCS ] = CreateEvent( "NPCs", true ); } void CGlobalEventLog::FrameUpdatePostEntityThink( ) { SendUpdate(); }