You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
606 lines
18 KiB
606 lines
18 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
#include "stdafx.h" |
|
#include <stdio.h> |
|
#include <process.h> |
|
#include <string.h> |
|
#include <windows.h> |
|
#include <sys/stat.h> |
|
#include <time.h> |
|
#include <string> |
|
|
|
|
|
#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" |
|
#include "episodic/ep2_gamestats.h" |
|
#include "base_gamestats_parse.h" |
|
|
|
void v_escape_string( std::string& s ); |
|
|
|
// EP2 custom data blob parsing stuff |
|
|
|
static Ep2LevelStats_t *g_pCurrentMap; |
|
static CUtlDict<Ep2LevelStats_t, unsigned short> g_dictMapStats; |
|
|
|
extern CUtlDict< int, unsigned short > g_mapOrder; |
|
|
|
void DescribeData( BasicGameStats_t &stats, const char *szStatsFileUserID, int iStatsFileVersion ); |
|
void InsertData( CUtlDict< int, unsigned short >& mapOrder, IMySQL *sql, BasicGameStats_t &gs, const char *szStatsFileUserID, int iStatsFileVersion, const char *gamename, char const *tag = NULL ); |
|
|
|
struct CounterInfo_t |
|
{ |
|
int type; |
|
char const *counterName; |
|
}; |
|
|
|
static CounterInfo_t g_IntCounterNames[ ] = |
|
{ |
|
{ Ep2LevelStats_t::COUNTER_CRATESSMASHED, "Crates Smashed" }, |
|
{ Ep2LevelStats_t::COUNTER_OBJECTSPUNTED, "Objects Punted" }, |
|
{ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES, "Vehicular Homicides" }, |
|
{ Ep2LevelStats_t::COUNTER_DISTANCE_INVEHICLE, "Distance in Vehicles (inches)" }, |
|
{ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOT, "Distance on Foot (inches)" }, |
|
{ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOTSPRINTING, "Distance on Foot Sprint (inches)" }, |
|
{ Ep2LevelStats_t::COUNTER_FALLINGDEATHS, "Falling Death" }, |
|
{ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED, "Flipped Vehicles" }, |
|
{ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE, "Loadgame while player alive" }, |
|
{ Ep2LevelStats_t::COUNTER_LOADS, "Number of saves (w/autosaves)" }, |
|
{ Ep2LevelStats_t::COUNTER_SAVES, "Number of game loads" }, |
|
{ Ep2LevelStats_t::COUNTER_GODMODES, "Times entered God mode" }, |
|
{ Ep2LevelStats_t::COUNTER_NOCLIPS, "Times entered NoClip mode" }, |
|
}; |
|
|
|
static CounterInfo_t g_FloatCounterNames[ ] = |
|
{ |
|
{ Ep2LevelStats_t::COUNTER_DAMAGETAKEN, "Damage Taken" }, |
|
}; |
|
|
|
char const *SaveType( int type ) |
|
{ |
|
switch ( type ) |
|
{ |
|
default: |
|
case Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_UNKNOWN: |
|
break; |
|
case Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_AUTOSAVE: |
|
return "Auto Save"; |
|
case Ep2LevelStats_t::SaveGameInfoRecord2_t::TYPE_USERSAVE: |
|
return "User Save"; |
|
} |
|
return "Unknown"; |
|
} |
|
|
|
#define INCHES_TO_MILES ( 1.0 / ( 5280.0 * 12.0 ) ) |
|
#define INCHES_TO_FEET ( 1.0 / 12.0 ) |
|
|
|
void DescribeEp2Stats() |
|
{ |
|
int i; |
|
int iMap; |
|
for ( iMap = g_dictMapStats.First(); iMap != g_dictMapStats.InvalidIndex(); iMap = g_dictMapStats.Next( iMap ) ) |
|
{ |
|
// Get the current map. |
|
Ep2LevelStats_t *pCurrentMap = &g_dictMapStats[iMap]; |
|
|
|
Msg( "map version[ %d ], user tag[ %s ]\n", pCurrentMap->m_Tag.m_nMapVersion, pCurrentMap->m_Tag.m_szTagText ); |
|
|
|
Msg( " --- %s ------\n %d deaths\n", pCurrentMap->m_Header.m_szMapName, pCurrentMap->m_aPlayerDeaths.Count() ); |
|
for ( i = 0; i < pCurrentMap->m_aPlayerDeaths.Count(); ++i ) |
|
{ |
|
Msg( " @ ( %d, %d, %d )\n", |
|
pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 0 ], |
|
pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 1 ], |
|
pCurrentMap->m_aPlayerDeaths[ i ].nPosition[ 2 ] ); |
|
} |
|
|
|
Msg( " -- Weapon Stats --\n" ); |
|
|
|
CUtlDict< Ep2LevelStats_t::WeaponLump_t, int > &wdict = pCurrentMap->m_dictWeapons; |
|
for ( i = wdict.First(); i != wdict.InvalidIndex(); i = wdict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::WeaponLump_t &lump = wdict[ i ]; |
|
double acc = 0.0f; |
|
if ( lump.m_nShots > 0 ) |
|
{ |
|
acc = 100.0f * (double)lump.m_nHits / (double)lump.m_nShots; |
|
} |
|
Msg( " %32.32s: shots %5d hits %5d damage %8.2f [accuracy %8.2f %%]\n", wdict.GetElementName( i ), lump.m_nShots, lump.m_nHits, lump.m_flDamageInflicted, acc ); |
|
} |
|
Msg( " -- NPC Stats -- \n" ); |
|
|
|
CUtlDict< Ep2LevelStats_t::EntityDeathsLump_t, int > &dict = pCurrentMap->m_dictEntityDeaths; |
|
for ( i = dict.First(); i != dict.InvalidIndex(); i = dict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::EntityDeathsLump_t &lump = dict[ i ]; |
|
Msg( " %32.32s: bodycount %5d killedplayer %3d\n", dict.GetElementName( i ), lump.m_nBodyCount, lump.m_nKilledPlayer ); |
|
} |
|
|
|
for ( i = 0; i < Ep2LevelStats_t::NUM_INTCOUNTER_TYPES; ++i ) |
|
{ |
|
if ( i == Ep2LevelStats_t::COUNTER_DISTANCE_INVEHICLE || |
|
i == Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOT || |
|
i == Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOTSPRINTING ) |
|
{ |
|
Msg( " %32.32s: %8I64u [%8.3f feet] [%8.3f miles]\n", g_IntCounterNames[ i ].counterName, pCurrentMap->m_IntCounters[ i ], INCHES_TO_FEET * (double)pCurrentMap->m_IntCounters[ i ], INCHES_TO_MILES * (double)pCurrentMap->m_IntCounters[ i ] ); |
|
} |
|
else |
|
{ |
|
Msg( " %32.32s: %8I64u\n", g_IntCounterNames[ i ].counterName, pCurrentMap->m_IntCounters[ i ] ); |
|
} |
|
} |
|
for ( i = 0; i < Ep2LevelStats_t::NUM_FLOATCOUNTER_TYPES; ++i ) |
|
{ |
|
Msg( " %32.32s: %8.2f\n", g_FloatCounterNames[ i ].counterName, pCurrentMap->m_FloatCounters[ i ] ); |
|
} |
|
|
|
Ep2LevelStats_t::SaveGameInfo_t *sg = &pCurrentMap->m_SaveGameInfo; |
|
Msg( " -- Save Game --\n" ); |
|
time_t t = (time_t)sg->m_nCurrentSaveFileTime; |
|
struct tm *timestamp; |
|
timestamp = localtime( &t ); |
|
if ( t == (time_t)0 ) |
|
{ |
|
Msg( " No save file\n" ); |
|
} |
|
else |
|
{ |
|
Msg( " Current save %s file time %s\n", sg->m_sCurrentSaveFile.String(), asctime( timestamp ) ); |
|
} |
|
for ( i = 0; i < sg->m_Records.Count(); ++i ) |
|
{ |
|
const Ep2LevelStats_t::SaveGameInfoRecord2_t &rec = sg->m_Records[ i ]; |
|
Msg( " %3d deaths, saved at (%5d %5d %5d) with health %3d %s\n", |
|
rec.m_nNumDeaths, |
|
(int)rec.m_nSavePos[ 0 ], |
|
(int)rec.m_nSavePos[ 1 ], |
|
(int)rec.m_nSavePos[ 2 ], |
|
(int)rec.m_nSaveHealth, |
|
SaveType( (int)rec.m_SaveType ) ); |
|
} |
|
|
|
Msg( " -- Generic Stats --\n" ); |
|
|
|
CUtlDict< Ep2LevelStats_t::GenericStatsLump_t, int > &gdict = pCurrentMap->m_dictGeneric; |
|
for ( i = gdict.First(); i != gdict.InvalidIndex(); i = gdict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::GenericStatsLump_t &lump = gdict[ i ]; |
|
Msg( " %32.32s: count %u total %f @[%d, %d, %d]\n", gdict.GetElementName( i ), lump.m_unCount, lump.m_flCurrentValue, lump.m_Pos[ 0 ], lump.m_Pos[ 1 ], lump.m_Pos[ 2 ] ); |
|
} |
|
|
|
Msg( "\n" ); |
|
} |
|
} |
|
|
|
void InsertCustomData( CUtlDict< int, unsigned short >& mapOrder, IMySQL *sql, const char *szStatsFileUserID, int iStatsFileVersion, const char *gamename ) |
|
{ |
|
if ( !sql ) |
|
return; |
|
|
|
char q[ 1024 ]; |
|
char counternames[ 512 ]; |
|
|
|
std::string userid; |
|
userid = szStatsFileUserID; |
|
v_escape_string( userid ); |
|
|
|
// Deal with the maps |
|
int i; |
|
int iMap; |
|
for ( iMap = g_dictMapStats.First(); iMap != g_dictMapStats.InvalidIndex(); iMap = g_dictMapStats.Next( iMap ) ) |
|
{ |
|
// Get the current map. |
|
Ep2LevelStats_t *pCurrentMap = &g_dictMapStats[iMap]; |
|
|
|
char const *pszMapName = g_dictMapStats.GetElementName( iMap ); |
|
std::string mapname; |
|
mapname = pszMapName; |
|
v_escape_string( mapname ); |
|
|
|
std::string tag; |
|
tag = pCurrentMap->m_Tag.m_szTagText; |
|
v_escape_string( tag ); |
|
|
|
int mapversion = pCurrentMap->m_Tag.m_nMapVersion; |
|
|
|
#if 1 |
|
int slot = mapOrder.Find( pszMapName ); |
|
if ( slot == mapOrder.InvalidIndex() ) |
|
{ |
|
if ( Q_stricmp( pszMapName, "devtest" ) ) |
|
continue; |
|
} |
|
#endif |
|
|
|
// Deal with deaths |
|
for ( 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);", |
|
gamename, |
|
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; |
|
} |
|
} |
|
|
|
// Deal with weapon data |
|
CUtlDict< Ep2LevelStats_t::WeaponLump_t, int > &wdict = pCurrentMap->m_dictWeapons; |
|
for ( i = wdict.First(); i != wdict.InvalidIndex(); i = wdict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::WeaponLump_t &lump = wdict[ i ]; |
|
|
|
std::string wname = wdict.GetElementName( i ); |
|
v_escape_string( wname ); |
|
|
|
Q_snprintf( q, sizeof( q ), "REPLACE into %s_weapons (UserID,Tag,LastUpdate,MapName,MapVersion,Weapon,Shots,Hits,Damage) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,\"%-.32s\",%d,%d,%g);", |
|
gamename, |
|
userid.c_str(), |
|
tag.c_str(), |
|
mapname.c_str(), |
|
mapversion, |
|
wname.c_str(), |
|
lump.m_nShots, |
|
lump.m_nHits, |
|
lump.m_flDamageInflicted |
|
); |
|
|
|
int retcode = sql->Execute( q ); |
|
if ( retcode != 0 ) |
|
{ |
|
printf( "Query %s failed\n", q ); |
|
return; |
|
} |
|
} |
|
|
|
CUtlDict< Ep2LevelStats_t::EntityDeathsLump_t, int > &dict = pCurrentMap->m_dictEntityDeaths; |
|
for ( i = dict.First(); i != dict.InvalidIndex(); i = dict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::EntityDeathsLump_t &lump = dict[ i ]; |
|
|
|
std::string ename = dict.GetElementName( i ); |
|
v_escape_string( ename ); |
|
|
|
Q_snprintf( q, sizeof( q ), "REPLACE into %s_entities (UserID,Tag,LastUpdate,MapName,MapVersion,Entity,BodyCount,KilledPlayer) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,\"%-.32s\",%d,%d);", |
|
gamename, |
|
userid.c_str(), |
|
tag.c_str(), |
|
mapname.c_str(), |
|
mapversion, |
|
ename.c_str(), |
|
lump.m_nBodyCount, |
|
lump.m_nKilledPlayer |
|
); |
|
|
|
int retcode = sql->Execute( q ); |
|
if ( retcode != 0 ) |
|
{ |
|
printf( "Query %s failed\n", q ); |
|
return; |
|
} |
|
} |
|
|
|
// Counters |
|
Q_snprintf( counternames, sizeof( counternames ), |
|
"CRATESSMASHED,"\ |
|
"OBJECTSPUNTED,"\ |
|
"VEHICULARHOMICIDES,"\ |
|
"DISTANCE_INVEHICLE,"\ |
|
"DISTANCE_ONFOOT,"\ |
|
"DISTANCE_ONFOOTSPRINTING,"\ |
|
"FALLINGDEATHS,"\ |
|
"VEHICLE_OVERTURNED,"\ |
|
"LOADGAME_STILLALIVE,"\ |
|
"LOADS,"\ |
|
"SAVES,"\ |
|
"GODMODES,"\ |
|
"NOCLIPS,"\ |
|
"DAMAGETAKEN" ); |
|
Q_snprintf( q, sizeof( q ), "REPLACE into %s_counters (UserID,Tag,LastUpdate,MapName,MapVersion,%s) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%I64u,"\ |
|
"%I64u,"\ |
|
"%I64u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%u,"\ |
|
"%f);", |
|
gamename, |
|
counternames, |
|
userid.c_str(), |
|
tag.c_str(), |
|
mapname.c_str(), |
|
mapversion, |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_CRATESSMASHED ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_OBJECTSPUNTED ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICULARHOMICIDES ], |
|
pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_INVEHICLE ], |
|
pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOT ], |
|
pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_DISTANCE_ONFOOTSPRINTING ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_FALLINGDEATHS ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_VEHICLE_OVERTURNED ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADGAME_STILLALIVE ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_LOADS ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_SAVES ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_GODMODES ], |
|
(uint32)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ], |
|
(double)pCurrentMap->m_FloatCounters[ Ep2LevelStats_t::COUNTER_DAMAGETAKEN ] |
|
); |
|
|
|
int retcode = sql->Execute( q ); |
|
if ( retcode != 0 ) |
|
{ |
|
printf( "Query %s failed\n", q ); |
|
return; |
|
} |
|
|
|
Ep2LevelStats_t::SaveGameInfo_t *sg = &pCurrentMap->m_SaveGameInfo; |
|
/* |
|
Msg( " -- Save Game --\n" ); |
|
time_t t = (time_t)sg->m_nCurrentSaveFileTime; |
|
struct tm *timestamp; |
|
timestamp = localtime( &t ); |
|
if ( t == (time_t)0 ) |
|
{ |
|
Msg( " No save file\n" ); |
|
} |
|
else |
|
{ |
|
Msg( " Current save %s file time %s\n", sg->m_sCurrentSaveFile.String(), asctime( timestamp ) ); |
|
} |
|
*/ |
|
for ( i = 0; i < sg->m_Records.Count(); ++i ) |
|
{ |
|
const Ep2LevelStats_t::SaveGameInfoRecord2_t &rec = sg->m_Records[ i ]; |
|
|
|
Q_snprintf( q, sizeof( q ), "REPLACE into %s_saves (UserID,Tag,LastUpdate,MapName,MapVersion,FirstDeath,NumDeaths,X,Y,Z,Health,SaveType) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,%d,%d,%d,%d,%d,%d,%d);", |
|
gamename, |
|
userid.c_str(), |
|
tag.c_str(), |
|
mapname.c_str(), |
|
mapversion, |
|
rec.m_nFirstDeathIndex, |
|
rec.m_nNumDeaths, |
|
(int)rec.m_nSavePos[ 0 ], |
|
(int)rec.m_nSavePos[ 1 ], |
|
(int)rec.m_nSavePos[ 2 ], |
|
(int)rec.m_nSaveHealth, |
|
(int)rec.m_SaveType |
|
); |
|
|
|
int retcode = sql->Execute( q ); |
|
if ( retcode != 0 ) |
|
{ |
|
printf( "Query %s failed\n", q ); |
|
return; |
|
} |
|
} |
|
|
|
|
|
// Deal with generic stats data |
|
CUtlDict< Ep2LevelStats_t::GenericStatsLump_t, int > &gdict = pCurrentMap->m_dictGeneric; |
|
for ( i = gdict.First(); i != gdict.InvalidIndex(); i = gdict.Next( i ) ) |
|
{ |
|
const Ep2LevelStats_t::GenericStatsLump_t &lump = gdict[ i ]; |
|
|
|
std::string statname = gdict.GetElementName( i ); |
|
v_escape_string( statname ); |
|
|
|
Q_snprintf( q, sizeof( q ), "REPLACE into %s_generic (UserID,Tag,LastUpdate,MapName,MapVersion,StatName,Count,Value,X,Y,Z) values (\"%s\",\"%s\",Now(),\"%-.20s\",%d,\"%-.16s\",%d,%f,%d,%d,%d);", |
|
gamename, |
|
userid.c_str(), |
|
tag.c_str(), |
|
mapname.c_str(), |
|
mapversion, |
|
statname.c_str(), |
|
lump.m_unCount, |
|
lump.m_flCurrentValue, |
|
lump.m_Pos[ 0 ], |
|
lump.m_Pos[ 1 ], |
|
lump.m_Pos[ 2 ] |
|
); |
|
|
|
int retcode = sql->Execute( q ); |
|
if ( retcode != 0 ) |
|
{ |
|
printf( "Query %s failed\n", q ); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *szMapName - |
|
// Output : Ep2LevelStats_t |
|
//----------------------------------------------------------------------------- |
|
Ep2LevelStats_t *FindOrAddMapStats( const char *szMapName ) |
|
{ |
|
int iMap = g_dictMapStats.Find( szMapName ); |
|
if( iMap == g_dictMapStats.InvalidIndex() ) |
|
{ |
|
iMap = g_dictMapStats.Insert( szMapName ); |
|
} |
|
|
|
return &g_dictMapStats[iMap]; |
|
} |
|
|
|
int GetMaxLumpCount() |
|
{ |
|
return EP2_MAX_LUMP_COUNT; |
|
} |
|
|
|
void LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer, char *tag, size_t tagsize ) |
|
{ |
|
tag[ 0 ] = 0; |
|
Ep2LevelStats_t::LoadData( g_dictMapStats, LoadBuffer ); |
|
if ( g_dictMapStats.Count() > 0 ) |
|
{ |
|
Q_strncpy( tag, g_dictMapStats[ g_dictMapStats.First() ].m_Tag.m_szTagText, tagsize ); |
|
} |
|
|
|
int i; |
|
int iMap; |
|
for ( iMap = g_dictMapStats.First(); iMap != g_dictMapStats.InvalidIndex(); iMap = g_dictMapStats.Next( iMap ) ) |
|
{ |
|
// Get the current map. |
|
Ep2LevelStats_t *pCurrentMap = &g_dictMapStats[iMap]; |
|
CUtlDict< Ep2LevelStats_t::WeaponLump_t, int > &wdict = pCurrentMap->m_dictWeapons; |
|
for ( i = wdict.First(); i != wdict.InvalidIndex(); i = wdict.Next( i ) ) |
|
{ |
|
Ep2LevelStats_t::WeaponLump_t &lump = wdict[ i ]; |
|
// Bogus #, some kind of damage scaling? |
|
if ( lump.m_flDamageInflicted > 50000 ) |
|
{ |
|
lump.m_flDamageInflicted = 0.0f; |
|
lump.m_nHits = 0; |
|
lump.m_nShots = 0; |
|
} |
|
} |
|
|
|
if ( (int64)pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] < 0 ) |
|
{ |
|
pCurrentMap->m_IntCounters[ Ep2LevelStats_t::COUNTER_NOCLIPS ] = 0; |
|
} |
|
} |
|
} |
|
|
|
bool Ep2_ParseCurrentUserID( char const *pchDataFile, char *pchUserID, size_t bufsize, time_t &modifiedtime ) |
|
{ |
|
FILE *fp = fopen( pchDataFile, "rb" ); |
|
if ( fp ) |
|
{ |
|
CUtlBuffer statsBuffer; |
|
|
|
struct _stat sb; |
|
_stat( pchDataFile, &sb ); |
|
|
|
// Msg( "Processing %s\n", ctx->file ); |
|
int nBytesToRead = min( sb.st_size, sizeof( short ) + 16 ); |
|
|
|
statsBuffer.Clear(); |
|
statsBuffer.EnsureCapacity( nBytesToRead ); |
|
fread( statsBuffer.Base(), nBytesToRead, 1, fp ); |
|
statsBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesToRead ); |
|
fclose( fp ); |
|
|
|
// Skip the version |
|
statsBuffer.GetShort(); |
|
statsBuffer.Get( pchUserID, 16 ); |
|
pchUserID[ bufsize - 1 ] = 0; |
|
|
|
modifiedtime = sb.st_mtime; |
|
|
|
return statsBuffer.TellPut() == statsBuffer.TellGet(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
int Ep2_ParseCustomGameStatsData( ParseContext_t *ctx ) |
|
{ |
|
g_dictMapStats.Purge(); |
|
|
|
FILE *fp = fopen( ctx->file, "rb" ); |
|
if ( fp ) |
|
{ |
|
CUtlBuffer statsBuffer; |
|
|
|
struct _stat sb; |
|
_stat( ctx->file, &sb ); |
|
|
|
// Msg( "Processing %s\n", ctx->file ); |
|
|
|
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 ); |
|
|
|
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; |
|
|
|
bool valid = true; |
|
BasicGameStats_t stats; |
|
|
|
char tag[ 32 ] = { 0 }; |
|
|
|
unsigned int iCheckIfStandardDataSaved = statsBuffer.GetUnsignedInt(); |
|
if( iCheckIfStandardDataSaved != GAMESTATS_STANDARD_NOT_SAVED ) |
|
{ |
|
//standard data was saved, rewind so the stats can read in time to completion |
|
statsBuffer.SeekGet( CUtlBuffer::SEEK_CURRENT, -((int)sizeof( unsigned int )) ); |
|
|
|
valid = stats.ParseFromBuffer( statsBuffer, iCurrentStatsFileVersion ); |
|
|
|
if ( ctx->describeonly ) |
|
{ |
|
DescribeData( stats, szCurrentStatsFileUserID, iCurrentStatsFileVersion ); |
|
} |
|
} |
|
|
|
//check for custom data |
|
bool bHasCustomData = (valid && (statsBuffer.TellPut() != statsBuffer.TellGet())); |
|
|
|
if( bHasCustomData ) |
|
{ |
|
LoadCustomDataFromBuffer( statsBuffer, tag, sizeof( tag ) ); |
|
|
|
if( ctx->describeonly ) |
|
{ |
|
DescribeEp2Stats(); |
|
} |
|
else |
|
{ |
|
InsertCustomData( g_mapOrder, ctx->mysql, szCurrentStatsFileUserID, iCurrentStatsFileVersion, ctx->gamename ); |
|
} |
|
} |
|
|
|
// Do base data after custom since we need the custom to parse out the "tag" used for this user |
|
if ( valid ) |
|
{ |
|
if ( !ctx->describeonly ) |
|
{ |
|
InsertData( g_mapOrder, ctx->mysql, stats, szCurrentStatsFileUserID, iCurrentStatsFileVersion, ctx->gamename, tag ); |
|
} |
|
} |
|
else |
|
{ |
|
++ctx->skipcount; |
|
} |
|
} |
|
return CUSTOMDATA_SUCCESS; |
|
} |
|
|
|
|