diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /devtools/processgamestats/tf_gamestats_parse.cpp | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'devtools/processgamestats/tf_gamestats_parse.cpp')
| -rw-r--r-- | devtools/processgamestats/tf_gamestats_parse.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/devtools/processgamestats/tf_gamestats_parse.cpp b/devtools/processgamestats/tf_gamestats_parse.cpp new file mode 100644 index 0000000..b8dd242 --- /dev/null +++ b/devtools/processgamestats/tf_gamestats_parse.cpp @@ -0,0 +1,480 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +// +// + +#include "stdafx.h" +#include <stdio.h> +#include <process.h> +#include <string.h> +#include <windows.h> +#include <sys/stat.h> +#include <time.h> +#include "interface.h" +#include "imysqlwrapper.h" +#include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utlsymbol.h" +#include "tier1/utlstring.h" +#include "tier1/utldict.h" +#include "tier2/tier2.h" +#include "filesystem.h" + +#include "cbase.h" +#include "gamestats.h" +class CBaseObject; +#include "tf/tf_gamestats.h" +#include "base_gamestats_parse.h" +#include "tier0/icommandline.h" + +#include <string> + +static TFReportedStats_t g_reportedStats; + +extern void v_escape_string (std::string& s); + +//----------------------------------------------------------------------------- +// Weapons. +//----------------------------------------------------------------------------- +static const char *s_aWeaponNames[] = +{ + "NONE", + "BAT", + "BOTTLE", + "FIREAXE", + "CLUB", + "CROWBAR", + "KNIFE", + "MEDIKIT", + "PIPE", + "SHOVEL", + "WRENCH", + "BONESAW", + "SHOTGUN_PRIMARY", + "SHOTGUN_SECONDARY", + "SNIPERRIFLE", + "MINIGUN", + "SMG", + "SYRINGEGUN_MEDIC", + "TRANQ", + "ROCKETLAUNCHER", + "GRENADELAUNCHER", + "PIPEBOMBLAUNCHER", + "FLAMETHROWER", + "GRENADE_NORMAL", + "GRENADE_CONCUSSION", + "GRENADE_NAIL", + "GRENADE_MIRV", + "GRENADE_MIRV_DEMOMAN", + "GRENADE_NAPALM", + "GRENADE_GAS", + "GRENADE_EMP", + "GRENADE_CALTROP", + "GRENADE_PIPEBOMB", + "GRENADE_SMOKE_BOMB", + "GRENADE_HEAL", + "PISTOL", + "REVOLVER", + "NAILGUN", + "PDA", + "PDA_DEMOMAN", + "PDA_ENGINEER", + "PDA_SPY", + "BUILDER", + "MEDIGUN", + "FLAG", + "GRENADE_MIRVBOMB", + "FLAMETHROWER_ROCKET", + "GRENADE_DEMOMAN", + "SENTRY_BULLET", + "SENTRY_ROCKET", + "DISPENSER", + "INVIS", +}; + +//----------------------------------------------------------------------------- +// Classes +//----------------------------------------------------------------------------- +static const char *s_aClassNames[] = +{ + "UNDEFINED", + "SCOUT", + "SNIPER", + "SOLDIER", + "DEMOMAN", + "MEDIC", + "HEAVYWEAPONS", + "PYRO", + "SPY", + "ENGINEER", + "CIVILIAN", +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static bool LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer ) +{ + g_reportedStats.Clear(); + return g_reportedStats.LoadCustomDataFromBuffer( LoadBuffer ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static const char *ClassIdToAlias( int iClass ) +{ + if ( ( iClass >= ARRAYSIZE( s_aClassNames ) ) || ( iClass < 0 ) ) + return NULL; + + return s_aClassNames[iClass]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *WeaponIdToAlias( int iWeapon ) +{ + if ( ( iWeapon >= ARRAYSIZE( s_aWeaponNames ) ) || ( iWeapon < 0 ) ) + return NULL; + + return s_aWeaponNames[iWeapon]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void DescribeTF2Stats() +{ +#if 0 // not up to date w/latest stats code. + int iMap; + for ( iMap = g_dictMapStats.First(); iMap != g_dictMapStats.InvalidIndex(); iMap = g_dictMapStats.Next( iMap ) ) + { + // Get the current map. + TF_Gamestats_LevelStats_t *pCurrentMap = &g_dictMapStats[iMap]; + + Msg( " --- %s ------\n %d deaths\n %.2f seconds total playtime\n", pCurrentMap->m_Header.m_szMapName, + pCurrentMap->m_aPlayerDeaths.Count(), pCurrentMap->m_Header.m_flTime ); + for( int i = 0; i < pCurrentMap->m_aPlayerDeaths.Count(); i++ ) + { + Msg( " %s killed %s with %s at (%d,%d,%d), distance %d\n", + ClassIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iAttackClass ), + ClassIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iTargetClass ), + WeaponIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iWeapon ), + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 0 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 1 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 2 ], + pCurrentMap->m_aPlayerDeaths[ i ].iDistance ); + } + Msg( "\n---------------------------------\n\n %d damage records\n", pCurrentMap->m_aPlayerDamage.Count() ); + + for( int i = 0; i < pCurrentMap->m_aPlayerDamage.Count(); i++ ) + { + Msg( " %.2f : %s at (%d,%d,%d) caused %d damage to %s with %s at (%d,%d,%d)%s%s\n", + pCurrentMap->m_aPlayerDamage[ i ].fTime, + ClassIdToAlias( pCurrentMap->m_aPlayerDamage[ i ].iAttackClass ), + pCurrentMap->m_aPlayerDamage[ i ].nAttackerPosition[ 0 ], + pCurrentMap->m_aPlayerDamage[ i ].nAttackerPosition[ 1 ], + pCurrentMap->m_aPlayerDamage[ i ].nAttackerPosition[ 2 ], + pCurrentMap->m_aPlayerDamage[ i ].iDamage, + ClassIdToAlias( pCurrentMap->m_aPlayerDamage[ i ].iTargetClass ), + WeaponIdToAlias( pCurrentMap->m_aPlayerDamage[ i ].iWeapon ), + pCurrentMap->m_aPlayerDamage[ i ].nTargetPosition[ 0 ], + pCurrentMap->m_aPlayerDamage[ i ].nTargetPosition[ 1 ], + pCurrentMap->m_aPlayerDamage[ i ].nTargetPosition[ 2 ], + pCurrentMap->m_aPlayerDamage[ i ].iCrit ? ", CRIT!" : "", + pCurrentMap->m_aPlayerDamage[ i ].iKill ? ", KILL" : "" ); + } + + Msg( "\n" ); + } +#endif +} +extern CUtlDict< int, unsigned short > g_mapOrder; +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void InsertTF2Data( bool bDeathOnly, char const *szStatsFileUserID, time_t fileTime, IMySQL *sql, int iStatsFileVersion ) +{ + if ( !sql ) + return; + + std::string userid; + userid = szStatsFileUserID; + v_escape_string( userid ); + + + char szDate[128]="Now()"; + if ( fileTime > 0 ) + { + tm * pTm = localtime( &fileTime ); + Q_snprintf( szDate, ARRAYSIZE( szDate ), "'%04d-%02d-%02d %02d:%02d:%02d'", + pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday, pTm->tm_hour, pTm->tm_min, pTm->tm_sec ); + } + + char q[4096]; + + + { + int iMap; + for ( iMap = g_reportedStats.m_dictMapStats.First(); iMap != g_reportedStats.m_dictMapStats.InvalidIndex(); iMap = g_reportedStats.m_dictMapStats.Next( iMap ) ) + { + // Get the current map. + TF_Gamestats_LevelStats_t *pCurrentMap = &g_reportedStats.m_dictMapStats[iMap]; + +#if 1 + int slot = g_mapOrder.Find( pCurrentMap->m_Header.m_szMapName ); + if ( slot == g_mapOrder.InvalidIndex() ) + { + if ( Q_stricmp( pCurrentMap->m_Header.m_szMapName, "devtest" ) ) + continue; + } +#endif + + std::string mapname; + mapname = pCurrentMap->m_Header.m_szMapName; + v_escape_string( mapname ); + + std::string tag; + tag = ""; // pCurrentMap->m_Tag.m_szTagText; + v_escape_string( tag ); + + int mapversion = 0; + + + + /* + for( int i = 0; i < pCurrentMap->m_aPlayerDeaths.Count(); i++ ) + { + Msg( " %s killed %s with %s at (%d,%d,%d), distance %d\n", + ClassIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iAttackClass ), + ClassIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iTargetClass ), + WeaponIdToAlias( pCurrentMap->m_aPlayerDeaths[ i ].iWeapon ), + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 0 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 1 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 2 ], + pCurrentMap->m_aPlayerDeaths[ i ].iDistance ); + } + */ + // Deal with deaths + for ( int i = 0; i < pCurrentMap->m_aPlayerDeaths.Count(); ++i ) + { + Q_snprintf( q, sizeof( q ), "REPLACE into %s_deaths (UserID,Tag,LastUpdate,MapName,MapVersion,DeathIndex,X,Y,Z) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,%d,%d,%d,%d);", + "tf", + userid.c_str(), + tag.c_str(), + mapname.c_str(), + mapversion, + i, + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 0 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 1 ], + pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 2 ] ); + + int retcode = sql->Execute( q ); + if ( retcode != 0 ) + { + printf( "Query %s failed\n", q ); + return; + } + } + } + if ( bDeathOnly ) + return; + } + + int iMap; + for ( iMap = g_reportedStats.m_dictMapStats.First(); iMap != g_reportedStats.m_dictMapStats.InvalidIndex(); + iMap = g_reportedStats.m_dictMapStats.Next( iMap ) ) + { + // Get the current map. + TF_Gamestats_LevelStats_t *pCurrentMap = &g_reportedStats.m_dictMapStats[iMap]; + std::string mapname = pCurrentMap->m_Header.m_szMapName; + v_escape_string( mapname ); + + // insert map data into database + Q_snprintf( q, sizeof( q ), "INSERT into tf_mapdata (ServerID,TimeSubmitted,MapName,RoundsPlayed,TotalTime,BlueWins,RedWins,Stalemates,BlueSuddenDeathWins,RedSuddenDeathWins) " + "values (\"%s\",%s,\"%s\",%d,%d,%d,%d,%d,%d,%d);", + szStatsFileUserID,szDate, mapname.c_str(), pCurrentMap->m_Header.m_iRoundsPlayed, pCurrentMap->m_Header.m_iTotalTime, pCurrentMap->m_Header.m_iBlueWins, + pCurrentMap->m_Header.m_iRedWins, pCurrentMap->m_Header.m_iStalemates, pCurrentMap->m_Header.m_iBlueSuddenDeathWins, pCurrentMap->m_Header.m_iRedSuddenDeathWins ); + +// Msg( "%s\n", q ); + + int retcode = sql->Execute( q ); + if ( retcode != 0 ) + { + Msg( "Query %s failed\n", q ); + return; + } + + // insert the class data + for ( int i = TF_CLASS_UNDEFINED + 1; i < TF_CLASS_CIVILIAN; i++ ) + { + TF_Gamestats_ClassStats_t &classStats = pCurrentMap->m_aClassStats[i]; + if ( 0 == classStats.iSpawns ) // skip any classes that have had no spawns + continue; + + Q_snprintf( q, sizeof( q ), "INSERT into tf_classdata (ServerID,MapName,TimeSubmitted,Class,Spawns,TotalTime,Score,Kills,Deaths,Assists,Captures) " + "values (\"%s\",\"%s\",%s,%d,%d,%d,%d,%d,%d,%d,%d);", + szStatsFileUserID, mapname.c_str(), szDate, i, classStats.iSpawns, classStats.iTotalTime, classStats.iScore, classStats.iKills, classStats.iDeaths, + classStats.iAssists, classStats.iCaptures ); + +// Msg( "%s\n", q ); + int retcode = sql->Execute( q ); + if ( retcode != 0 ) + { + Msg( "Query %s failed\n", q ); + return; + } + } + + // insert the weapon data + for ( int i = TF_WEAPON_NONE+1; i < TF_WEAPON_COUNT; i++ ) + { + TF_Gamestats_WeaponStats_t &weaponStats = pCurrentMap->m_aWeaponStats[i]; + + // skip any weapons that have no shots fired + if ( 0 == weaponStats.iShotsFired ) + continue; + + Q_snprintf( q, sizeof( q ), "INSERT into tf_weapondata (ServerID, MapName, TimeSubmitted,WeaponID,ShotsFired,ShotsFiredCrit,ShotsHit,DamageTotal," + "HitsWithKnownDistance,DistanceTotal) " + "values (\"%s\",\"%s\",%s,%d,%d,%d,%d,%d,%d,%llu)", + szStatsFileUserID, mapname.c_str(), szDate, i, weaponStats.iShotsFired, weaponStats.iCritShotsFired, weaponStats.iHits, weaponStats.iTotalDamage, + weaponStats.iHitsWithKnownDistance, weaponStats.iTotalDistance ); + +// Msg( "%s\n", q ); + int retcode = sql->Execute( q ); + if ( retcode != 0 ) + { + Msg( "Query %s failed\n", q ); + return; + } + } + } +} + +bool g_bDeathOnly = false; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TF_ParseCustomGameStatsData( ParseContext_t *ctx ) +{ + FILE *fp = fopen( ctx->file, "rb" ); + if ( !fp ) + return CUSTOMDATA_FAILED; + + CUtlBuffer statsBuffer; + + struct _stat sb; + _stat( ctx->file, &sb ); + statsBuffer.Clear(); + statsBuffer.EnsureCapacity( sb.st_size ); + fread( statsBuffer.Base(), sb.st_size, 1, fp ); + statsBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, sb.st_size ); + fclose( fp ); + + if ( memcmp( statsBuffer.Base(), "\"gamestats\"", 11 ) == 0 ) + { + Msg( "Got new-style file format that we don't support, skipping\n" ); + return CUSTOMDATA_SUCCESS; + } + + if ( memcmp( statsBuffer.Base(), "PERFDATA:", 9 ) == 0 ) + { + ProcessPerfData( ctx->mysql, sb.st_mtime, "tf_perfdata", ( ( char * ) statsBuffer.Base() ) + 9 ); + return CUSTOMDATA_SUCCESS; + } + char shortname[ 128 ]; + Q_FileBase( ctx->file, shortname, sizeof( shortname ) ); + + char szCurrentStatsFileUserID[17]; + int iCurrentStatsFileVersion; + + iCurrentStatsFileVersion = statsBuffer.GetShort(); + statsBuffer.Get( szCurrentStatsFileUserID, 16 ); + szCurrentStatsFileUserID[ sizeof( szCurrentStatsFileUserID ) - 1 ] = 0; + + unsigned int iCheckIfStandardDataSaved = statsBuffer.GetUnsignedInt(); + if( iCheckIfStandardDataSaved != GAMESTATS_STANDARD_NOT_SAVED ) + { + // we don't care about the standard data, so why is it here? + return CUSTOMDATA_FAILED; + } + + // check for custom data + bool bHasCustomData = (statsBuffer.TellPut() != statsBuffer.TellGet()); + if( !bHasCustomData ) + { + // where's our data? + return CUSTOMDATA_FAILED; + } + + if ( !LoadCustomDataFromBuffer( statsBuffer ) ) + return CUSTOMDATA_FAILED; + + if( ctx->describeonly ) + { + DescribeTF2Stats(); + } + else + { + if ( CommandLine()->CheckParm( "-deathsonly" ) ) + { + g_bDeathOnly = true; + } + + InsertTF2Data( g_bDeathOnly, szCurrentStatsFileUserID, sb.st_mtime, ctx->mysql, iCurrentStatsFileVersion ); + } + + return CUSTOMDATA_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Purpose: Called after all new files have been parsed & imported +//----------------------------------------------------------------------------- +void TF_PostImport( IMySQL *sql ) +{ +#if 0 // now handled by PHP script + if ( g_bDeathOnly ) + return; + // All the new data files have been imported to SQL. Now, do a rollup of the raw data into the rollup tables. + + // delete existing rollup for class data + int retcode = sql->Execute( "delete from tf_classdata_rollup" ); + if ( 0 != retcode ) + { + Msg( "Failed to delete from tf_classdata_rollup\n" ); + return; + } + // create new rollup for class data + retcode = sql->Execute( "insert into tf_classdata_rollup (class,spawns,totaltime,score,kills,deaths,assists,captures) " + "select class,sum(spawns),sum(totaltime),sum(score),sum(kills),sum(deaths),sum(assists),sum(captures) from tf_classdata group by class;" ); + if ( 0 != retcode ) + { + Msg( "Failed to create class data rollup\n" ); + return; + } + + // delete existing rollup for map data + retcode = sql->Execute( "delete from tf_mapdata_rollup" ); + if ( 0 != retcode ) + { + Msg( "Failed to delete from tf_mapdata_rollup\n" ); + return; + } + // create new rollup for map data + retcode = sql->Execute( "insert into tf_mapdata_rollup (mapname,roundsplayed,totaltime,bluewins,redwins,stalemates) " + "select mapname,sum(roundsplayed),sum(totaltime),sum(bluewins),sum(redwins),sum(stalemates) from tf_mapdata group by mapname;" ); + if ( 0 != retcode ) + { + Msg( "Failed to create map data rollup\n" ); + return; + } +#endif +} |