diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/GameStats.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/GameStats.cpp')
| -rw-r--r-- | mp/src/game/shared/GameStats.cpp | 3048 |
1 files changed, 1524 insertions, 1524 deletions
diff --git a/mp/src/game/shared/GameStats.cpp b/mp/src/game/shared/GameStats.cpp index e81eaed6..695dc553 100644 --- a/mp/src/game/shared/GameStats.cpp +++ b/mp/src/game/shared/GameStats.cpp @@ -1,1524 +1,1524 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================
-#include "cbase.h"
-
-#include "igamesystem.h"
-#include "gamestats.h"
-#include "tier1/utlstring.h"
-#include "filesystem.h"
-#include "tier1/utlbuffer.h"
-#include "fmtstr.h"
-
-#ifndef SWDS
-#include "iregistry.h"
-#endif
-
-#include "tier1/utldict.h"
-#include "tier0/icommandline.h"
-#include <time.h>
-#ifdef GAME_DLL
-#include "vehicle_base.h"
-#endif
-
-#if defined( _X360 )
-#include "xbox/xbox_win32stubs.h"
-#endif
-
-#ifdef CLIENT_DLL
-#include "materialsystem/materialsystem_config.h"
-#include "vgui_int.h"
-#include "igameresources.h"
-#include "voice_status.h"
-extern const ConVar *sv_cheats;
-#if !defined(NO_STEAM)
-#include "steam/steam_api.h"
-#endif
-#endif
-
-
-#if !defined(NO_STEAM) && defined(CLIENT_DLL)
-#if defined(TF_CLIENT_DLL) || defined(CSTRIKE_DLL)
-#define STEAMWORKS_GAMESTATS_ACTIVE
-#include "steamworks_gamestats.h"
-#endif
-#endif
-
-// NOTE: This has to be the last file included!
-#include "tier0/memdbgon.h"
-
-
-#define GAMESTATS_LOG_FILE "gamestats.log"
-#define GAMESTATS_PATHID "MOD"
-
-/*
-#define ONE_DAY_IN_SECONDS 86400
-
-// Lower threshold in debug for testing...
-#if defined( _DEBUG )
-#define WALKED_AWAY_FROM_KEYBOARD_SECONDS 15.0f // 15 seconds of movement == might be paused
-#else
-#define WALKED_AWAY_FROM_KEYBOARD_SECONDS 300.0f // 5 minutes of no movement == might be paused
-#endif
-*/
-
-extern IUploadGameStats *gamestatsuploader;
-
-static char s_szPseudoUniqueID[20] = "";
-
-static inline char const *SafeString( char const *pStr )
-{
- return ( pStr ) ? pStr : "?";
-}
-
-static CBaseGameStats s_GameStats_Singleton;
-CBaseGameStats *gamestats = &s_GameStats_Singleton; //start out pointing at the basic version which does nothing by default
-extern ConVar skill;
-void OverWriteCharsWeHate( char *pStr );
-
-bool StatsTrackingIsFullyEnabled( void );
-
-class CGamestatsData
-{
-public:
- CGamestatsData()
- {
- m_pKVData = NULL;
- m_bHaveData = false;
- AllocData();
- }
-
- ~CGamestatsData()
- {
- FreeData();
- }
-
- void AllocData()
- {
- FreeData();
- m_pKVData = new KeyValues( "gamestats" );
- }
- void FreeData()
- {
- if ( m_pKVData != NULL )
- {
- m_pKVData->deleteThis();
- m_pKVData = NULL;
- }
- }
-
- KeyValues *m_pKVData;
- bool m_bHaveData;
-};
-
-CBaseGameStats_Driver CBGSDriver;
-
-void UpdatePerfStats( void )
-{
- CBGSDriver.UpdatePerfStats();
-}
-
-CBaseGameStats_Driver::CBaseGameStats_Driver( void ) :
- BaseClass( "CGameStats" ),
- m_iLoadedVersion( -1 ),
- m_bEnabled( false ),
- m_bShuttingDown( false ),
- m_bInLevel( false ),
- m_bFirstLevel( true ),
- m_flLevelStartTime( 0.0f ),
- m_bStationary( false ),
- m_flLastMovementTime( 0.0f ),
- m_bGamePaused( false ),
- m_pGamestatsData( NULL ),
- m_bBufferFull( false ),
- m_nWriteIndex( 0 ),
- m_flLastRealTime( -1 ),
- m_flLastSampleTime( -1 ),
- m_flTotalTimeInLevels( 0 ),
- m_iNumLevels( 0 ),
- m_bDidVoiceChat( false )
-
-{
- m_szLoadedUserID[0] = 0;;
- m_tLastUpload = 0;
- m_LastUserCmd.Reset();
-}
-
-static FileHandle_t g_LogFileHandle = FILESYSTEM_INVALID_HANDLE;
-
-CBaseGameStats::CBaseGameStats() :
- m_bLogging( false ),
- m_bLoggingToFile( false )
-{
-}
-
-bool CBaseGameStats::StatTrackingAllowed( void )
-{
- return CBGSDriver.m_bEnabled;
-}
-
-// Don't care about vcr hooks here...
-#undef localtime
-#undef asctime
-
-#include <time.h>
-
-void CBaseGameStats::StatsLog( char const *fmt, ... )
-{
- if ( !m_bLogging && !m_bLoggingToFile )
- return;
-
- char buf[ 2048 ];
- va_list argptr;
- va_start( argptr, fmt );
- Q_vsnprintf( buf, sizeof( buf ), fmt, argptr );
- va_end( argptr );
-
- // Prepend timestamp and spew it
-
- // Prepend the time.
- time_t aclock;
- time( &aclock );
- struct tm *newtime = localtime( &aclock );
-
- char timeString[ 128 ];
- Q_strncpy( timeString, asctime( newtime ), sizeof( timeString ) );
- // Get rid of the \n.
- char *pEnd = strstr( timeString, "\n" );
- if ( pEnd )
- {
- *pEnd = 0;
- }
-
- if ( m_bLogging )
- {
- DevMsg( "[GS %s - %7.2f] %s", timeString, gpGlobals->realtime, buf );
- }
-
- if ( m_bLoggingToFile )
- {
- if ( FILESYSTEM_INVALID_HANDLE == g_LogFileHandle )
- {
- g_LogFileHandle = filesystem->Open( GAMESTATS_LOG_FILE, "a", GAMESTATS_PATHID );
- }
-
- if ( FILESYSTEM_INVALID_HANDLE != g_LogFileHandle )
- {
- filesystem->FPrintf( g_LogFileHandle, "[GS %s - %7.2f] %s", timeString, gpGlobals->realtime, buf );
- filesystem->Flush( g_LogFileHandle );
- }
- }
-}
-
-static char s_szSaveFileName[256] = "";
-static char s_szStatUploadRegistryKeyName[256] = "";
-
-const char *CBaseGameStats::GetStatSaveFileName( void )
-{
- AssertMsg( s_szSaveFileName[0] != '\0', "Don't know what file to save stats to." );
- return s_szSaveFileName;
-}
-
-const char *CBaseGameStats::GetStatUploadRegistryKeyName( void )
-{
- AssertMsg( s_szStatUploadRegistryKeyName[0] != '\0', "Don't know the registry key to use to mark stats uploads." );
- return s_szStatUploadRegistryKeyName;
-}
-
-const char *CBaseGameStats::GetUserPseudoUniqueID( void )
-{
- AssertMsg( s_szPseudoUniqueID[0] != '\0', "Don't have a pseudo unique ID." );
- return s_szPseudoUniqueID;
-}
-
-void CBaseGameStats::Event_Init( void )
-{
-#ifdef GAME_DLL
- SetHL2UnlockedChapterStatistic();
- SetSteamStatistic( filesystem->IsSteam() );
- SetCyberCafeStatistic( gamestatsuploader->IsCyberCafeUser() );
- ConVarRef pDXLevel( "mat_dxlevel" );
- if( pDXLevel.IsValid() )
- {
- SetDXLevelStatistic( pDXLevel.GetInt() );
- }
- ++m_BasicStats.m_Summary.m_nCount;
-
- StatsLog( "CBaseGameStats::Event_Init [%dth session]\n", m_BasicStats.m_Summary.m_nCount );
-#endif // GAME_DLL
-}
-
-void CBaseGameStats::Event_Shutdown( void )
-{
-#ifdef GAME_DLL
- StatsLog( "CBaseGameStats::Event_Shutdown [%dth session]\n", m_BasicStats.m_Summary.m_nCount );
-
- StatsLog( "\n====================================================================\n\n" );
-#endif
-}
-
-void CBaseGameStats::Event_MapChange( const char *szOldMapName, const char *szNewMapName )
-{
- StatsLog( "CBaseGameStats::Event_MapChange to [%s]\n", szNewMapName );
-}
-
-void CBaseGameStats::Event_LevelInit( void )
-{
-#ifdef GAME_DLL
- StatsLog( "CBaseGameStats::Event_LevelInit [%s]\n", CBGSDriver.m_PrevMapName.String() );
-
- BasicGameStatsRecord_t *map = gamestats->m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nCount;
-
- // HACK HACK: Punching this hole through only works in single player!!!
- if ( gpGlobals->maxClients == 1 )
- {
- ConVarRef closecaption( "closecaption" );
- if( closecaption.IsValid() )
- SetCaptionsStatistic( closecaption.GetBool() );
-
- SetHDRStatistic( gamestatsuploader->IsHDREnabled() );
-
- SetSkillStatistic( skill.GetInt() );
- SetSteamStatistic( filesystem->IsSteam() );
- SetCyberCafeStatistic( gamestatsuploader->IsCyberCafeUser() );
- }
-#endif // GAME_DLL
-}
-
-void CBaseGameStats::Event_LevelShutdown( float flElapsed )
-{
-#ifdef GAME_DLL
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- Assert( map );
- map->m_nSeconds += (int)flElapsed;
- gamestats->m_BasicStats.m_Summary.m_nSeconds += (int)flElapsed;
-
- StatsLog( "CBaseGameStats::Event_LevelShutdown [%s] %.2f elapsed %d total\n", CBGSDriver.m_PrevMapName.String(), flElapsed, gamestats->m_BasicStats.m_Summary.m_nSeconds );
-#endif // GAME_DLL
-}
-
-void CBaseGameStats::Event_SaveGame( void )
-{
- StatsLog( "CBaseGameStats::Event_SaveGame [%s]\n", CBGSDriver.m_PrevMapName.String() );
-}
-
-void CBaseGameStats::Event_LoadGame( void )
-{
-#ifdef GAME_DLL
- char const *pchSaveFile = engine->GetMostRecentlyLoadedFileName();
- StatsLog( "CBaseGameStats::Event_LoadGame [%s] from %s\n", CBGSDriver.m_PrevMapName.String(), pchSaveFile );
-#endif
-}
-
-#ifdef GAME_DLL
-
-void CBaseGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info )
-{
- ++m_BasicStats.m_Summary.m_nDeaths;
-
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nDeaths;
- StatsLog( " Player died %dth time in level [%s]!!!\n", map->m_nDeaths, CBGSDriver.m_PrevMapName.String() );
- }
- else
- {
- StatsLog( " Player died, but not in a level!!!\n" );
- Assert( 0 );
- }
-
- StatsLog( "CBaseGameStats::Event_PlayerKilled [%s] [%dth death]\n", pPlayer->GetPlayerName(), m_BasicStats.m_Summary.m_nDeaths );
-}
-
-void CBaseGameStats::Event_Commentary()
-{
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nCommentary;
- }
-
- ++m_BasicStats.m_Summary.m_nCommentary;
-
- StatsLog( "CBaseGameStats::Event_Commentary [%d]\n", m_BasicStats.m_Summary.m_nCommentary );
-}
-
-void CBaseGameStats::Event_Credits()
-{
- StatsLog( "CBaseGameStats::Event_Credits\n" );
-
- float elapsed = 0.0f;
- if( CBGSDriver.m_bInLevel )
- {
- elapsed = gpGlobals->realtime - CBGSDriver.m_flLevelStartTime;
- }
-
- if( elapsed < 0.0f )
- {
- Assert( 0 );
- Warning( "EVENT_CREDITS with negative elapsed time (rt %f starttime %f)\n", gpGlobals->realtime, CBGSDriver.m_flLevelStartTime );
- elapsed = 0.0f;
- }
-
- // Only set this one time!!!
- if( gamestats->m_BasicStats.m_nSecondsToCompleteGame == 0 )
- {
- if( gamestats->UserPlayedAllTheMaps() )
- {
- gamestats->m_BasicStats.m_nSecondsToCompleteGame = elapsed + gamestats->m_BasicStats.m_Summary.m_nSeconds;
- gamestats->SaveToFileNOW();
- }
- }
-}
-
-void CBaseGameStats::Event_CrateSmashed()
-{
- StatsLog( "CBaseGameStats::Event_CrateSmashed\n" );
-}
-
-void CBaseGameStats::Event_Punted( CBaseEntity *pObject )
-{
- StatsLog( "CBaseGameStats::Event_Punted [%s]\n", pObject->GetClassname() );
-}
-
-void CBaseGameStats::Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting )
-{
-}
-
-void CBaseGameStats::Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle )
-{
- StatsLog( "CBaseGameStats::Event_FlippedVehicle [%s] flipped [%s]\n", pDriver->GetPlayerName(), pVehicle->GetClassname() );
-}
-
-// Called before .sav file is actually loaded (player should still be in previous level, if any)
-void CBaseGameStats::Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame )
-{
- StatsLog( "CBaseGameStats::Event_PreSaveGameLoaded [%s] %s\n", pSaveName, bInGame ? "in-game" : "at console" );
-}
-
-bool CBaseGameStats::SaveToFileNOW( bool bForceSyncWrite /* = false */ )
-{
- if ( !StatsTrackingIsFullyEnabled() )
- return false;
-
- // this code path is only for old format stats. Products that use new format take a different path.
- if ( !gamestats->UseOldFormat() )
- return false;
-
- CUtlBuffer buf;
- buf.PutShort( GAMESTATS_FILE_VERSION );
- buf.Put( s_szPseudoUniqueID, 16 );
-
- if( ShouldTrackStandardStats() )
- m_BasicStats.SaveToBuffer( buf );
- else
- buf.PutInt( GAMESTATS_STANDARD_NOT_SAVED );
-
- gamestats->AppendCustomDataToSaveBuffer( buf );
-
- char fullpath[ 512 ] = { 0 };
- if ( filesystem->FileExists( GetStatSaveFileName(), GAMESTATS_PATHID ) )
- {
- filesystem->RelativePathToFullPath( GetStatSaveFileName(), GAMESTATS_PATHID, fullpath, sizeof( fullpath ) );
- }
- else
- {
- // filename is local to game dir for Steam, so we need to prepend game dir for regular file save
- char gamePath[256];
- engine->GetGameDir( gamePath, 256 );
- Q_StripTrailingSlash( gamePath );
- Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", gamePath, GetStatSaveFileName() );
- Q_strlower( fullpath );
- Q_FixSlashes( fullpath );
- }
-
- // StatsLog( "SaveToFileNOW '%s'\n", fullpath );
-
- if( CBGSDriver.m_bShuttingDown || bForceSyncWrite ) //write synchronously
- {
- filesystem->WriteFile( fullpath, GAMESTATS_PATHID, buf );
-
- StatsLog( "Shut down wrote to '%s'\n", fullpath );
- }
- else
- {
- // Allocate memory for async system to use (and free afterward!!!)
- size_t nBufferSize = buf.TellPut();
- void *pMem = malloc(nBufferSize);
- CUtlBuffer statsBuffer( pMem, nBufferSize );
- statsBuffer.Put( buf.Base(), nBufferSize );
-
- // Write data async
- filesystem->AsyncWrite( fullpath, statsBuffer.Base(), statsBuffer.TellPut(), true, false );
- }
-
- return true;
-}
-
-void CBaseGameStats::Event_PlayerConnected( CBasePlayer *pBasePlayer )
-{
- StatsLog( "CBaseGameStats::Event_PlayerConnected [%s]\n", pBasePlayer->GetPlayerName() );
-}
-
-void CBaseGameStats::Event_PlayerDisconnected( CBasePlayer *pBasePlayer )
-{
- StatsLog( "CBaseGameStats::Event_PlayerDisconnected\n" );
-}
-
-void CBaseGameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info )
-{
- //StatsLog( "CBaseGameStats::Event_PlayerDamage [%s] took %.2f damage\n", pBasePlayer->GetPlayerName(), info.GetDamage() );
-}
-
-void CBaseGameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info )
-{
- StatsLog( "CBaseGameStats::Event_PlayerKilledOther [%s] killed [%s]\n", pAttacker->GetPlayerName(), pVictim->GetClassname() );
-}
-
-void CBaseGameStats::Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName )
-{
- StatsLog( "CBaseGameStats::Event_WeaponFired [%s] %s weapon [%s]\n", pShooter->GetPlayerName(), bPrimary ? "primary" : "secondary", pchWeaponName );
-}
-
-void CBaseGameStats::Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info )
-{
- StatsLog( "CBaseGameStats::Event_WeaponHit [%s] %s weapon [%s] damage [%f]\n", pShooter->GetPlayerName(), bPrimary ? "primary" : "secondary", pchWeaponName, info.GetDamage() );
-}
-
-void CBaseGameStats::Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer )
-{
- StatsLog( "CBaseGameStats::Event_PlayerEnteredGodMode [%s] entered GOD mode\n", pBasePlayer->GetPlayerName() );
-}
-
-void CBaseGameStats::Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer )
-{
- StatsLog( "CBaseGameStats::Event_PlayerEnteredNoClip [%s] entered NOCLIPe\n", pBasePlayer->GetPlayerName() );
-}
-
-void CBaseGameStats::Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer )
-{
- StatsLog( "CBaseGameStats::Event_DecrementPlayerEnteredNoClip [%s] decrementing NOCLIPe\n", pBasePlayer->GetPlayerName() );
-}
-
-void CBaseGameStats::Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount )
-{
- StatsLog( "Incrementing %s by %f at pos (%d, %d, %d)\n", pchStatisticName, flIncrementAmount, (int)vecAbsOrigin.x, (int)vecAbsOrigin.y, (int)vecAbsOrigin.z );
-}
-
-//=============================================================================
-// HPE_BEGIN
-// [dwenger] Needed for CS window-breaking stat
-//=============================================================================
-void CBaseGameStats::Event_WindowShattered( CBasePlayer *pPlayer )
-{
- StatsLog( "In Event_WindowShattered\n" );
-}
-//=============================================================================
-// HPE_END
-//=============================================================================
-
-bool CBaseGameStats::UploadStatsFileNOW( void )
-{
- if( !StatsTrackingIsFullyEnabled() || !HaveValidData() || !gamestats->UseOldFormat() )
- return false;
-
- if ( !filesystem->FileExists( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID ) )
- {
- return false;
- }
-
- int curtime = Plat_FloatTime();
-
- CBGSDriver.m_tLastUpload = curtime;
-
- // Update the registry
-#ifndef SWDS
- IRegistry *reg = InstanceRegistry( "Steam" );
- Assert( reg );
- reg->WriteInt( GetStatUploadRegistryKeyName(), CBGSDriver.m_tLastUpload );
- ReleaseInstancedRegistry( reg );
-#endif
-
- CUtlBuffer buf;
- filesystem->ReadFile( GetStatSaveFileName(), GAMESTATS_PATHID, buf );
- unsigned int uBlobSize = buf.TellPut();
- if ( uBlobSize == 0 )
- {
- return false;
- }
-
- const void *pvBlobData = ( const void * )buf.Base();
-
- if( gamestatsuploader )
- {
- return gamestatsuploader->UploadGameStats( "",
- 1,
- uBlobSize,
- pvBlobData );
- }
-
- return false;
-}
-
-
-void CBaseGameStats::LoadingEvent_PlayerIDDifferentThanLoadedStats( void )
-{
- StatsLog( "CBaseGameStats::LoadingEvent_PlayerIDDifferentThanLoadedStats\n" );
-}
-
-
-bool CBaseGameStats::LoadFromFile( void )
-{
- if ( filesystem->FileExists( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID ) )
- {
- char fullpath[ 512 ];
- filesystem->RelativePathToFullPath( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID, fullpath, sizeof( fullpath ) );
- StatsLog( "Loading stats from '%s'\n", fullpath );
- }
-
- CUtlBuffer buf;
- if ( filesystem->ReadFile( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID, buf ) )
- {
- bool bRetVal = true;
-
- int version = buf.GetShort();
- if ( version > GAMESTATS_FILE_VERSION )
- return false; //file is beyond our comprehension
-
- // Set global parse version
- CBGSDriver.m_iLoadedVersion = version;
-
- buf.Get( CBGSDriver.m_szLoadedUserID, 16 );
- CBGSDriver.m_szLoadedUserID[ sizeof( CBGSDriver.m_szLoadedUserID ) - 1 ] = 0;
-
- if ( s_szPseudoUniqueID[ 0 ] != 0 )
- {
- if ( Q_stricmp( CBGSDriver.m_szLoadedUserID, s_szPseudoUniqueID ) )
- {
- //UserID changed, blow away log!!!
- filesystem->RemoveFile( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID );
- filesystem->RemoveFile( GAMESTATS_LOG_FILE, GAMESTATS_PATHID );
- Warning( "Userid changed, clearing stats file\n" );
- CBGSDriver.m_szLoadedUserID[0] = '\0';
- CBGSDriver.m_iLoadedVersion = -1;
- gamestats->m_BasicStats.Clear();
- gamestats->LoadingEvent_PlayerIDDifferentThanLoadedStats();
- bRetVal = false;
- }
-
- if ( version <= GAMESTATS_FILE_VERSION_OLD5 )
- {
- gamestats->m_BasicStats.Clear();
- bRetVal = false;
- }
- else
- {
- // Peek ahead in buffer to see if we have the "no default stats" secret flag set.
- int iCheckForStandardStatsInFile = *( int * )buf.PeekGet();
- bool bValid = true;
-
- if ( iCheckForStandardStatsInFile != GAMESTATS_STANDARD_NOT_SAVED )
- {
- //the GAMESTATS_STANDARD_NOT_SAVED flag coincides with user completion time, rewind so the gamestats parser can grab it
- bValid = gamestats->m_BasicStats.ParseFromBuffer( buf, version );
- }
- else
- {
- // skip over the flag
- buf.GetInt();
- }
-
- if( !bValid )
- {
- m_BasicStats.Clear();
- }
-
- if( ( buf.TellPut() - buf.TellGet() ) != 0 ) //more data left, must be custom data
- {
- gamestats->LoadCustomDataFromBuffer( buf );
- }
- }
- }
-
- return bRetVal;
- }
- else
- {
- filesystem->RemoveFile( GAMESTATS_LOG_FILE, GAMESTATS_PATHID );
- }
-
- return false;
-}
-
-void CBaseGameStats::CollectData( StatSendType_t sendType )
-{
- CBGSDriver.CollectData( sendType );
-}
-
-void CBaseGameStats::SendData()
-{
- CBGSDriver.SendData();
-}
-
-
-#endif // GAME_DLL
-
-bool CBaseGameStats_Driver::Init()
-{
- const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" );
-
- //standardizing is a good thing
- char szLoweredGameDir[256];
- Q_strncpy( szLoweredGameDir, pGameDir, sizeof( szLoweredGameDir ) );
- Q_strlower( szLoweredGameDir );
-
- gamestats = gamestats->OnInit( gamestats, szLoweredGameDir );
-
- //determine constant strings needed for saving and uploading
- Q_strncpy( s_szSaveFileName, szLoweredGameDir, sizeof( s_szSaveFileName ) );
- Q_strncat( s_szSaveFileName, "_gamestats.dat", sizeof( s_szSaveFileName ) );
-
- Q_strncpy( s_szStatUploadRegistryKeyName, "GameStatsUpload_", sizeof( s_szStatUploadRegistryKeyName ) );
- Q_strncat( s_szStatUploadRegistryKeyName, szLoweredGameDir, sizeof( s_szStatUploadRegistryKeyName ) );
-
- gamestats->m_bLoggingToFile = CommandLine()->FindParm( "-gamestatsloggingtofile" ) ? true : false;
- gamestats->m_bLogging = CommandLine()->FindParm( "-gamestatslogging" ) ? true : false;
-
- if ( gamestatsuploader )
- {
- m_bEnabled = gamestatsuploader->IsGameStatsLoggingEnabled();
- if ( m_bEnabled )
- {
- gamestatsuploader->GetPseudoUniqueId( s_szPseudoUniqueID, sizeof( s_szPseudoUniqueID ) );
- }
- }
-
- ResetData();
-
-#ifdef GAME_DLL
- if ( StatsTrackingIsFullyEnabled() )
- {
- // FIXME: Load m_tLastUpload from registry and save it back out, too
-#ifndef SWDS
- IRegistry *reg = InstanceRegistry( "Steam" );
- Assert( reg );
- m_tLastUpload = reg->ReadInt( gamestats->GetStatUploadRegistryKeyName(), 0 );
- ReleaseInstancedRegistry( reg );
-#endif
- //load existing stats
- gamestats->LoadFromFile();
- }
-#endif // GAME_DLL
-
- if ( s_szPseudoUniqueID[ 0 ] != 0 )
- {
- gamestats->Event_Init();
-#ifdef GAME_DLL
- if ( gamestats->UseOldFormat() )
- {
- if( gamestats->AutoSave_OnInit() )
- gamestats->SaveToFileNOW();
-
- if( gamestats->AutoUpload_OnInit() )
- gamestats->UploadStatsFileNOW();
- }
-#endif
- }
- else
- {
- m_bEnabled = false; //unable to generate a pseudo-unique ID, disable tracking
- }
-
- return true;
-}
-
-
-void CBaseGameStats_Driver::Shutdown()
-{
- m_bShuttingDown = true;
-
- gamestats->Event_Shutdown();
-
- if ( gamestats->UseOldFormat() )
- {
-#ifdef GAME_DLL
- if( gamestats->AutoSave_OnShutdown() )
- gamestats->SaveToFileNOW();
-
- if( gamestats->AutoUpload_OnShutdown() )
- gamestats->UploadStatsFileNOW();
-#endif // GAME_DLL
- }
- else
- {
- // code path for new format game stats
- if ( gamestats->ShouldSendDataOnAppShutdown() )
- {
- CollectData( STATSEND_APPSHUTDOWN );
- SendData();
- }
- }
- if ( FILESYSTEM_INVALID_HANDLE != g_LogFileHandle )
- {
- filesystem->Close( g_LogFileHandle );
- g_LogFileHandle = FILESYSTEM_INVALID_HANDLE;
- }
-
- if ( m_pGamestatsData != NULL )
- {
-#ifdef CLIENT_DLL
- engine->SetGamestatsData( NULL );
-#endif
- delete m_pGamestatsData;
- m_pGamestatsData = NULL;
- }
-}
-
-void CBaseGameStats_Driver::UpdatePerfStats( void )
-{
- float flCurTime = Plat_FloatTime();
- if (
- ( m_flLastSampleTime == -1 ) ||
- ( flCurTime - m_flLastSampleTime >= STATS_RECORD_INTERVAL ) )
- {
- if ( ( m_flLastRealTime > 0 ) && ( flCurTime > m_flLastRealTime ) )
- {
- float flFrameRate = 1.0 / ( flCurTime - m_flLastRealTime );
- StatsBufferRecord_t &stat = m_StatsBuffer[m_nWriteIndex];
- stat.m_flFrameRate = flFrameRate;
-#ifdef CLIENT_DLL
- // The stat system isn't designed to handle split screen players. Until it get's
- // redesigned, let's take the first player's split screen ping, since all other stats
- // will be based on the first player
- IGameResources *gr = GameResources();
- int ping = 0;
- C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); // GetLocalPlayer( FirstValidSplitScreenSlot() );
- if ( pPlayer && gr )
- {
- ping = gr->GetPing( pPlayer->entindex() );
- }
- stat.m_flServerPing = ping;
-#endif
- AdvanceIndex();
- m_flLastSampleTime = flCurTime;
- }
- }
- m_flLastRealTime = flCurTime;
-
-#ifdef CLIENT_DLL
- if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
- {
- m_bDidVoiceChat |= GetClientVoiceMgr()->IsLocalPlayerSpeaking();
- }
-#endif
-}
-
-void CBaseGameStats_Driver::PossibleMapChange( void )
-{
-#ifdef GAME_DLL
- //detect and copy map changes
- if ( Q_stricmp( m_PrevMapName.String(), STRING( gpGlobals->mapname ) ) )
- {
- MEM_ALLOC_CREDIT();
-
- CUtlString PrevMapBackup = m_PrevMapName;
-
- m_PrevMapName = STRING( gpGlobals->mapname );
-
- gamestats->Event_MapChange( PrevMapBackup.String(), STRING( gpGlobals->mapname ) );
-
- if ( gamestats->UseOldFormat() )
- {
- if( gamestats->AutoSave_OnMapChange() )
- gamestats->SaveToFileNOW();
-
- if( gamestats->AutoUpload_OnMapChange() )
- gamestats->UploadStatsFileNOW();
- }
- }
-#endif
-}
-
-
-
-void CBaseGameStats_Driver::LevelInitPreEntity()
-{
- m_bInLevel = true;
- m_bFirstLevel = false;
-
- if ( Q_stricmp( s_szPseudoUniqueID, "unknown" ) == 0 )
- {
- // "unknown" means this is a dedicated server and we weren't able to generate a unique ID (e.g. Linux server).
- // Change the unique ID to be a hash of IP & port. We couldn't do this earlier because IP is not known until level
- // init time.
- ConVar *hostip = cvar->FindVar( "hostip" );
- ConVar *hostport = cvar->FindVar( "hostport" );
- if ( hostip && hostport )
- {
- int crcInput[2];
- crcInput[0] = hostip->GetInt();
- crcInput[1] = hostport->GetInt();
- if ( crcInput[0] && crcInput[1] )
- {
- CRC32_t crc = CRC32_ProcessSingleBuffer( crcInput, sizeof( crcInput ) );
- Q_snprintf( s_szPseudoUniqueID, ARRAYSIZE( s_szPseudoUniqueID ), "H:%x", crc );
- }
- }
- }
-
- PossibleMapChange();
-
- m_flPauseStartTime = 0.0f;
- m_flLevelStartTime = gpGlobals->realtime;
-
- gamestats->Event_LevelInit();
-
-#ifdef GAME_DLL
- if ( gamestats->UseOldFormat() )
- {
- if( gamestats->AutoSave_OnLevelInit() )
- gamestats->SaveToFileNOW();
-
- if( gamestats->AutoUpload_OnLevelInit() )
- gamestats->UploadStatsFileNOW();
- }
-#endif
-}
-
-
-void CBaseGameStats_Driver::LevelShutdownPreEntity()
-{
-#ifdef CLIENT_DLL
- LevelShutdown();
-#endif
-}
-
-void CBaseGameStats_Driver::LevelShutdownPreClearSteamAPIContext()
-{
-#ifdef GAME_DLL
- LevelShutdown();
-#endif
-}
-
-void CBaseGameStats_Driver::LevelShutdown()
-{
- float flElapsed = gpGlobals->realtime - m_flLevelStartTime;
-
- if ( flElapsed < 0.0f )
- {
- Assert( 0 );
- Warning( "EVENT_LEVELSHUTDOWN: with negative elapsed time (rt %f starttime %f)\n", gpGlobals->realtime, m_flLevelStartTime );
- flElapsed = 0.0f;
- }
-
- //Assert( m_bInLevel ); //so, apparently shutdowns can happen before inits
-
-#ifdef GAME_DLL
- if ( m_bInLevel && ( gpGlobals->eLoadType != MapLoad_Background ) )
-#else
- if ( m_bInLevel )
-#endif
- {
- m_flTotalTimeInLevels += flElapsed;
- m_iNumLevels ++;
-
- gamestats->Event_LevelShutdown( flElapsed );
-
- if ( gamestats->UseOldFormat() )
- {
-#ifdef GAME_DLL
- if( gamestats->AutoSave_OnLevelShutdown() )
- gamestats->SaveToFileNOW( true );
-
- if( gamestats->AutoUpload_OnLevelShutdown() )
- gamestats->UploadStatsFileNOW();
-#endif
- }
- else
- {
- // code path for new format game stats
- CollectData( STATSEND_LEVELSHUTDOWN );
- if ( gamestats->ShouldSendDataOnLevelShutdown() )
- {
- SendData();
- }
- }
- m_bInLevel = false;
- }
-}
-
-void CBaseGameStats_Driver::OnSave()
-{
- gamestats->Event_SaveGame();
-}
-
-
-void CBaseGameStats_Driver::CollectData( StatSendType_t sendType )
-{
- CGamestatsData *pGamestatsData = NULL;
-#ifdef GAME_DLL
- // for server, check with the engine to see if there already a gamestats data container registered. (There will be if there is a client
- // running in the same process.)
- pGamestatsData = engine->GetGamestatsData();
- if ( pGamestatsData )
- {
- // use the registered gamestats container, so free the one we allocated
- if ( m_pGamestatsData != NULL )
- {
- delete m_pGamestatsData;
- m_pGamestatsData = NULL;
- }
- }
- else
- {
- pGamestatsData = m_pGamestatsData;
- }
-#else
- pGamestatsData = m_pGamestatsData;
-#endif
- Assert( pGamestatsData );
- KeyValues *pKV = pGamestatsData->m_pKVData;
-
- int iAppID = engine->GetAppID();
- pKV->SetInt( "appid", iAppID );
-
- switch ( sendType )
- {
- case STATSEND_LEVELSHUTDOWN:
- {
- // make a map node in the KeyValues to use for this level
- char szMap[MAX_PATH+1]="";
-#ifdef CLIENT_DLL
- Q_FileBase( MapName(), szMap, ARRAYSIZE( szMap ) );
-#else
- Q_strncpy( szMap, gpGlobals->mapname.ToCStr(), ARRAYSIZE( szMap ) );
-#endif // CLIENT_DLL
- if ( !szMap[0] )
- return;
- KeyValues *pKVMap = new KeyValues( "map" );
- pKV->AddSubKey( pKVMap );
- pKVMap->SetString( "mapname", szMap );
- pKV = pKVMap;
-
- }
- break;
- case STATSEND_APPSHUTDOWN:
- break;
- default:
- Assert( false );
- break;
- }
-
- // add common data
- pGamestatsData->m_bHaveData |= AddBaseDataForSend( pKV, sendType );
-
-#if defined(STEAMWORKS_GAMESTATS_ACTIVE)
- // At the end of every map, clients submit their perfdata for the map
- if ( sendType == STATSEND_LEVELSHUTDOWN && pGamestatsData && pGamestatsData->m_bHaveData )
- {
- GetSteamWorksSGameStatsUploader().AddClientPerfData( pGamestatsData->m_pKVData );
- }
- GetSteamWorksSGameStatsUploader().LevelShutdown();
-#endif
-
- // add game-specific data
- pGamestatsData->m_bHaveData |= gamestats->AddDataForSend( pKV, sendType );
-
-// Need to initialiate a reset since cs isn't using the gamestat system to add data
-#if defined(CSTRIKE_DLL) && defined(CLIENT_DLL)
- ResetData();
-#endif
-}
-
-
-void CBaseGameStats_Driver::SendData()
-{
- // if we don't own the data container or there's no valid data, nothing to do
- if ( !m_pGamestatsData || !m_pGamestatsData->m_bHaveData )
- return;
-
- // save the data to a buffer
- CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
- m_pGamestatsData->m_pKVData->RecursiveSaveToFile( buf, 0 );
-
- if ( CommandLine()->FindParm( "-gamestatsfileoutputonly" ) )
- {
- // write file for debugging
- const char szFileName[] = "gamestats.dat";
- filesystem->WriteFile( szFileName, GAMESTATS_PATHID, buf );
- }
- else
- {
- // upload the file to Steam
- if ( gamestatsuploader )
- gamestatsuploader->UploadGameStats( "", 1, buf.TellPut(), buf.Base() );
- }
-
- ResetData();
-}
-
-bool CBaseGameStats_Driver::AddBaseDataForSend( KeyValues *pKV, StatSendType_t sendType )
-{
- switch ( sendType )
- {
- case STATSEND_APPSHUTDOWN:
-#ifdef CLIENT_DLL
- if ( m_iNumLevels > 0 )
- {
- // add playtime data
- KeyValues *pKVData = new KeyValues( "playtime" );
- pKVData->SetInt( "TotalLevelTime", m_flTotalTimeInLevels );
- pKVData->SetInt( "NumLevels", m_iNumLevels );
- pKV->AddSubKey( pKVData );
- return true;
- }
-#endif
- break;
- case STATSEND_LEVELSHUTDOWN:
-#ifdef CLIENT_DLL
- if ( m_bBufferFull )
- {
- // add perf data
- KeyValues *pKVPerf = new KeyValues( "perfdata" );
- float flAverageFrameRate = AverageStat( &StatsBufferRecord_t::m_flFrameRate );
- float flMinFrameRate = MinStat( &StatsBufferRecord_t::m_flFrameRate );
- float flMaxFrameRate = MaxStat( &StatsBufferRecord_t::m_flFrameRate );
-
-
- float flDeviationsquared = 0;
- // Compute std deviation
- for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
- {
- float val = m_StatsBuffer[i].m_flFrameRate - flAverageFrameRate;
- flDeviationsquared += ( val * val );
- }
-
- float var = flDeviationsquared / (float)( STATS_WINDOW_SIZE - 1 );
- float flStandardDeviationFrameRate = sqrt( var );
-
- pKVPerf->SetFloat( "AvgFPS", flAverageFrameRate );
- pKVPerf->SetFloat( "MinFPS", flMinFrameRate );
- pKVPerf->SetFloat( "MaxFPS", flMaxFrameRate );
- pKVPerf->SetFloat( "StdDevFPS", flStandardDeviationFrameRate );
-
- // Determine the min/avg/max Server Ping and store the results
- float flAverageServerPing = AverageStat( &StatsBufferRecord_t::m_flServerPing );
- pKVPerf->SetFloat( "AvgServerPing", flAverageServerPing );
-
- pKV->AddSubKey( pKVPerf );
-
- // Only keeping track of using voice in a multiplayer game
- if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
- {
- pKV->SetInt( "UsedVoice", m_bDidVoiceChat );
- }
-
- extern ConVar closecaption;
- pKV->SetInt( "Caption", closecaption.GetInt() );
-
-#ifndef NO_STEAM
- // We can now get the game language from steam :)
- if ( steamapicontext && steamapicontext->SteamApps() )
- {
- const char *currentLanguage = steamapicontext->SteamApps()->GetCurrentGameLanguage();
- pKV->SetString( "Language", currentLanguage ? currentLanguage : "unknown" );
- }
-#endif
-
- // We need to filter out client side dev work from playtest work for the stat reporting.
- // The simplest way is to check for sv_cheats, since we also do NOT want client stat reports
- // where the player has cheated.
- if ( NULL != sv_cheats )
- {
- int iCheats = sv_cheats->GetInt();
- pKV->SetInt( "Cheats", iCheats );
- }
-
- int mapTime = gpGlobals->realtime - m_flLevelStartTime;
- pKV->SetInt( "MapTime", mapTime );
-
- return true;
- }
-#endif
- break;
- }
-
- return false;
-}
-
-
-void CBaseGameStats_Driver::ResetData()
-{
-#ifdef GAME_DLL
- // on the server, if there is a gamestats data container registered (by a client in the same process), they're in charge of resetting it, nothing for us to do
- if ( engine->GetGamestatsData() != NULL )
- return;
-#endif
-
- if ( m_pGamestatsData != NULL )
- {
- delete m_pGamestatsData;
- m_pGamestatsData = NULL;
- }
-
- m_bDidVoiceChat = false;
- m_pGamestatsData = new CGamestatsData();
- KeyValues *pKV = m_pGamestatsData->m_pKVData;
- pKV->SetInt( "IsPc", IsPC() );
- pKV->SetInt( "version", GAMESTATS_VERSION );
- pKV->SetString( "srcid", s_szPseudoUniqueID );
-
-#ifdef CLIENT_DLL
- const CPUInformation &cpu = *GetCPUInformation();
- OverWriteCharsWeHate( cpu.m_szProcessorID );
- pKV->SetString( "CPUID", cpu.m_szProcessorID );
- pKV->SetFloat( "CPUGhz", cpu.m_Speed * ( 1.0 / 1.0e9 ) );
- pKV->SetInt( "NumCores", cpu.m_nPhysicalProcessors );
-
- MaterialAdapterInfo_t gpu;
- materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), gpu );
-
- CMatRenderContextPtr pRenderContext( materials );
- int dest_width,dest_height;
- pRenderContext->GetRenderTargetDimensions( dest_width, dest_height );
-
- if ( gpu.m_pDriverName )
- {
- OverWriteCharsWeHate( gpu.m_pDriverName );
- }
- pKV->SetString( "GPUDrv", SafeString( gpu.m_pDriverName ) );
- pKV->SetInt( "GPUVendor", gpu.m_VendorID );
- pKV->SetInt( "GPUDeviceID", gpu.m_DeviceID );
- pKV->SetString( "GPUDriverVersion", CFmtStr( "%d.%d", gpu.m_nDriverVersionHigh, gpu.m_nDriverVersionLow ) );
- pKV->SetInt( "DxLvl", g_pMaterialSystemHardwareConfig->GetDXSupportLevel() );
- pKV->SetInt( "Width", dest_width );
- pKV->SetInt( "Height", dest_height );
- const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard();
- pKV->SetInt( "Windowed", config.Windowed() == true );
-
- engine->SetGamestatsData( m_pGamestatsData );
-#endif
-
-#if defined(CSTRIKE_DLL) && defined(CLIENT_DLL)
- // reset perf buffer for next map
- for( int i = 0; i < STATS_WINDOW_SIZE; i++ )
- {
- m_StatsBuffer[i].m_flFrameRate = 0;
- m_StatsBuffer[i].m_flServerPing = 0;
- }
-
- m_bBufferFull = false;
- m_nWriteIndex = 0;
-#endif
-}
-
-void CBaseGameStats_Driver::OnRestore()
-{
- PossibleMapChange();
-
- gamestats->Event_LoadGame();
-}
-
-
-void CBaseGameStats_Driver::FrameUpdatePostEntityThink()
-{
- bool bGamePaused = ( gpGlobals->frametime == 0.0f );
-
- if ( !m_bInLevel )
- {
- m_flPauseStartTime = 0.0f;
- }
- else if ( m_bGamePaused != bGamePaused )
- {
- if ( bGamePaused )
- {
- m_flPauseStartTime = gpGlobals->realtime;
- }
- else if ( m_flPauseStartTime != 0.0f )
- {
- float flPausedTime = gpGlobals->realtime - m_flPauseStartTime;
- if ( flPausedTime < 0.0f )
- {
- Assert( 0 );
- Warning( "Game paused time showing up negative (rt %f pausestart %f)\n", gpGlobals->realtime, m_flPauseStartTime );
- flPausedTime = 0.0f;
- }
-
- // Remove this from global time counters
-
- // Msg( "Pause: adding %f to level starttime\n", flPausedTime );
-
- m_flLevelStartTime += flPausedTime;
- m_flPauseStartTime = 0.0f;
-
- // Msg( "Paused for %.2f seconds\n", flPausedTime );
- }
- m_bGamePaused = bGamePaused;
- }
-}
-
-bool StatsTrackingIsFullyEnabled( void )
-{
- return CBGSDriver.m_bEnabled && gamestats->StatTrackingEnabledForMod();
-}
-
-void CBaseGameStats::Clear( void )
-{
-#ifdef GAME_DLL
- gamestats->m_BasicStats.Clear();
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Nukes any dangerous characters and replaces w/space char
-//-----------------------------------------------------------------------------
-void OverWriteCharsWeHate( char *pStr )
-{
- while( *pStr )
- {
- switch( *pStr )
- {
- case '\n':
- case '\r':
- case '\\':
- case '\"':
- case '\'':
- case '\032':
- case ';':
- *pStr = ' ';
- }
- pStr++;
- }
-}
-
-#ifdef GAME_DLL
-
-void CBaseGameStats::SetSteamStatistic( bool bUsingSteam )
-{
- if( CBGSDriver.m_bFirstLevel )
- {
- m_BasicStats.m_Summary.m_bSteam = bUsingSteam;
- }
-
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- map->m_bSteam = bUsingSteam;
- }
-
- m_BasicStats.m_bSteam = bUsingSteam;
-}
-
-void CBaseGameStats::SetCyberCafeStatistic( bool bIsCyberCafeUser )
-{
- if( CBGSDriver.m_bFirstLevel )
- {
- m_BasicStats.m_Summary.m_bCyberCafe = bIsCyberCafeUser;
- }
-
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- map->m_bCyberCafe = bIsCyberCafeUser;
- }
-
- m_BasicStats.m_bCyberCafe = bIsCyberCafeUser;
-}
-
-void CBaseGameStats::SetHDRStatistic( bool bHDREnabled )
-{
- if( bHDREnabled )
- {
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nHDR;
- }
-
- if( CBGSDriver.m_bFirstLevel )
- {
- ++m_BasicStats.m_Summary.m_nHDR;
- }
- }
-}
-
-void CBaseGameStats::SetCaptionsStatistic( bool bClosedCaptionsEnabled )
-{
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nCaptions;
- }
-
- if( CBGSDriver.m_bFirstLevel )
- {
- ++m_BasicStats.m_Summary.m_nCaptions;
- }
-}
-
-void CBaseGameStats::SetSkillStatistic( int iSkillSetting )
-{
- int skill = clamp( iSkillSetting, 1, 3 ) - 1;
-
- if( CBGSDriver.m_bInLevel )
- {
- BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() );
- ++map->m_nSkill[ skill ];
- }
-
- if ( CBGSDriver. m_bFirstLevel )
- {
- ++m_BasicStats.m_Summary.m_nSkill[ skill ];
- }
-}
-
-void CBaseGameStats::SetDXLevelStatistic( int iDXLevel )
-{
- m_BasicStats.m_nDXLevel = iDXLevel;
-}
-
-void CBaseGameStats::SetHL2UnlockedChapterStatistic( void )
-{
- // Now grab the hl2/cfg/config.cfg and suss out the sv_unlockedchapters cvar to estimate how far they got in HL2
- char const *relative = "cfg/config.cfg";
- char fullpath[ 512 ];
- char gamedir[256];
- engine->GetGameDir( gamedir, 256 );
- Q_snprintf( fullpath, sizeof( fullpath ), "%s/../hl2/%s", gamedir, relative );
-
- if ( filesystem->FileExists( fullpath ) )
- {
- FileHandle_t fh = filesystem->Open( fullpath, "rb" );
- if ( FILESYSTEM_INVALID_HANDLE != fh )
- {
- // read file into memory
- int size = filesystem->Size(fh);
- char *configBuffer = new char[ size + 1 ];
- filesystem->Read( configBuffer, size, fh );
- configBuffer[size] = 0;
- filesystem->Close( fh );
-
- // loop through looking for all the cvars to apply
- const char *search = Q_stristr(configBuffer, "sv_unlockedchapters" );
- if ( search )
- {
- // read over the token
- search = strtok( (char *)search, " \n" );
- search = strtok( NULL, " \n" );
-
- if ( search[0]== '\"' )
- ++search;
-
- // read the value
- int iChapter = Q_atoi( search );
- m_BasicStats.m_nHL2ChaptureUnlocked = iChapter;
- }
-
- // free
- delete [] configBuffer;
- }
- }
-}
-
-static void CC_ResetGameStats( const CCommand &args )
-{
-#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
- // Disabled this until we fix the TF gamestat crashes that result
- return;
-#endif
-
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- gamestats->Clear();
- gamestats->SaveToFileNOW();
- gamestats->StatsLog( "CC_ResetGameStats : Server cleared game stats\n" );
-}
-
-static ConCommand resetGameStats("_resetgamestats", CC_ResetGameStats, "Erases current game stats and writes out a blank stats file", 0 );
-
-class CPointGamestatsCounter : public CPointEntity
-{
-public:
- DECLARE_CLASS( CPointGamestatsCounter, CPointEntity );
- DECLARE_DATADESC();
-
- CPointGamestatsCounter();
-
-protected:
-
- void InputSetName( inputdata_t &inputdata );
- void InputIncrement( inputdata_t &inputdata );
-
- void InputEnable( inputdata_t &inputdata );
- void InputDisable( inputdata_t &inputdata );
-private:
-
- string_t m_strStatisticName;
- bool m_bDisabled;
-};
-
-BEGIN_DATADESC( CPointGamestatsCounter )
-
- DEFINE_KEYFIELD( m_strStatisticName, FIELD_STRING, "Name" ),
- DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_STRING, "SetName", InputSetName ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "Increment", InputIncrement ),
-
- DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( point_gamestats_counter, CPointGamestatsCounter )
-
-
-CPointGamestatsCounter::CPointGamestatsCounter() :
- m_strStatisticName( NULL_STRING ),
- m_bDisabled( false )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Changes name of statistic
-//-----------------------------------------------------------------------------
-void CPointGamestatsCounter::InputSetName( inputdata_t &inputdata )
-{
- m_strStatisticName = inputdata.value.StringID();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Changes name of statistic
-//-----------------------------------------------------------------------------
-void CPointGamestatsCounter::InputIncrement( inputdata_t &inputdata )
-{
- if ( m_bDisabled )
- return;
-
- if ( NULL_STRING == m_strStatisticName )
- {
- DevMsg( 1, "CPointGamestatsCounter::InputIncrement: No stat name specified for point_gamestats_counter @%f, %f, %f [ent index %d]\n",
- GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, entindex() );
- return;
- }
-
- gamestats->Event_IncrementCountedStatistic( GetAbsOrigin(), STRING( m_strStatisticName ), inputdata.value.Float() );
-}
-
-void CPointGamestatsCounter::InputEnable( inputdata_t &inputdata )
-{
- m_bDisabled = false;
-}
-
-void CPointGamestatsCounter::InputDisable( inputdata_t &inputdata )
-{
- m_bDisabled = true;
-}
-
-#endif // GAME_DLL
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= +#include "cbase.h" + +#include "igamesystem.h" +#include "gamestats.h" +#include "tier1/utlstring.h" +#include "filesystem.h" +#include "tier1/utlbuffer.h" +#include "fmtstr.h" + +#ifndef SWDS +#include "iregistry.h" +#endif + +#include "tier1/utldict.h" +#include "tier0/icommandline.h" +#include <time.h> +#ifdef GAME_DLL +#include "vehicle_base.h" +#endif + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +#ifdef CLIENT_DLL +#include "materialsystem/materialsystem_config.h" +#include "vgui_int.h" +#include "igameresources.h" +#include "voice_status.h" +extern const ConVar *sv_cheats; +#if !defined(NO_STEAM) +#include "steam/steam_api.h" +#endif +#endif + + +#if !defined(NO_STEAM) && defined(CLIENT_DLL) +#if defined(TF_CLIENT_DLL) || defined(CSTRIKE_DLL) +#define STEAMWORKS_GAMESTATS_ACTIVE +#include "steamworks_gamestats.h" +#endif +#endif + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + + +#define GAMESTATS_LOG_FILE "gamestats.log" +#define GAMESTATS_PATHID "MOD" + +/* +#define ONE_DAY_IN_SECONDS 86400 + +// Lower threshold in debug for testing... +#if defined( _DEBUG ) +#define WALKED_AWAY_FROM_KEYBOARD_SECONDS 15.0f // 15 seconds of movement == might be paused +#else +#define WALKED_AWAY_FROM_KEYBOARD_SECONDS 300.0f // 5 minutes of no movement == might be paused +#endif +*/ + +extern IUploadGameStats *gamestatsuploader; + +static char s_szPseudoUniqueID[20] = ""; + +static inline char const *SafeString( char const *pStr ) +{ + return ( pStr ) ? pStr : "?"; +} + +static CBaseGameStats s_GameStats_Singleton; +CBaseGameStats *gamestats = &s_GameStats_Singleton; //start out pointing at the basic version which does nothing by default +extern ConVar skill; +void OverWriteCharsWeHate( char *pStr ); + +bool StatsTrackingIsFullyEnabled( void ); + +class CGamestatsData +{ +public: + CGamestatsData() + { + m_pKVData = NULL; + m_bHaveData = false; + AllocData(); + } + + ~CGamestatsData() + { + FreeData(); + } + + void AllocData() + { + FreeData(); + m_pKVData = new KeyValues( "gamestats" ); + } + void FreeData() + { + if ( m_pKVData != NULL ) + { + m_pKVData->deleteThis(); + m_pKVData = NULL; + } + } + + KeyValues *m_pKVData; + bool m_bHaveData; +}; + +CBaseGameStats_Driver CBGSDriver; + +void UpdatePerfStats( void ) +{ + CBGSDriver.UpdatePerfStats(); +} + +CBaseGameStats_Driver::CBaseGameStats_Driver( void ) : + BaseClass( "CGameStats" ), + m_iLoadedVersion( -1 ), + m_bEnabled( false ), + m_bShuttingDown( false ), + m_bInLevel( false ), + m_bFirstLevel( true ), + m_flLevelStartTime( 0.0f ), + m_bStationary( false ), + m_flLastMovementTime( 0.0f ), + m_bGamePaused( false ), + m_pGamestatsData( NULL ), + m_bBufferFull( false ), + m_nWriteIndex( 0 ), + m_flLastRealTime( -1 ), + m_flLastSampleTime( -1 ), + m_flTotalTimeInLevels( 0 ), + m_iNumLevels( 0 ), + m_bDidVoiceChat( false ) + +{ + m_szLoadedUserID[0] = 0;; + m_tLastUpload = 0; + m_LastUserCmd.Reset(); +} + +static FileHandle_t g_LogFileHandle = FILESYSTEM_INVALID_HANDLE; + +CBaseGameStats::CBaseGameStats() : + m_bLogging( false ), + m_bLoggingToFile( false ) +{ +} + +bool CBaseGameStats::StatTrackingAllowed( void ) +{ + return CBGSDriver.m_bEnabled; +} + +// Don't care about vcr hooks here... +#undef localtime +#undef asctime + +#include <time.h> + +void CBaseGameStats::StatsLog( char const *fmt, ... ) +{ + if ( !m_bLogging && !m_bLoggingToFile ) + return; + + char buf[ 2048 ]; + va_list argptr; + va_start( argptr, fmt ); + Q_vsnprintf( buf, sizeof( buf ), fmt, argptr ); + va_end( argptr ); + + // Prepend timestamp and spew it + + // Prepend the time. + time_t aclock; + time( &aclock ); + struct tm *newtime = localtime( &aclock ); + + char timeString[ 128 ]; + Q_strncpy( timeString, asctime( newtime ), sizeof( timeString ) ); + // Get rid of the \n. + char *pEnd = strstr( timeString, "\n" ); + if ( pEnd ) + { + *pEnd = 0; + } + + if ( m_bLogging ) + { + DevMsg( "[GS %s - %7.2f] %s", timeString, gpGlobals->realtime, buf ); + } + + if ( m_bLoggingToFile ) + { + if ( FILESYSTEM_INVALID_HANDLE == g_LogFileHandle ) + { + g_LogFileHandle = filesystem->Open( GAMESTATS_LOG_FILE, "a", GAMESTATS_PATHID ); + } + + if ( FILESYSTEM_INVALID_HANDLE != g_LogFileHandle ) + { + filesystem->FPrintf( g_LogFileHandle, "[GS %s - %7.2f] %s", timeString, gpGlobals->realtime, buf ); + filesystem->Flush( g_LogFileHandle ); + } + } +} + +static char s_szSaveFileName[256] = ""; +static char s_szStatUploadRegistryKeyName[256] = ""; + +const char *CBaseGameStats::GetStatSaveFileName( void ) +{ + AssertMsg( s_szSaveFileName[0] != '\0', "Don't know what file to save stats to." ); + return s_szSaveFileName; +} + +const char *CBaseGameStats::GetStatUploadRegistryKeyName( void ) +{ + AssertMsg( s_szStatUploadRegistryKeyName[0] != '\0', "Don't know the registry key to use to mark stats uploads." ); + return s_szStatUploadRegistryKeyName; +} + +const char *CBaseGameStats::GetUserPseudoUniqueID( void ) +{ + AssertMsg( s_szPseudoUniqueID[0] != '\0', "Don't have a pseudo unique ID." ); + return s_szPseudoUniqueID; +} + +void CBaseGameStats::Event_Init( void ) +{ +#ifdef GAME_DLL + SetHL2UnlockedChapterStatistic(); + SetSteamStatistic( filesystem->IsSteam() ); + SetCyberCafeStatistic( gamestatsuploader->IsCyberCafeUser() ); + ConVarRef pDXLevel( "mat_dxlevel" ); + if( pDXLevel.IsValid() ) + { + SetDXLevelStatistic( pDXLevel.GetInt() ); + } + ++m_BasicStats.m_Summary.m_nCount; + + StatsLog( "CBaseGameStats::Event_Init [%dth session]\n", m_BasicStats.m_Summary.m_nCount ); +#endif // GAME_DLL +} + +void CBaseGameStats::Event_Shutdown( void ) +{ +#ifdef GAME_DLL + StatsLog( "CBaseGameStats::Event_Shutdown [%dth session]\n", m_BasicStats.m_Summary.m_nCount ); + + StatsLog( "\n====================================================================\n\n" ); +#endif +} + +void CBaseGameStats::Event_MapChange( const char *szOldMapName, const char *szNewMapName ) +{ + StatsLog( "CBaseGameStats::Event_MapChange to [%s]\n", szNewMapName ); +} + +void CBaseGameStats::Event_LevelInit( void ) +{ +#ifdef GAME_DLL + StatsLog( "CBaseGameStats::Event_LevelInit [%s]\n", CBGSDriver.m_PrevMapName.String() ); + + BasicGameStatsRecord_t *map = gamestats->m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nCount; + + // HACK HACK: Punching this hole through only works in single player!!! + if ( gpGlobals->maxClients == 1 ) + { + ConVarRef closecaption( "closecaption" ); + if( closecaption.IsValid() ) + SetCaptionsStatistic( closecaption.GetBool() ); + + SetHDRStatistic( gamestatsuploader->IsHDREnabled() ); + + SetSkillStatistic( skill.GetInt() ); + SetSteamStatistic( filesystem->IsSteam() ); + SetCyberCafeStatistic( gamestatsuploader->IsCyberCafeUser() ); + } +#endif // GAME_DLL +} + +void CBaseGameStats::Event_LevelShutdown( float flElapsed ) +{ +#ifdef GAME_DLL + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + Assert( map ); + map->m_nSeconds += (int)flElapsed; + gamestats->m_BasicStats.m_Summary.m_nSeconds += (int)flElapsed; + + StatsLog( "CBaseGameStats::Event_LevelShutdown [%s] %.2f elapsed %d total\n", CBGSDriver.m_PrevMapName.String(), flElapsed, gamestats->m_BasicStats.m_Summary.m_nSeconds ); +#endif // GAME_DLL +} + +void CBaseGameStats::Event_SaveGame( void ) +{ + StatsLog( "CBaseGameStats::Event_SaveGame [%s]\n", CBGSDriver.m_PrevMapName.String() ); +} + +void CBaseGameStats::Event_LoadGame( void ) +{ +#ifdef GAME_DLL + char const *pchSaveFile = engine->GetMostRecentlyLoadedFileName(); + StatsLog( "CBaseGameStats::Event_LoadGame [%s] from %s\n", CBGSDriver.m_PrevMapName.String(), pchSaveFile ); +#endif +} + +#ifdef GAME_DLL + +void CBaseGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ) +{ + ++m_BasicStats.m_Summary.m_nDeaths; + + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nDeaths; + StatsLog( " Player died %dth time in level [%s]!!!\n", map->m_nDeaths, CBGSDriver.m_PrevMapName.String() ); + } + else + { + StatsLog( " Player died, but not in a level!!!\n" ); + Assert( 0 ); + } + + StatsLog( "CBaseGameStats::Event_PlayerKilled [%s] [%dth death]\n", pPlayer->GetPlayerName(), m_BasicStats.m_Summary.m_nDeaths ); +} + +void CBaseGameStats::Event_Commentary() +{ + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nCommentary; + } + + ++m_BasicStats.m_Summary.m_nCommentary; + + StatsLog( "CBaseGameStats::Event_Commentary [%d]\n", m_BasicStats.m_Summary.m_nCommentary ); +} + +void CBaseGameStats::Event_Credits() +{ + StatsLog( "CBaseGameStats::Event_Credits\n" ); + + float elapsed = 0.0f; + if( CBGSDriver.m_bInLevel ) + { + elapsed = gpGlobals->realtime - CBGSDriver.m_flLevelStartTime; + } + + if( elapsed < 0.0f ) + { + Assert( 0 ); + Warning( "EVENT_CREDITS with negative elapsed time (rt %f starttime %f)\n", gpGlobals->realtime, CBGSDriver.m_flLevelStartTime ); + elapsed = 0.0f; + } + + // Only set this one time!!! + if( gamestats->m_BasicStats.m_nSecondsToCompleteGame == 0 ) + { + if( gamestats->UserPlayedAllTheMaps() ) + { + gamestats->m_BasicStats.m_nSecondsToCompleteGame = elapsed + gamestats->m_BasicStats.m_Summary.m_nSeconds; + gamestats->SaveToFileNOW(); + } + } +} + +void CBaseGameStats::Event_CrateSmashed() +{ + StatsLog( "CBaseGameStats::Event_CrateSmashed\n" ); +} + +void CBaseGameStats::Event_Punted( CBaseEntity *pObject ) +{ + StatsLog( "CBaseGameStats::Event_Punted [%s]\n", pObject->GetClassname() ); +} + +void CBaseGameStats::Event_PlayerTraveled( CBasePlayer *pBasePlayer, float distanceInInches, bool bInVehicle, bool bSprinting ) +{ +} + +void CBaseGameStats::Event_FlippedVehicle( CBasePlayer *pDriver, CPropVehicleDriveable *pVehicle ) +{ + StatsLog( "CBaseGameStats::Event_FlippedVehicle [%s] flipped [%s]\n", pDriver->GetPlayerName(), pVehicle->GetClassname() ); +} + +// Called before .sav file is actually loaded (player should still be in previous level, if any) +void CBaseGameStats::Event_PreSaveGameLoaded( char const *pSaveName, bool bInGame ) +{ + StatsLog( "CBaseGameStats::Event_PreSaveGameLoaded [%s] %s\n", pSaveName, bInGame ? "in-game" : "at console" ); +} + +bool CBaseGameStats::SaveToFileNOW( bool bForceSyncWrite /* = false */ ) +{ + if ( !StatsTrackingIsFullyEnabled() ) + return false; + + // this code path is only for old format stats. Products that use new format take a different path. + if ( !gamestats->UseOldFormat() ) + return false; + + CUtlBuffer buf; + buf.PutShort( GAMESTATS_FILE_VERSION ); + buf.Put( s_szPseudoUniqueID, 16 ); + + if( ShouldTrackStandardStats() ) + m_BasicStats.SaveToBuffer( buf ); + else + buf.PutInt( GAMESTATS_STANDARD_NOT_SAVED ); + + gamestats->AppendCustomDataToSaveBuffer( buf ); + + char fullpath[ 512 ] = { 0 }; + if ( filesystem->FileExists( GetStatSaveFileName(), GAMESTATS_PATHID ) ) + { + filesystem->RelativePathToFullPath( GetStatSaveFileName(), GAMESTATS_PATHID, fullpath, sizeof( fullpath ) ); + } + else + { + // filename is local to game dir for Steam, so we need to prepend game dir for regular file save + char gamePath[256]; + engine->GetGameDir( gamePath, 256 ); + Q_StripTrailingSlash( gamePath ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", gamePath, GetStatSaveFileName() ); + Q_strlower( fullpath ); + Q_FixSlashes( fullpath ); + } + + // StatsLog( "SaveToFileNOW '%s'\n", fullpath ); + + if( CBGSDriver.m_bShuttingDown || bForceSyncWrite ) //write synchronously + { + filesystem->WriteFile( fullpath, GAMESTATS_PATHID, buf ); + + StatsLog( "Shut down wrote to '%s'\n", fullpath ); + } + else + { + // Allocate memory for async system to use (and free afterward!!!) + size_t nBufferSize = buf.TellPut(); + void *pMem = malloc(nBufferSize); + CUtlBuffer statsBuffer( pMem, nBufferSize ); + statsBuffer.Put( buf.Base(), nBufferSize ); + + // Write data async + filesystem->AsyncWrite( fullpath, statsBuffer.Base(), statsBuffer.TellPut(), true, false ); + } + + return true; +} + +void CBaseGameStats::Event_PlayerConnected( CBasePlayer *pBasePlayer ) +{ + StatsLog( "CBaseGameStats::Event_PlayerConnected [%s]\n", pBasePlayer->GetPlayerName() ); +} + +void CBaseGameStats::Event_PlayerDisconnected( CBasePlayer *pBasePlayer ) +{ + StatsLog( "CBaseGameStats::Event_PlayerDisconnected\n" ); +} + +void CBaseGameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info ) +{ + //StatsLog( "CBaseGameStats::Event_PlayerDamage [%s] took %.2f damage\n", pBasePlayer->GetPlayerName(), info.GetDamage() ); +} + +void CBaseGameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + StatsLog( "CBaseGameStats::Event_PlayerKilledOther [%s] killed [%s]\n", pAttacker->GetPlayerName(), pVictim->GetClassname() ); +} + +void CBaseGameStats::Event_WeaponFired( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName ) +{ + StatsLog( "CBaseGameStats::Event_WeaponFired [%s] %s weapon [%s]\n", pShooter->GetPlayerName(), bPrimary ? "primary" : "secondary", pchWeaponName ); +} + +void CBaseGameStats::Event_WeaponHit( CBasePlayer *pShooter, bool bPrimary, char const *pchWeaponName, const CTakeDamageInfo &info ) +{ + StatsLog( "CBaseGameStats::Event_WeaponHit [%s] %s weapon [%s] damage [%f]\n", pShooter->GetPlayerName(), bPrimary ? "primary" : "secondary", pchWeaponName, info.GetDamage() ); +} + +void CBaseGameStats::Event_PlayerEnteredGodMode( CBasePlayer *pBasePlayer ) +{ + StatsLog( "CBaseGameStats::Event_PlayerEnteredGodMode [%s] entered GOD mode\n", pBasePlayer->GetPlayerName() ); +} + +void CBaseGameStats::Event_PlayerEnteredNoClip( CBasePlayer *pBasePlayer ) +{ + StatsLog( "CBaseGameStats::Event_PlayerEnteredNoClip [%s] entered NOCLIPe\n", pBasePlayer->GetPlayerName() ); +} + +void CBaseGameStats::Event_DecrementPlayerEnteredNoClip( CBasePlayer *pBasePlayer ) +{ + StatsLog( "CBaseGameStats::Event_DecrementPlayerEnteredNoClip [%s] decrementing NOCLIPe\n", pBasePlayer->GetPlayerName() ); +} + +void CBaseGameStats::Event_IncrementCountedStatistic( const Vector& vecAbsOrigin, char const *pchStatisticName, float flIncrementAmount ) +{ + StatsLog( "Incrementing %s by %f at pos (%d, %d, %d)\n", pchStatisticName, flIncrementAmount, (int)vecAbsOrigin.x, (int)vecAbsOrigin.y, (int)vecAbsOrigin.z ); +} + +//============================================================================= +// HPE_BEGIN +// [dwenger] Needed for CS window-breaking stat +//============================================================================= +void CBaseGameStats::Event_WindowShattered( CBasePlayer *pPlayer ) +{ + StatsLog( "In Event_WindowShattered\n" ); +} +//============================================================================= +// HPE_END +//============================================================================= + +bool CBaseGameStats::UploadStatsFileNOW( void ) +{ + if( !StatsTrackingIsFullyEnabled() || !HaveValidData() || !gamestats->UseOldFormat() ) + return false; + + if ( !filesystem->FileExists( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID ) ) + { + return false; + } + + int curtime = Plat_FloatTime(); + + CBGSDriver.m_tLastUpload = curtime; + + // Update the registry +#ifndef SWDS + IRegistry *reg = InstanceRegistry( "Steam" ); + Assert( reg ); + reg->WriteInt( GetStatUploadRegistryKeyName(), CBGSDriver.m_tLastUpload ); + ReleaseInstancedRegistry( reg ); +#endif + + CUtlBuffer buf; + filesystem->ReadFile( GetStatSaveFileName(), GAMESTATS_PATHID, buf ); + unsigned int uBlobSize = buf.TellPut(); + if ( uBlobSize == 0 ) + { + return false; + } + + const void *pvBlobData = ( const void * )buf.Base(); + + if( gamestatsuploader ) + { + return gamestatsuploader->UploadGameStats( "", + 1, + uBlobSize, + pvBlobData ); + } + + return false; +} + + +void CBaseGameStats::LoadingEvent_PlayerIDDifferentThanLoadedStats( void ) +{ + StatsLog( "CBaseGameStats::LoadingEvent_PlayerIDDifferentThanLoadedStats\n" ); +} + + +bool CBaseGameStats::LoadFromFile( void ) +{ + if ( filesystem->FileExists( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID ) ) + { + char fullpath[ 512 ]; + filesystem->RelativePathToFullPath( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID, fullpath, sizeof( fullpath ) ); + StatsLog( "Loading stats from '%s'\n", fullpath ); + } + + CUtlBuffer buf; + if ( filesystem->ReadFile( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID, buf ) ) + { + bool bRetVal = true; + + int version = buf.GetShort(); + if ( version > GAMESTATS_FILE_VERSION ) + return false; //file is beyond our comprehension + + // Set global parse version + CBGSDriver.m_iLoadedVersion = version; + + buf.Get( CBGSDriver.m_szLoadedUserID, 16 ); + CBGSDriver.m_szLoadedUserID[ sizeof( CBGSDriver.m_szLoadedUserID ) - 1 ] = 0; + + if ( s_szPseudoUniqueID[ 0 ] != 0 ) + { + if ( Q_stricmp( CBGSDriver.m_szLoadedUserID, s_szPseudoUniqueID ) ) + { + //UserID changed, blow away log!!! + filesystem->RemoveFile( gamestats->GetStatSaveFileName(), GAMESTATS_PATHID ); + filesystem->RemoveFile( GAMESTATS_LOG_FILE, GAMESTATS_PATHID ); + Warning( "Userid changed, clearing stats file\n" ); + CBGSDriver.m_szLoadedUserID[0] = '\0'; + CBGSDriver.m_iLoadedVersion = -1; + gamestats->m_BasicStats.Clear(); + gamestats->LoadingEvent_PlayerIDDifferentThanLoadedStats(); + bRetVal = false; + } + + if ( version <= GAMESTATS_FILE_VERSION_OLD5 ) + { + gamestats->m_BasicStats.Clear(); + bRetVal = false; + } + else + { + // Peek ahead in buffer to see if we have the "no default stats" secret flag set. + int iCheckForStandardStatsInFile = *( int * )buf.PeekGet(); + bool bValid = true; + + if ( iCheckForStandardStatsInFile != GAMESTATS_STANDARD_NOT_SAVED ) + { + //the GAMESTATS_STANDARD_NOT_SAVED flag coincides with user completion time, rewind so the gamestats parser can grab it + bValid = gamestats->m_BasicStats.ParseFromBuffer( buf, version ); + } + else + { + // skip over the flag + buf.GetInt(); + } + + if( !bValid ) + { + m_BasicStats.Clear(); + } + + if( ( buf.TellPut() - buf.TellGet() ) != 0 ) //more data left, must be custom data + { + gamestats->LoadCustomDataFromBuffer( buf ); + } + } + } + + return bRetVal; + } + else + { + filesystem->RemoveFile( GAMESTATS_LOG_FILE, GAMESTATS_PATHID ); + } + + return false; +} + +void CBaseGameStats::CollectData( StatSendType_t sendType ) +{ + CBGSDriver.CollectData( sendType ); +} + +void CBaseGameStats::SendData() +{ + CBGSDriver.SendData(); +} + + +#endif // GAME_DLL + +bool CBaseGameStats_Driver::Init() +{ + const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); + + //standardizing is a good thing + char szLoweredGameDir[256]; + Q_strncpy( szLoweredGameDir, pGameDir, sizeof( szLoweredGameDir ) ); + Q_strlower( szLoweredGameDir ); + + gamestats = gamestats->OnInit( gamestats, szLoweredGameDir ); + + //determine constant strings needed for saving and uploading + Q_strncpy( s_szSaveFileName, szLoweredGameDir, sizeof( s_szSaveFileName ) ); + Q_strncat( s_szSaveFileName, "_gamestats.dat", sizeof( s_szSaveFileName ) ); + + Q_strncpy( s_szStatUploadRegistryKeyName, "GameStatsUpload_", sizeof( s_szStatUploadRegistryKeyName ) ); + Q_strncat( s_szStatUploadRegistryKeyName, szLoweredGameDir, sizeof( s_szStatUploadRegistryKeyName ) ); + + gamestats->m_bLoggingToFile = CommandLine()->FindParm( "-gamestatsloggingtofile" ) ? true : false; + gamestats->m_bLogging = CommandLine()->FindParm( "-gamestatslogging" ) ? true : false; + + if ( gamestatsuploader ) + { + m_bEnabled = gamestatsuploader->IsGameStatsLoggingEnabled(); + if ( m_bEnabled ) + { + gamestatsuploader->GetPseudoUniqueId( s_szPseudoUniqueID, sizeof( s_szPseudoUniqueID ) ); + } + } + + ResetData(); + +#ifdef GAME_DLL + if ( StatsTrackingIsFullyEnabled() ) + { + // FIXME: Load m_tLastUpload from registry and save it back out, too +#ifndef SWDS + IRegistry *reg = InstanceRegistry( "Steam" ); + Assert( reg ); + m_tLastUpload = reg->ReadInt( gamestats->GetStatUploadRegistryKeyName(), 0 ); + ReleaseInstancedRegistry( reg ); +#endif + //load existing stats + gamestats->LoadFromFile(); + } +#endif // GAME_DLL + + if ( s_szPseudoUniqueID[ 0 ] != 0 ) + { + gamestats->Event_Init(); +#ifdef GAME_DLL + if ( gamestats->UseOldFormat() ) + { + if( gamestats->AutoSave_OnInit() ) + gamestats->SaveToFileNOW(); + + if( gamestats->AutoUpload_OnInit() ) + gamestats->UploadStatsFileNOW(); + } +#endif + } + else + { + m_bEnabled = false; //unable to generate a pseudo-unique ID, disable tracking + } + + return true; +} + + +void CBaseGameStats_Driver::Shutdown() +{ + m_bShuttingDown = true; + + gamestats->Event_Shutdown(); + + if ( gamestats->UseOldFormat() ) + { +#ifdef GAME_DLL + if( gamestats->AutoSave_OnShutdown() ) + gamestats->SaveToFileNOW(); + + if( gamestats->AutoUpload_OnShutdown() ) + gamestats->UploadStatsFileNOW(); +#endif // GAME_DLL + } + else + { + // code path for new format game stats + if ( gamestats->ShouldSendDataOnAppShutdown() ) + { + CollectData( STATSEND_APPSHUTDOWN ); + SendData(); + } + } + if ( FILESYSTEM_INVALID_HANDLE != g_LogFileHandle ) + { + filesystem->Close( g_LogFileHandle ); + g_LogFileHandle = FILESYSTEM_INVALID_HANDLE; + } + + if ( m_pGamestatsData != NULL ) + { +#ifdef CLIENT_DLL + engine->SetGamestatsData( NULL ); +#endif + delete m_pGamestatsData; + m_pGamestatsData = NULL; + } +} + +void CBaseGameStats_Driver::UpdatePerfStats( void ) +{ + float flCurTime = Plat_FloatTime(); + if ( + ( m_flLastSampleTime == -1 ) || + ( flCurTime - m_flLastSampleTime >= STATS_RECORD_INTERVAL ) ) + { + if ( ( m_flLastRealTime > 0 ) && ( flCurTime > m_flLastRealTime ) ) + { + float flFrameRate = 1.0 / ( flCurTime - m_flLastRealTime ); + StatsBufferRecord_t &stat = m_StatsBuffer[m_nWriteIndex]; + stat.m_flFrameRate = flFrameRate; +#ifdef CLIENT_DLL + // The stat system isn't designed to handle split screen players. Until it get's + // redesigned, let's take the first player's split screen ping, since all other stats + // will be based on the first player + IGameResources *gr = GameResources(); + int ping = 0; + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); // GetLocalPlayer( FirstValidSplitScreenSlot() ); + if ( pPlayer && gr ) + { + ping = gr->GetPing( pPlayer->entindex() ); + } + stat.m_flServerPing = ping; +#endif + AdvanceIndex(); + m_flLastSampleTime = flCurTime; + } + } + m_flLastRealTime = flCurTime; + +#ifdef CLIENT_DLL + if ( g_pGameRules && g_pGameRules->IsMultiplayer() ) + { + m_bDidVoiceChat |= GetClientVoiceMgr()->IsLocalPlayerSpeaking(); + } +#endif +} + +void CBaseGameStats_Driver::PossibleMapChange( void ) +{ +#ifdef GAME_DLL + //detect and copy map changes + if ( Q_stricmp( m_PrevMapName.String(), STRING( gpGlobals->mapname ) ) ) + { + MEM_ALLOC_CREDIT(); + + CUtlString PrevMapBackup = m_PrevMapName; + + m_PrevMapName = STRING( gpGlobals->mapname ); + + gamestats->Event_MapChange( PrevMapBackup.String(), STRING( gpGlobals->mapname ) ); + + if ( gamestats->UseOldFormat() ) + { + if( gamestats->AutoSave_OnMapChange() ) + gamestats->SaveToFileNOW(); + + if( gamestats->AutoUpload_OnMapChange() ) + gamestats->UploadStatsFileNOW(); + } + } +#endif +} + + + +void CBaseGameStats_Driver::LevelInitPreEntity() +{ + m_bInLevel = true; + m_bFirstLevel = false; + + if ( Q_stricmp( s_szPseudoUniqueID, "unknown" ) == 0 ) + { + // "unknown" means this is a dedicated server and we weren't able to generate a unique ID (e.g. Linux server). + // Change the unique ID to be a hash of IP & port. We couldn't do this earlier because IP is not known until level + // init time. + ConVar *hostip = cvar->FindVar( "hostip" ); + ConVar *hostport = cvar->FindVar( "hostport" ); + if ( hostip && hostport ) + { + int crcInput[2]; + crcInput[0] = hostip->GetInt(); + crcInput[1] = hostport->GetInt(); + if ( crcInput[0] && crcInput[1] ) + { + CRC32_t crc = CRC32_ProcessSingleBuffer( crcInput, sizeof( crcInput ) ); + Q_snprintf( s_szPseudoUniqueID, ARRAYSIZE( s_szPseudoUniqueID ), "H:%x", crc ); + } + } + } + + PossibleMapChange(); + + m_flPauseStartTime = 0.0f; + m_flLevelStartTime = gpGlobals->realtime; + + gamestats->Event_LevelInit(); + +#ifdef GAME_DLL + if ( gamestats->UseOldFormat() ) + { + if( gamestats->AutoSave_OnLevelInit() ) + gamestats->SaveToFileNOW(); + + if( gamestats->AutoUpload_OnLevelInit() ) + gamestats->UploadStatsFileNOW(); + } +#endif +} + + +void CBaseGameStats_Driver::LevelShutdownPreEntity() +{ +#ifdef CLIENT_DLL + LevelShutdown(); +#endif +} + +void CBaseGameStats_Driver::LevelShutdownPreClearSteamAPIContext() +{ +#ifdef GAME_DLL + LevelShutdown(); +#endif +} + +void CBaseGameStats_Driver::LevelShutdown() +{ + float flElapsed = gpGlobals->realtime - m_flLevelStartTime; + + if ( flElapsed < 0.0f ) + { + Assert( 0 ); + Warning( "EVENT_LEVELSHUTDOWN: with negative elapsed time (rt %f starttime %f)\n", gpGlobals->realtime, m_flLevelStartTime ); + flElapsed = 0.0f; + } + + //Assert( m_bInLevel ); //so, apparently shutdowns can happen before inits + +#ifdef GAME_DLL + if ( m_bInLevel && ( gpGlobals->eLoadType != MapLoad_Background ) ) +#else + if ( m_bInLevel ) +#endif + { + m_flTotalTimeInLevels += flElapsed; + m_iNumLevels ++; + + gamestats->Event_LevelShutdown( flElapsed ); + + if ( gamestats->UseOldFormat() ) + { +#ifdef GAME_DLL + if( gamestats->AutoSave_OnLevelShutdown() ) + gamestats->SaveToFileNOW( true ); + + if( gamestats->AutoUpload_OnLevelShutdown() ) + gamestats->UploadStatsFileNOW(); +#endif + } + else + { + // code path for new format game stats + CollectData( STATSEND_LEVELSHUTDOWN ); + if ( gamestats->ShouldSendDataOnLevelShutdown() ) + { + SendData(); + } + } + m_bInLevel = false; + } +} + +void CBaseGameStats_Driver::OnSave() +{ + gamestats->Event_SaveGame(); +} + + +void CBaseGameStats_Driver::CollectData( StatSendType_t sendType ) +{ + CGamestatsData *pGamestatsData = NULL; +#ifdef GAME_DLL + // for server, check with the engine to see if there already a gamestats data container registered. (There will be if there is a client + // running in the same process.) + pGamestatsData = engine->GetGamestatsData(); + if ( pGamestatsData ) + { + // use the registered gamestats container, so free the one we allocated + if ( m_pGamestatsData != NULL ) + { + delete m_pGamestatsData; + m_pGamestatsData = NULL; + } + } + else + { + pGamestatsData = m_pGamestatsData; + } +#else + pGamestatsData = m_pGamestatsData; +#endif + Assert( pGamestatsData ); + KeyValues *pKV = pGamestatsData->m_pKVData; + + int iAppID = engine->GetAppID(); + pKV->SetInt( "appid", iAppID ); + + switch ( sendType ) + { + case STATSEND_LEVELSHUTDOWN: + { + // make a map node in the KeyValues to use for this level + char szMap[MAX_PATH+1]=""; +#ifdef CLIENT_DLL + Q_FileBase( MapName(), szMap, ARRAYSIZE( szMap ) ); +#else + Q_strncpy( szMap, gpGlobals->mapname.ToCStr(), ARRAYSIZE( szMap ) ); +#endif // CLIENT_DLL + if ( !szMap[0] ) + return; + KeyValues *pKVMap = new KeyValues( "map" ); + pKV->AddSubKey( pKVMap ); + pKVMap->SetString( "mapname", szMap ); + pKV = pKVMap; + + } + break; + case STATSEND_APPSHUTDOWN: + break; + default: + Assert( false ); + break; + } + + // add common data + pGamestatsData->m_bHaveData |= AddBaseDataForSend( pKV, sendType ); + +#if defined(STEAMWORKS_GAMESTATS_ACTIVE) + // At the end of every map, clients submit their perfdata for the map + if ( sendType == STATSEND_LEVELSHUTDOWN && pGamestatsData && pGamestatsData->m_bHaveData ) + { + GetSteamWorksSGameStatsUploader().AddClientPerfData( pGamestatsData->m_pKVData ); + } + GetSteamWorksSGameStatsUploader().LevelShutdown(); +#endif + + // add game-specific data + pGamestatsData->m_bHaveData |= gamestats->AddDataForSend( pKV, sendType ); + +// Need to initialiate a reset since cs isn't using the gamestat system to add data +#if defined(CSTRIKE_DLL) && defined(CLIENT_DLL) + ResetData(); +#endif +} + + +void CBaseGameStats_Driver::SendData() +{ + // if we don't own the data container or there's no valid data, nothing to do + if ( !m_pGamestatsData || !m_pGamestatsData->m_bHaveData ) + return; + + // save the data to a buffer + CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); + m_pGamestatsData->m_pKVData->RecursiveSaveToFile( buf, 0 ); + + if ( CommandLine()->FindParm( "-gamestatsfileoutputonly" ) ) + { + // write file for debugging + const char szFileName[] = "gamestats.dat"; + filesystem->WriteFile( szFileName, GAMESTATS_PATHID, buf ); + } + else + { + // upload the file to Steam + if ( gamestatsuploader ) + gamestatsuploader->UploadGameStats( "", 1, buf.TellPut(), buf.Base() ); + } + + ResetData(); +} + +bool CBaseGameStats_Driver::AddBaseDataForSend( KeyValues *pKV, StatSendType_t sendType ) +{ + switch ( sendType ) + { + case STATSEND_APPSHUTDOWN: +#ifdef CLIENT_DLL + if ( m_iNumLevels > 0 ) + { + // add playtime data + KeyValues *pKVData = new KeyValues( "playtime" ); + pKVData->SetInt( "TotalLevelTime", m_flTotalTimeInLevels ); + pKVData->SetInt( "NumLevels", m_iNumLevels ); + pKV->AddSubKey( pKVData ); + return true; + } +#endif + break; + case STATSEND_LEVELSHUTDOWN: +#ifdef CLIENT_DLL + if ( m_bBufferFull ) + { + // add perf data + KeyValues *pKVPerf = new KeyValues( "perfdata" ); + float flAverageFrameRate = AverageStat( &StatsBufferRecord_t::m_flFrameRate ); + float flMinFrameRate = MinStat( &StatsBufferRecord_t::m_flFrameRate ); + float flMaxFrameRate = MaxStat( &StatsBufferRecord_t::m_flFrameRate ); + + + float flDeviationsquared = 0; + // Compute std deviation + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + { + float val = m_StatsBuffer[i].m_flFrameRate - flAverageFrameRate; + flDeviationsquared += ( val * val ); + } + + float var = flDeviationsquared / (float)( STATS_WINDOW_SIZE - 1 ); + float flStandardDeviationFrameRate = sqrt( var ); + + pKVPerf->SetFloat( "AvgFPS", flAverageFrameRate ); + pKVPerf->SetFloat( "MinFPS", flMinFrameRate ); + pKVPerf->SetFloat( "MaxFPS", flMaxFrameRate ); + pKVPerf->SetFloat( "StdDevFPS", flStandardDeviationFrameRate ); + + // Determine the min/avg/max Server Ping and store the results + float flAverageServerPing = AverageStat( &StatsBufferRecord_t::m_flServerPing ); + pKVPerf->SetFloat( "AvgServerPing", flAverageServerPing ); + + pKV->AddSubKey( pKVPerf ); + + // Only keeping track of using voice in a multiplayer game + if ( g_pGameRules && g_pGameRules->IsMultiplayer() ) + { + pKV->SetInt( "UsedVoice", m_bDidVoiceChat ); + } + + extern ConVar closecaption; + pKV->SetInt( "Caption", closecaption.GetInt() ); + +#ifndef NO_STEAM + // We can now get the game language from steam :) + if ( steamapicontext && steamapicontext->SteamApps() ) + { + const char *currentLanguage = steamapicontext->SteamApps()->GetCurrentGameLanguage(); + pKV->SetString( "Language", currentLanguage ? currentLanguage : "unknown" ); + } +#endif + + // We need to filter out client side dev work from playtest work for the stat reporting. + // The simplest way is to check for sv_cheats, since we also do NOT want client stat reports + // where the player has cheated. + if ( NULL != sv_cheats ) + { + int iCheats = sv_cheats->GetInt(); + pKV->SetInt( "Cheats", iCheats ); + } + + int mapTime = gpGlobals->realtime - m_flLevelStartTime; + pKV->SetInt( "MapTime", mapTime ); + + return true; + } +#endif + break; + } + + return false; +} + + +void CBaseGameStats_Driver::ResetData() +{ +#ifdef GAME_DLL + // on the server, if there is a gamestats data container registered (by a client in the same process), they're in charge of resetting it, nothing for us to do + if ( engine->GetGamestatsData() != NULL ) + return; +#endif + + if ( m_pGamestatsData != NULL ) + { + delete m_pGamestatsData; + m_pGamestatsData = NULL; + } + + m_bDidVoiceChat = false; + m_pGamestatsData = new CGamestatsData(); + KeyValues *pKV = m_pGamestatsData->m_pKVData; + pKV->SetInt( "IsPc", IsPC() ); + pKV->SetInt( "version", GAMESTATS_VERSION ); + pKV->SetString( "srcid", s_szPseudoUniqueID ); + +#ifdef CLIENT_DLL + const CPUInformation &cpu = *GetCPUInformation(); + OverWriteCharsWeHate( cpu.m_szProcessorID ); + pKV->SetString( "CPUID", cpu.m_szProcessorID ); + pKV->SetFloat( "CPUGhz", cpu.m_Speed * ( 1.0 / 1.0e9 ) ); + pKV->SetInt( "NumCores", cpu.m_nPhysicalProcessors ); + + MaterialAdapterInfo_t gpu; + materials->GetDisplayAdapterInfo( materials->GetCurrentAdapter(), gpu ); + + CMatRenderContextPtr pRenderContext( materials ); + int dest_width,dest_height; + pRenderContext->GetRenderTargetDimensions( dest_width, dest_height ); + + if ( gpu.m_pDriverName ) + { + OverWriteCharsWeHate( gpu.m_pDriverName ); + } + pKV->SetString( "GPUDrv", SafeString( gpu.m_pDriverName ) ); + pKV->SetInt( "GPUVendor", gpu.m_VendorID ); + pKV->SetInt( "GPUDeviceID", gpu.m_DeviceID ); + pKV->SetString( "GPUDriverVersion", CFmtStr( "%d.%d", gpu.m_nDriverVersionHigh, gpu.m_nDriverVersionLow ) ); + pKV->SetInt( "DxLvl", g_pMaterialSystemHardwareConfig->GetDXSupportLevel() ); + pKV->SetInt( "Width", dest_width ); + pKV->SetInt( "Height", dest_height ); + const MaterialSystem_Config_t &config = materials->GetCurrentConfigForVideoCard(); + pKV->SetInt( "Windowed", config.Windowed() == true ); + + engine->SetGamestatsData( m_pGamestatsData ); +#endif + +#if defined(CSTRIKE_DLL) && defined(CLIENT_DLL) + // reset perf buffer for next map + for( int i = 0; i < STATS_WINDOW_SIZE; i++ ) + { + m_StatsBuffer[i].m_flFrameRate = 0; + m_StatsBuffer[i].m_flServerPing = 0; + } + + m_bBufferFull = false; + m_nWriteIndex = 0; +#endif +} + +void CBaseGameStats_Driver::OnRestore() +{ + PossibleMapChange(); + + gamestats->Event_LoadGame(); +} + + +void CBaseGameStats_Driver::FrameUpdatePostEntityThink() +{ + bool bGamePaused = ( gpGlobals->frametime == 0.0f ); + + if ( !m_bInLevel ) + { + m_flPauseStartTime = 0.0f; + } + else if ( m_bGamePaused != bGamePaused ) + { + if ( bGamePaused ) + { + m_flPauseStartTime = gpGlobals->realtime; + } + else if ( m_flPauseStartTime != 0.0f ) + { + float flPausedTime = gpGlobals->realtime - m_flPauseStartTime; + if ( flPausedTime < 0.0f ) + { + Assert( 0 ); + Warning( "Game paused time showing up negative (rt %f pausestart %f)\n", gpGlobals->realtime, m_flPauseStartTime ); + flPausedTime = 0.0f; + } + + // Remove this from global time counters + + // Msg( "Pause: adding %f to level starttime\n", flPausedTime ); + + m_flLevelStartTime += flPausedTime; + m_flPauseStartTime = 0.0f; + + // Msg( "Paused for %.2f seconds\n", flPausedTime ); + } + m_bGamePaused = bGamePaused; + } +} + +bool StatsTrackingIsFullyEnabled( void ) +{ + return CBGSDriver.m_bEnabled && gamestats->StatTrackingEnabledForMod(); +} + +void CBaseGameStats::Clear( void ) +{ +#ifdef GAME_DLL + gamestats->m_BasicStats.Clear(); +#endif +} + +//----------------------------------------------------------------------------- +// Nukes any dangerous characters and replaces w/space char +//----------------------------------------------------------------------------- +void OverWriteCharsWeHate( char *pStr ) +{ + while( *pStr ) + { + switch( *pStr ) + { + case '\n': + case '\r': + case '\\': + case '\"': + case '\'': + case '\032': + case ';': + *pStr = ' '; + } + pStr++; + } +} + +#ifdef GAME_DLL + +void CBaseGameStats::SetSteamStatistic( bool bUsingSteam ) +{ + if( CBGSDriver.m_bFirstLevel ) + { + m_BasicStats.m_Summary.m_bSteam = bUsingSteam; + } + + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + map->m_bSteam = bUsingSteam; + } + + m_BasicStats.m_bSteam = bUsingSteam; +} + +void CBaseGameStats::SetCyberCafeStatistic( bool bIsCyberCafeUser ) +{ + if( CBGSDriver.m_bFirstLevel ) + { + m_BasicStats.m_Summary.m_bCyberCafe = bIsCyberCafeUser; + } + + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + map->m_bCyberCafe = bIsCyberCafeUser; + } + + m_BasicStats.m_bCyberCafe = bIsCyberCafeUser; +} + +void CBaseGameStats::SetHDRStatistic( bool bHDREnabled ) +{ + if( bHDREnabled ) + { + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nHDR; + } + + if( CBGSDriver.m_bFirstLevel ) + { + ++m_BasicStats.m_Summary.m_nHDR; + } + } +} + +void CBaseGameStats::SetCaptionsStatistic( bool bClosedCaptionsEnabled ) +{ + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nCaptions; + } + + if( CBGSDriver.m_bFirstLevel ) + { + ++m_BasicStats.m_Summary.m_nCaptions; + } +} + +void CBaseGameStats::SetSkillStatistic( int iSkillSetting ) +{ + int skill = clamp( iSkillSetting, 1, 3 ) - 1; + + if( CBGSDriver.m_bInLevel ) + { + BasicGameStatsRecord_t *map = m_BasicStats.FindOrAddRecordForMap( CBGSDriver.m_PrevMapName.String() ); + ++map->m_nSkill[ skill ]; + } + + if ( CBGSDriver. m_bFirstLevel ) + { + ++m_BasicStats.m_Summary.m_nSkill[ skill ]; + } +} + +void CBaseGameStats::SetDXLevelStatistic( int iDXLevel ) +{ + m_BasicStats.m_nDXLevel = iDXLevel; +} + +void CBaseGameStats::SetHL2UnlockedChapterStatistic( void ) +{ + // Now grab the hl2/cfg/config.cfg and suss out the sv_unlockedchapters cvar to estimate how far they got in HL2 + char const *relative = "cfg/config.cfg"; + char fullpath[ 512 ]; + char gamedir[256]; + engine->GetGameDir( gamedir, 256 ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s/../hl2/%s", gamedir, relative ); + + if ( filesystem->FileExists( fullpath ) ) + { + FileHandle_t fh = filesystem->Open( fullpath, "rb" ); + if ( FILESYSTEM_INVALID_HANDLE != fh ) + { + // read file into memory + int size = filesystem->Size(fh); + char *configBuffer = new char[ size + 1 ]; + filesystem->Read( configBuffer, size, fh ); + configBuffer[size] = 0; + filesystem->Close( fh ); + + // loop through looking for all the cvars to apply + const char *search = Q_stristr(configBuffer, "sv_unlockedchapters" ); + if ( search ) + { + // read over the token + search = strtok( (char *)search, " \n" ); + search = strtok( NULL, " \n" ); + + if ( search[0]== '\"' ) + ++search; + + // read the value + int iChapter = Q_atoi( search ); + m_BasicStats.m_nHL2ChaptureUnlocked = iChapter; + } + + // free + delete [] configBuffer; + } + } +} + +static void CC_ResetGameStats( const CCommand &args ) +{ +#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) + // Disabled this until we fix the TF gamestat crashes that result + return; +#endif + + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + gamestats->Clear(); + gamestats->SaveToFileNOW(); + gamestats->StatsLog( "CC_ResetGameStats : Server cleared game stats\n" ); +} + +static ConCommand resetGameStats("_resetgamestats", CC_ResetGameStats, "Erases current game stats and writes out a blank stats file", 0 ); + +class CPointGamestatsCounter : public CPointEntity +{ +public: + DECLARE_CLASS( CPointGamestatsCounter, CPointEntity ); + DECLARE_DATADESC(); + + CPointGamestatsCounter(); + +protected: + + void InputSetName( inputdata_t &inputdata ); + void InputIncrement( inputdata_t &inputdata ); + + void InputEnable( inputdata_t &inputdata ); + void InputDisable( inputdata_t &inputdata ); +private: + + string_t m_strStatisticName; + bool m_bDisabled; +}; + +BEGIN_DATADESC( CPointGamestatsCounter ) + + DEFINE_KEYFIELD( m_strStatisticName, FIELD_STRING, "Name" ), + DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_STRING, "SetName", InputSetName ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "Increment", InputIncrement ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( point_gamestats_counter, CPointGamestatsCounter ) + + +CPointGamestatsCounter::CPointGamestatsCounter() : + m_strStatisticName( NULL_STRING ), + m_bDisabled( false ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Changes name of statistic +//----------------------------------------------------------------------------- +void CPointGamestatsCounter::InputSetName( inputdata_t &inputdata ) +{ + m_strStatisticName = inputdata.value.StringID(); +} + +//----------------------------------------------------------------------------- +// Purpose: Changes name of statistic +//----------------------------------------------------------------------------- +void CPointGamestatsCounter::InputIncrement( inputdata_t &inputdata ) +{ + if ( m_bDisabled ) + return; + + if ( NULL_STRING == m_strStatisticName ) + { + DevMsg( 1, "CPointGamestatsCounter::InputIncrement: No stat name specified for point_gamestats_counter @%f, %f, %f [ent index %d]\n", + GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, entindex() ); + return; + } + + gamestats->Event_IncrementCountedStatistic( GetAbsOrigin(), STRING( m_strStatisticName ), inputdata.value.Float() ); +} + +void CPointGamestatsCounter::InputEnable( inputdata_t &inputdata ) +{ + m_bDisabled = false; +} + +void CPointGamestatsCounter::InputDisable( inputdata_t &inputdata ) +{ + m_bDisabled = true; +} + +#endif // GAME_DLL |