diff options
Diffstat (limited to 'utils/gamestats_reader')
| -rw-r--r-- | utils/gamestats_reader/dod_gamestats.h | 175 | ||||
| -rw-r--r-- | utils/gamestats_reader/gamestats_reader.cpp | 460 | ||||
| -rw-r--r-- | utils/gamestats_reader/gamestats_reader.vpc | 39 |
3 files changed, 674 insertions, 0 deletions
diff --git a/utils/gamestats_reader/dod_gamestats.h b/utils/gamestats_reader/dod_gamestats.h new file mode 100644 index 0000000..a33abfc --- /dev/null +++ b/utils/gamestats_reader/dod_gamestats.h @@ -0,0 +1,175 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: The dod game stats header +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef DOD_GAMESTATS_H +#define DOD_GAMESTATS_H +#ifdef _WIN32 +#pragma once +#endif + +// Redefine some things for the stat reader so it doesn't have to include weapon_dodbase.h +#ifndef GAME_DLL + + typedef enum + { + WEAPON_NONE = 0, + + //Melee + WEAPON_AMERKNIFE, + WEAPON_SPADE, + + //Pistols + WEAPON_COLT, + WEAPON_P38, + WEAPON_C96, + + //Rifles + WEAPON_GARAND, + WEAPON_M1CARBINE, + WEAPON_K98, + + //Sniper Rifles + WEAPON_SPRING, + WEAPON_K98_SCOPED, + + //SMG + WEAPON_THOMPSON, + WEAPON_MP40, + WEAPON_MP44, + WEAPON_BAR, + + //Machine guns + WEAPON_30CAL, + WEAPON_MG42, + + //Rocket weapons + WEAPON_BAZOOKA, + WEAPON_PSCHRECK, + + //Grenades + WEAPON_FRAG_US, + WEAPON_FRAG_GER, + + WEAPON_FRAG_US_LIVE, + WEAPON_FRAG_GER_LIVE, + + WEAPON_SMOKE_US, + WEAPON_SMOKE_GER, + + WEAPON_RIFLEGREN_US, + WEAPON_RIFLEGREN_GER, + + WEAPON_RIFLEGREN_US_LIVE, + WEAPON_RIFLEGREN_GER_LIVE, + + // not actually separate weapons, but defines used in stats recording + // find a better way to do this without polluting the list of actual weapons. + WEAPON_THOMPSON_PUNCH, + WEAPON_MP40_PUNCH, + + WEAPON_GARAND_ZOOMED, + WEAPON_K98_ZOOMED, + WEAPON_SPRING_ZOOMED, + WEAPON_K98_SCOPED_ZOOMED, + + WEAPON_30CAL_UNDEPLOYED, + WEAPON_MG42_UNDEPLOYED, + + WEAPON_BAR_SEMIAUTO, + WEAPON_MP44_SEMIAUTO, + + WEAPON_MAX, // number of weapons weapon index + + } DODWeaponID; + +#endif // ndef WEAPON_NONE + +#define DOD_STATS_BLOB_VERSION 1 + +#define DOD_NUM_DISTANCE_STAT_WEAPONS 22 +#define DOD_NUM_NODIST_STAT_WEAPONS 14 +#define DOD_NUM_WEAPON_DISTANCE_BUCKETS 10 + +extern int iDistanceStatWeapons[DOD_NUM_DISTANCE_STAT_WEAPONS]; +extern int iNoDistStatWeapons[DOD_NUM_NODIST_STAT_WEAPONS]; +extern int iWeaponBucketDistances[DOD_NUM_WEAPON_DISTANCE_BUCKETS-1]; + +#ifndef GAME_DLL + extern const char * s_WeaponAliasInfo[]; +#endif + +typedef struct +{ + char szGameName[8]; + byte iVersion; + char szMapName[32]; + char ipAddr[4]; + short port; + int serverid; +} gamestats_header_t; + +// Stats for bullet weapons - includes distance of hits +typedef struct +{ + short iNumAttacks; // times fired + short iNumHits; // times hit + + // distance buckets - distances are defined per-weapon ( 0 is closest, buckets-1 farthest ) + short iDistanceBuckets[DOD_NUM_WEAPON_DISTANCE_BUCKETS]; + +} dod_gamestats_weapon_distance_t; + +// Stats for non-bullet weapons +typedef struct +{ + short iNumAttacks; // times fired + short iNumHits; // times hit +} dod_gamestats_weapon_nodist_t; + +typedef struct +{ + gamestats_header_t header; + + // Team Scores + byte iNumAlliesWins; + byte iNumAxisWins; + + short iAlliesTickPoints; + short iAxisTickPoints; + + short iMinutesPlayed; // time spent on the map rotation + + // Player Data + short iMinutesPlayedPerClass_Allies[7]; // includes random + short iMinutesPlayedPerClass_Axis[7]; // includes random + + short iKillsPerClass_Allies[6]; + short iKillsPerClass_Axis[6]; + + short iSpawnsPerClass_Allies[6]; + short iSpawnsPerClass_Axis[6]; + + short iCapsPerClass_Allies[6]; + short iCapsPerClass_Axis[6]; + + byte iDefensesPerClass_Allies[6]; + byte iDefensesPerClass_Axis[6]; + + // Server Settings + // assume these class limits don't change through the course of the map + byte iClassLimits_Allies[6]; + byte iClassLimits_Axis[6]; + + // Weapon Data + dod_gamestats_weapon_distance_t weaponStatsDistance[DOD_NUM_DISTANCE_STAT_WEAPONS]; // 14 * 22 = 308 bytes + dod_gamestats_weapon_nodist_t weaponStats[DOD_NUM_NODIST_STAT_WEAPONS]; // 4 * 14 = 56 bytes + + // how many times a weapon was picked up ? + +} dod_gamestats_t; + +#endif // DOD_GAMESTATS_H
\ No newline at end of file diff --git a/utils/gamestats_reader/gamestats_reader.cpp b/utils/gamestats_reader/gamestats_reader.cpp new file mode 100644 index 0000000..038ae58 --- /dev/null +++ b/utils/gamestats_reader/gamestats_reader.cpp @@ -0,0 +1,460 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// +#include <stdio.h> +#include <stdlib.h> +#include <direct.h> +#include "tier1/strtools.h" +#include <conio.h> +#include "tier1/utlbuffer.h" +#include "tier2/tier2.h" +#include "filesystem.h" +#include "imysqlwrapper.h" +#include "../../game/server/dod/dod_gamestats.h" +#include <time.h> + + +static void Pause( void ) +{ + printf( "Hit a key to continue\n" ); + getch(); +} + +static void Usage() +{ + fprintf( stderr, "Usage: gamestats_reader <hostname> <database> <username> <password> <table> <directory to parse> ( -verbose )\n" ); + Pause(); + exit( -1 ); +} + +#define SQL_CMD_BUFSIZE 16000 + +char sqlCmd[SQL_CMD_BUFSIZE]; +bool g_bFirstCmd; +bool g_bVerbose; + +IMySQL *mysql; + +char **g_argv; + +void StartMYSQLInsert( void ) +{ + g_bFirstCmd = true; + sqlCmd[0] = '\0'; + + Q_snprintf( sqlCmd, SQL_CMD_BUFSIZE, "INSERT INTO %s SET ", g_argv[5] ); +} + +void AddField( const char *field, const char *value ) +{ + char buf[128]; + + if ( !g_bFirstCmd ) + { + Q_strncat( sqlCmd, ", ", SQL_CMD_BUFSIZE, COPY_ALL_CHARACTERS ); + } + + Q_snprintf( buf, sizeof(buf), "%s=\"%s\"", field, value ); + Q_strncat( sqlCmd, buf, SQL_CMD_BUFSIZE, COPY_ALL_CHARACTERS ); + + g_bFirstCmd = false; +} + +void AddField( const char *field, const int value ) +{ + char buf[128]; + + if ( !g_bFirstCmd ) + { + Q_strncat( sqlCmd, ", ", SQL_CMD_BUFSIZE, COPY_ALL_CHARACTERS ); + } + + Q_snprintf( buf, sizeof(buf), "%s=%d", field, value ); + +#ifdef _DEBUG + if ( Q_strlen(buf) + Q_strlen(sqlCmd) > SQL_CMD_BUFSIZE ) + { + Assert( !"increase buf size\n" ); + } +#endif + + Q_strncat( sqlCmd, buf, SQL_CMD_BUFSIZE, COPY_ALL_CHARACTERS ); + + g_bFirstCmd = false; +} + +int CompleteMYSQLInsert( void ) +{ + // terminate command and execute it + Q_strncat( sqlCmd, "\n", SQL_CMD_BUFSIZE, COPY_ALL_CHARACTERS ); + + if ( g_bVerbose ) + { + printf( "%s", sqlCmd ); + } + + int retcode = mysql->Execute( sqlCmd ); + + if ( retcode != 0 ) + { + const char *asdf = mysql->GetLastError(); + printf( "Error: %s\n", asdf ); + } + + return retcode; +} + +static const char *pszTeamNames[] = +{ + "allies", + "axis" +}; + +static const char *pszClassNames[] = +{ + "rifleman", + "assault", + "support", + "sniper", + "mg", + "rocket" +}; + +int iDistanceStatWeapons[DOD_NUM_DISTANCE_STAT_WEAPONS] = +{ + WEAPON_COLT, + WEAPON_P38, + WEAPON_C96, + WEAPON_GARAND, + WEAPON_GARAND_ZOOMED, + WEAPON_M1CARBINE, + WEAPON_K98, + WEAPON_K98_ZOOMED, + WEAPON_SPRING, + WEAPON_SPRING_ZOOMED, + WEAPON_K98_SCOPED, + WEAPON_K98_SCOPED_ZOOMED, + WEAPON_THOMPSON, + WEAPON_MP40, + WEAPON_MP44, + WEAPON_MP44_SEMIAUTO, + WEAPON_BAR, + WEAPON_BAR_SEMIAUTO, + WEAPON_30CAL, + WEAPON_30CAL_UNDEPLOYED, + WEAPON_MG42, + WEAPON_MG42_UNDEPLOYED, +}; + +// Send hit/shots only for the following weapons +int iNoDistStatWeapons[DOD_NUM_NODIST_STAT_WEAPONS] = +{ + WEAPON_AMERKNIFE, + WEAPON_SPADE, + WEAPON_BAZOOKA, + WEAPON_PSCHRECK, + WEAPON_FRAG_US, + WEAPON_FRAG_GER, + WEAPON_FRAG_US_LIVE, + WEAPON_FRAG_GER_LIVE, + WEAPON_RIFLEGREN_US, + WEAPON_RIFLEGREN_GER, + WEAPON_RIFLEGREN_US_LIVE, + WEAPON_RIFLEGREN_GER_LIVE, + WEAPON_THOMPSON_PUNCH, + WEAPON_MP40_PUNCH, +}; + +const char * s_WeaponAliasInfo[] = +{ + "none", // WEAPON_NONE = 0, + + //Melee + "amerknife", //WEAPON_AMERKNIFE, + "spade", //WEAPON_SPADE, + + //Pistols + "colt", //WEAPON_COLT, + "p38", //WEAPON_P38, + "c96", //WEAPON_C96 + + //Rifles + "garand", //WEAPON_GARAND, + "m1carbine", //WEAPON_M1CARBINE, + "k98", //WEAPON_K98, + + //Sniper Rifles + "spring", //WEAPON_SPRING, + "k98_scoped", //WEAPON_K98_SCOPED, + + //SMG + "thompson", //WEAPON_THOMPSON, + "mp40", //WEAPON_MP40, + "mp44", //WEAPON_MP44, + "bar", //WEAPON_BAR, + + //Machine guns + "30cal", //WEAPON_30CAL, + "mg42", //WEAPON_MG42, + + //Rocket weapons + "bazooka", //WEAPON_BAZOOKA, + "pschreck", //WEAPON_PSCHRECK, + + //Grenades + "frag_us", //WEAPON_FRAG_US, + "frag_ger", //WEAPON_FRAG_GER, + + "frag_us_live", //WEAPON_FRAG_US_LIVE + "frag_ger_live", //WEAPON_FRAG_GER_LIVE + + "smoke_us", //WEAPON_SMOKE_US + "smoke_ger", //WEAPON_SMOKE_GER + + "riflegren_us", //WEAPON_RIFLEGREN_US + "riflegren_ger", //WEAPON_RIFLEGREN_GER + + "riflegren_us_live", //WEAPON_RIFLEGREN_US_LIVE + "riflegren_ger_live", //WEAPON_RIFLEGREN_GER_LIVE + + // not actually separate weapons, but defines used in stats recording + "thompson_punch", //WEAPON_THOMPSON_PUNCH + "mp40_punch", //WEAPON_MP40_PUNCH + "garand_zoomed", //WEAPON_GARAND_ZOOMED, + + "k98_zoomed", //WEAPON_K98_ZOOMED + "spring_zoomed", //WEAPON_SPRING_ZOOMED + "k98_scoped_zoomed", //WEAPON_K98_SCOPED_ZOOMED + + "30cal_undeployed", //WEAPON_30CAL_UNDEPLOYED, + "mg42_undeployed", //WEAPON_MG42_UNDEPLOYED, + + "bar_semiauto", //WEAPON_BAR_SEMIAUTO, + "mp44_semiauto", //WEAPON_MP44_SEMIAUTO, + + 0, // end of list marker +}; + +void ParseFile( const char *fileName ) +{ + FileHandle_t file = g_pFullFileSystem->Open( fileName, "rb" ); + + if ( !file ) + { + return; + } + + dod_gamestats_t stats; + g_pFullFileSystem->Read( &stats, sizeof( dod_gamestats_t ), file ); + + if ( stats.header.iVersion != DOD_STATS_BLOB_VERSION || Q_stricmp( stats.header.szGameName, "dod" ) ) + { + printf( "Error parsing file, bad header info: %s\n", fileName ); + return; + } + + StartMYSQLInsert(); + + AddField( "map", stats.header.szMapName ); + + const time_t mapfiletime = g_pFullFileSystem->GetFileTime( fileName ); + + struct tm *t = localtime( &mapfiletime ); + + // YYYY-MM-DD HH:MM::SS + + char filetimebuf[64]; + + Q_snprintf( filetimebuf, sizeof(filetimebuf), "%04d-%02d-%02d %02d:%02d:%02d", + t->tm_year + 1900, + t->tm_mon + 1, + t->tm_mday, + t->tm_hour, + t->tm_min, + t->tm_sec ); + + AddField( "time", filetimebuf ); + + AddField( "version", stats.header.iVersion ); + + AddField( "ipaddr_0", stats.header.ipAddr[0] ); + AddField( "ipaddr_1", stats.header.ipAddr[1] ); + AddField( "ipaddr_2", stats.header.ipAddr[2] ); + AddField( "ipaddr_3", stats.header.ipAddr[3] ); + AddField( "port", stats.header.port ); + + AddField( "minutes_map", stats.iMinutesPlayed ); + AddField( "wins_allies", stats.iNumAlliesWins ); + AddField( "wins_axis", stats.iNumAxisWins); + AddField( "tickpoints_allies", stats.iAlliesTickPoints ); + AddField( "tickpoints_axis", stats.iAxisTickPoints ); + + char buf[128]; + + for ( int cls=0;cls<6;cls++ ) + { + Q_snprintf( buf, sizeof(buf), "minutes_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iMinutesPlayedPerClass_Allies[cls] ); + + Q_snprintf( buf, sizeof(buf), "kills_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iKillsPerClass_Allies[cls] ); + + Q_snprintf( buf, sizeof(buf), "defenses_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iDefensesPerClass_Allies[cls] ); + + Q_snprintf( buf, sizeof(buf), "caps_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iCapsPerClass_Allies[cls] ); + + Q_snprintf( buf, sizeof(buf), "spawns_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iSpawnsPerClass_Allies[cls] ); + + Q_snprintf( buf, sizeof(buf), "classlimit_allies_%s", pszClassNames[cls] ); + AddField( buf, stats.iClassLimits_Allies[cls] ); + + + Q_snprintf( buf, sizeof(buf), "minutes_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iMinutesPlayedPerClass_Axis[cls] ); + + Q_snprintf( buf, sizeof(buf), "kills_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iKillsPerClass_Axis[cls] ); + + Q_snprintf( buf, sizeof(buf), "defenses_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iDefensesPerClass_Axis[cls] ); + + Q_snprintf( buf, sizeof(buf), "caps_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iCapsPerClass_Axis[cls] ); + + Q_snprintf( buf, sizeof(buf), "spawns_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iSpawnsPerClass_Axis[cls] ); + + Q_snprintf( buf, sizeof(buf), "classlimit_axis_%s", pszClassNames[cls] ); + AddField( buf, stats.iClassLimits_Axis[cls] ); + } + + int i; + + for ( i=0;i<DOD_NUM_DISTANCE_STAT_WEAPONS;i++ ) + { + int iWeapon = iDistanceStatWeapons[i]; + const char *pszWeapon = s_WeaponAliasInfo[iWeapon]; + + Q_snprintf( buf, sizeof(buf), "weapon_shots_%s", pszWeapon ); + AddField( buf, stats.weaponStatsDistance[i].iNumAttacks ); + + Q_snprintf( buf, sizeof(buf), "weapon_hits_%s", pszWeapon ); + AddField( buf, stats.weaponStatsDistance[i].iNumHits ); + + for ( int j=0;j<DOD_NUM_WEAPON_DISTANCE_BUCKETS;j++ ) + { + Q_snprintf( buf, sizeof(buf), "weapon_distance_%s_%d", pszWeapon, j ); + AddField( buf, stats.weaponStatsDistance[i].iDistanceBuckets[j] ); + } + } + + for ( i=0;i<DOD_NUM_NODIST_STAT_WEAPONS;i++ ) + { + int iWeapon = iNoDistStatWeapons[i]; + const char *pszWeapon = s_WeaponAliasInfo[iWeapon]; + + Q_snprintf( buf, sizeof(buf), "weapon_shots_%s", pszWeapon ); + AddField( buf, stats.weaponStats[i].iNumAttacks ); + + Q_snprintf( buf, sizeof(buf), "weapon_hits_%s", pszWeapon ); + AddField( buf, stats.weaponStats[i].iNumHits ); + } + + CompleteMYSQLInsert(); + + g_pFullFileSystem->Close( file ); +} + +int main( int argc, char **argv ) +{ + g_argv = argv; + + if( argc < 6 ) + { + Usage(); + } + + if ( argc == 7 && !Q_stricmp( argv[6], "-verbose" ) ) + { + g_bVerbose = true; + } + + InitDefaultFileSystem(); + + // Init MYSQL connection + CSysModule *sql = Sys_LoadModule( "mysql_wrapper" ); + if ( sql ) + { + CreateInterfaceFn factory = Sys_GetFactory( sql ); + if ( factory ) + { + mysql = ( IMySQL * )factory( MYSQL_WRAPPER_VERSION_NAME, NULL ); + if ( mysql ) + { + if ( mysql->InitMySQL( argv[ 2 ], argv[ 1 ], argv[ 3 ], argv[ 4 ] ) ) + { + // insert rows + + const char *dir = argv[6]; + + char searchString[MAX_PATH*2]; + Q_strncpy( searchString, dir, sizeof( searchString ) ); + Q_AppendSlash( searchString, sizeof( searchString ) ); + Q_strncat( searchString, "*.dat", sizeof( searchString ), COPY_ALL_CHARACTERS ); + + int iNumFiles = 0; + FileFindHandle_t findHandle = NULL; + const char *filename = g_pFullFileSystem->FindFirst( searchString, &findHandle ); + while ( filename ) + { + char fullFileName[MAX_PATH*2]; + + Q_strncpy( fullFileName, dir, sizeof( fullFileName ) ); + Q_AppendSlash( fullFileName, sizeof( fullFileName ) ); + Q_strncat( fullFileName, filename, sizeof( fullFileName ), COPY_ALL_CHARACTERS ); + + ParseFile( fullFileName ); + + printf( "processing file: %s\n", fullFileName ); + iNumFiles++; + + filename = g_pFullFileSystem->FindNext(findHandle); + } + g_pFullFileSystem->FindClose(findHandle); + + printf( "Completed: %d files processed from directory \"%s\"\n", iNumFiles, dir ); + + } + else + { + printf( "InitMySQL failed ( %s )\n", mysql->GetLastError() ); + } + + mysql->Release(); + } + else + { + printf( "Unable to connect via mysql_wrapper\n"); + } + } + else + { + printf( "Unable to get factory from mysql_wrapper.dll, not updating access mysql table!!!" ); + } + + Sys_UnloadModule( sql ); + } + else + { + printf( "Unable to load mysql_wrapper.dll, not updating access mysql table!!!" ); + } + + return 0; +} diff --git a/utils/gamestats_reader/gamestats_reader.vpc b/utils/gamestats_reader/gamestats_reader.vpc new file mode 100644 index 0000000..a71157e --- /dev/null +++ b/utils/gamestats_reader/gamestats_reader.vpc @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// GAMESTATS_READER.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE;..\common;..\vmpi" + } +} + +$Project "Gamestats_reader" +{ + $Folder "Source Files" + { + $File "gamestats_reader.cpp" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\tier1\interface.h" + $File "$SRCDIR\public\tier1\UtlBuffer.h" + } + + $Folder "Link Libraries" + { + $DynamicFile "$SRCDIR\lib\public\bitmap.lib" + $DynamicFile "$SRCDIR\lib\public\mathlib.lib" + $DynamicFile "$SRCDIR\lib\public\tier2.lib" + } +} |