mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// tagbuild.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__BC019BF6_D3B1_4EA1_AA0E_044EEC919C6D__INCLUDED_)
|
||||
#define AFX_STDAFX_H__BC019BF6_D3B1_4EA1_AA0E_044EEC919C6D__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__BC019BF6_D3B1_4EA1_AA0E_044EEC919C6D__INCLUDED_)
|
||||
@@ -0,0 +1,34 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef BASE_GAMESTATS_PARSE_H
|
||||
#define BASE_GAMESTATS_PARSE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
enum CustomDataReturnCode
|
||||
{
|
||||
CUSTOMDATA_NONE = 0,
|
||||
CUSTOMDATA_FAILED,
|
||||
CUSTOMDATA_SUCCESS
|
||||
};
|
||||
|
||||
class IMySQL;
|
||||
|
||||
struct ParseContext_t
|
||||
{
|
||||
char const *file;
|
||||
char const *gamename;
|
||||
bool describeonly;
|
||||
IMySQL *mysql;
|
||||
int skipcount;
|
||||
bool bCustomDirectoryNotMade;
|
||||
};
|
||||
|
||||
void ProcessPerfData( IMySQL *pSQL, time_t fileTime, char const *pGameName, char const *pPerfString );
|
||||
|
||||
#endif // BASE_GAMESTATS_PARSE_H
|
||||
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The cs game stats header
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#define CS_STATS_BLOB_VERSION 3
|
||||
|
||||
extern const char *pValidStatLevels[];
|
||||
|
||||
#define CS_NUM_LEVELS 18
|
||||
#define WEAPON_MAX 34
|
||||
|
||||
int GetCSLevelIndex( const char *pLevelName );
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char szGameName[8];
|
||||
byte iVersion;
|
||||
char szMapName[32];
|
||||
char ipAddr[4];
|
||||
short port;
|
||||
int serverid;
|
||||
} gamestats_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gamestats_header_t header;
|
||||
short iMinutesPlayed;
|
||||
|
||||
short iTerroristVictories[CS_NUM_LEVELS];
|
||||
short iCounterTVictories[CS_NUM_LEVELS];
|
||||
short iBlackMarketPurchases[WEAPON_MAX];
|
||||
|
||||
short iAutoBuyPurchases;
|
||||
short iReBuyPurchases;
|
||||
short iAutoBuyM4A1Purchases;
|
||||
short iAutoBuyAK47Purchases;
|
||||
short iAutoBuyFamasPurchases;
|
||||
short iAutoBuyGalilPurchases;
|
||||
short iAutoBuyVestHelmPurchases;
|
||||
short iAutoBuyVestPurchases;
|
||||
|
||||
} cs_gamestats_t;
|
||||
@@ -0,0 +1,191 @@
|
||||
create table weapons
|
||||
(
|
||||
WeaponID INT,
|
||||
PRIMARY KEY( WeaponID ),
|
||||
Count INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table maps
|
||||
(
|
||||
MapName CHAR(16),
|
||||
PRIMARY KEY( MapName ),
|
||||
TerroristWins INT,
|
||||
CTWins INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table autobuy
|
||||
(
|
||||
Purchase CHAR(16),
|
||||
PRIMARY KEY( Purchase ),
|
||||
Count INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table purchases_pricing
|
||||
(
|
||||
snapshot CHAR(16),
|
||||
PRIMARY KEY( snapshot ),
|
||||
dt2 DATETIME,
|
||||
weapon_id INT,
|
||||
purchases INT,
|
||||
current_price INT,
|
||||
projected_price INT,
|
||||
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table snapshot_counter
|
||||
(
|
||||
counter INT,
|
||||
PRIMARY KEY( counter ),
|
||||
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table weapon_info
|
||||
(
|
||||
WeaponID INT,
|
||||
PRIMARY KEY( WeaponID ),
|
||||
side ENUM('Terrorists', 'Counter-Terrorists', 'Both'),
|
||||
fullname CHAR( 32 ),
|
||||
default_price INT,
|
||||
current_price INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
|
||||
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 1, 3, "228 COMPACT", 600, 600 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 2, 3, "9X19MM SIDEARM", 400, 400 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 16, 3, "K&M .45 TACTICAL", 500, 500 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 25, 3, "NIGHT HAWK .50C", 650, 650 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 11, 2, "ES FIVE-SEVEN", 750, 750 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 10, 1, ".40 DUAL ELITES", 800, 800 );
|
||||
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 20, 3, "LEONE 12 GAUGE SUPER", 1700, 1700 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 5, 3, "LEONE YG1265 AUTO SHOTGUN", 3000, 3000 );
|
||||
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 22, 2, "SCHMIDT MACHINE PISTOL", 1250, 1250 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 18, 3, "K&M SUB-MACHINE GUN", 1500, 1500 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 12, 3, "K&M UMP45", 1700, 1700 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 29, 3, "ES C90", 2350, 2350 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 7, 1, "INGRAM MAC-10", 1400, 1400 );
|
||||
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 15, 2, "CLARION 5.56", 2250, 2250 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 3, 3, "SCHMIDT SCOUT", 2750, 2750 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 21, 2, "MAVERICK M4A1 CARBINE", 3100, 3100 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 8, 2, "BULLPUP", 3500, 3500 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 13, 1, "KRIEG 550 COMMANDO", 4200, 4200 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 26, 1, "KRIEG 552", 3500, 3500 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 17, 3, "MAGNUM SNIPER RIFLE", 4750, 4750 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 14, 1, "IDF DEFENDER", 2000, 2000 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 27, 1, "CV-47", 2500, 2500 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 23, 2, "D3/AU-1", 5000, 5000 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 19, 3, "M249", 5750, 5750 );
|
||||
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 31, 3, "KEVLAR", 650, 650 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 32, 3, "KEVLAR+HELMET", 1000, 1000 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 24, 3, "FLASHBANG", 200, 200 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 4, 3, "HE GRENADE", 300, 300 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 9, 3, "SMOKE GRENADE", 300, 300 );
|
||||
Insert into weapon_info ( WeaponID, side, fullname, default_price, current_price ) values ( 33, 3, "NIGHTVISION", 1250, 1250 );
|
||||
|
||||
|
||||
|
||||
# update weapons set Count=Count+450 where WeaponID = 0;
|
||||
# Insert into weapons (WeaponID,Count) values ( 1, 0 );
|
||||
|
||||
# update maps set TerroristWins=0, CTWins=0 where MapName = "cs_assault";
|
||||
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_assault", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_compound", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_havana", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_italy", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_militia", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "cs_office", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_aztec", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_cbble", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_chateau", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_dust2", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_dust", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_inferno", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_nuke", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_piranesi", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_port", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_prodigy", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_tides", 0, 0 );
|
||||
# Insert into maps (MapName,TerroristWins, CTWins) values ( "de_train", 0, 0 );
|
||||
|
||||
|
||||
//Autobuy
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Re-Buy", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: M4A1", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: AK47", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: Famas", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: Galil", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: Suit", 0 );
|
||||
Insert into autobuy (Purchase, Count) values ( "Auto-Buy: Kev", 0 );
|
||||
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy";
|
||||
update autobuy set Count=0 where Purchase = "Re-Buy";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: M4A1";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: AK47";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: Famas";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: Galil";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: Suit";
|
||||
update autobuy set Count=0 where Purchase = "Auto-Buy: Kev";
|
||||
|
||||
|
||||
|
||||
//Copy and Paste into the SQL terminal to reset everything
|
||||
|
||||
update weapons set Count=0 where WeaponID = 1;
|
||||
update weapons set Count=0 where WeaponID = 2;
|
||||
update weapons set Count=0 where WeaponID = 3;
|
||||
update weapons set Count=0 where WeaponID = 4;
|
||||
update weapons set Count=0 where WeaponID = 5;
|
||||
update weapons set Count=0 where WeaponID = 6;
|
||||
update weapons set Count=0 where WeaponID = 7;
|
||||
update weapons set Count=0 where WeaponID = 8;
|
||||
update weapons set Count=0 where WeaponID = 9;
|
||||
update weapons set Count=0 where WeaponID = 10;
|
||||
update weapons set Count=0 where WeaponID = 11;
|
||||
update weapons set Count=0 where WeaponID = 12;
|
||||
update weapons set Count=0 where WeaponID = 13;
|
||||
update weapons set Count=0 where WeaponID = 14;
|
||||
update weapons set Count=0 where WeaponID = 15;
|
||||
update weapons set Count=0 where WeaponID = 16;
|
||||
update weapons set Count=0 where WeaponID = 17;
|
||||
update weapons set Count=0 where WeaponID = 18;
|
||||
update weapons set Count=0 where WeaponID = 19;
|
||||
update weapons set Count=0 where WeaponID = 20;
|
||||
update weapons set Count=0 where WeaponID = 21;
|
||||
update weapons set Count=0 where WeaponID = 22;
|
||||
update weapons set Count=0 where WeaponID = 23;
|
||||
update weapons set Count=0 where WeaponID = 24;
|
||||
update weapons set Count=0 where WeaponID = 25;
|
||||
update weapons set Count=0 where WeaponID = 26;
|
||||
update weapons set Count=0 where WeaponID = 27;
|
||||
update weapons set Count=0 where WeaponID = 28;
|
||||
update weapons set Count=0 where WeaponID = 29;
|
||||
update weapons set Count=0 where WeaponID = 30;
|
||||
update weapons set Count=0 where WeaponID = 31;
|
||||
update weapons set Count=0 where WeaponID = 32;
|
||||
update weapons set Count=0 where WeaponID = 33;
|
||||
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_assault";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_compound";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_havana";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_italy";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_militia";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "cs_office";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_aztec";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_cbble";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_chateau";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_dust2";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_dust";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_inferno";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_nuke";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_piranesi";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_port";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_prodigy";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_tides";
|
||||
update maps set TerroristWins=0, CTWins=0 where MapName = "de_train";
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
//========= 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 "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 "cs_gamestats.h"
|
||||
#include "base_gamestats_parse.h"
|
||||
|
||||
extern CUtlDict< int, unsigned short > g_mapOrder;
|
||||
|
||||
const char *pValidStatLevels[] =
|
||||
{
|
||||
"cs_assault",
|
||||
"cs_compound",
|
||||
"cs_havana",
|
||||
"cs_italy",
|
||||
"cs_militia",
|
||||
"cs_office",
|
||||
"de_aztec",
|
||||
"de_cbble",
|
||||
"de_chateau",
|
||||
"de_dust2",
|
||||
"de_dust",
|
||||
"de_inferno",
|
||||
"de_nuke",
|
||||
"de_piranesi",
|
||||
"de_port",
|
||||
"de_prodigy",
|
||||
"de_tides",
|
||||
"de_train",
|
||||
};
|
||||
|
||||
static const char * s_WeaponAliasInfo[] =
|
||||
{
|
||||
"none", // WEAPON_NONE
|
||||
"p228", // WEAPON_P228
|
||||
"glock", // WEAPON_GLOCK // old glock
|
||||
"scout", // WEAPON_SCOUT
|
||||
"hegren", // WEAPON_HEGRENADE
|
||||
"xm1014", // WEAPON_XM1014 // auto shotgun
|
||||
"c4", // WEAPON_C4
|
||||
"mac10", // WEAPON_MAC10 // T only
|
||||
"aug", // WEAPON_AUG
|
||||
"sgren", // WEAPON_SMOKEGRENADE
|
||||
"elite", // WEAPON_ELITE
|
||||
"fiveseven",// WEAPON_FIVESEVEN
|
||||
"ump45", // WEAPON_UMP45
|
||||
"sg550", // WEAPON_SG550 // auto-sniper
|
||||
"galil", // WEAPON_GALIL
|
||||
"famas", // WEAPON_FAMAS // CT cheap m4a1
|
||||
"usp", // WEAPON_USP
|
||||
"awp", // WEAPON_AWP
|
||||
"mp5navy", // WEAPON_MP5N
|
||||
"m249", // WEAPON_M249 // big machinegun
|
||||
"m3", // WEAPON_M3 // cheap shotgun
|
||||
"m4a1", // WEAPON_M4A1
|
||||
"tmp", // WEAPON_TMP
|
||||
"g3sg1", // WEAPON_G3SG1 // T auto-sniper
|
||||
"flash", // WEAPON_FLASHBANG
|
||||
"deagle", // WEAPON_DEAGLE
|
||||
"sg552", // WEAPON_SG552 // T aug equivalent
|
||||
"ak47", // WEAPON_AK47
|
||||
"knife", // WEAPON_KNIFE
|
||||
"p90", // WEAPON_P90
|
||||
"shield", // WEAPON_SHIELDGUN
|
||||
"kevlar",
|
||||
"assaultsuit",
|
||||
"nightvision",
|
||||
NULL, // WEAPON_NONE
|
||||
};
|
||||
|
||||
void DescribeData( cs_gamestats_t &stats )
|
||||
{
|
||||
Msg( " Blob version: %d\n", stats.header.iVersion );
|
||||
Msg( " Server Uptime: %d\n", stats.iMinutesPlayed );
|
||||
|
||||
for ( int i = 0; i < CS_NUM_LEVELS; i++ )
|
||||
{
|
||||
Msg( "%s - Terrorists Wins: %d | Counter-Terrorists Wins: %d\n", pValidStatLevels[i], stats.iTerroristVictories[i], stats.iCounterTVictories[i] );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < WEAPON_MAX; i++ )
|
||||
{
|
||||
Msg( "%s was purchased %d time(s)\n", s_WeaponAliasInfo[i], stats.iBlackMarketPurchases[i] );
|
||||
}
|
||||
|
||||
|
||||
char q[ 512 ];
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy = %d\n", stats.iAutoBuyPurchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Re-Buy = %d\n", stats.iReBuyPurchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: M4A1 = %d\n", stats.iAutoBuyM4A1Purchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: AK47 = %d\n", stats.iAutoBuyAK47Purchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: Famas = %d\n", stats.iAutoBuyFamasPurchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: Galil = %d\n", stats.iAutoBuyGalilPurchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: Suit = %d\n", stats.iAutoBuyVestHelmPurchases );
|
||||
Msg( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "Auto-Buy: Kev = %d\n", stats.iAutoBuyVestPurchases );
|
||||
Msg( q );
|
||||
}
|
||||
|
||||
int CS_ParseCustomGameStatsData( ParseContext_t *ctx )
|
||||
{
|
||||
if ( g_pFullFileSystem == NULL )
|
||||
return CUSTOMDATA_FAILED;
|
||||
|
||||
FileHandle_t FileHandle = g_pFullFileSystem->Open( ctx->file, "rb" );
|
||||
|
||||
if ( !FileHandle )
|
||||
{
|
||||
return CUSTOMDATA_FAILED;
|
||||
}
|
||||
|
||||
if ( ctx->mysql == NULL && ctx->describeonly == false )
|
||||
return CUSTOMDATA_FAILED;
|
||||
|
||||
char q[ 512 ];
|
||||
cs_gamestats_t stats;
|
||||
g_pFullFileSystem->Read( &stats, sizeof( cs_gamestats_t ), FileHandle );
|
||||
|
||||
if ( Q_stricmp( stats.header.szGameName, "cstrike" ) )
|
||||
return CUSTOMDATA_FAILED;
|
||||
|
||||
if ( stats.header.iVersion != CS_STATS_BLOB_VERSION )
|
||||
{
|
||||
Msg( "Error: Incorrect Blob Version! Got: %d - Expected: %d\n", stats.header.iVersion, CS_STATS_BLOB_VERSION );
|
||||
return CUSTOMDATA_FAILED;
|
||||
}
|
||||
|
||||
if ( ctx->describeonly == true )
|
||||
{
|
||||
DescribeData( stats );
|
||||
return CUSTOMDATA_SUCCESS;
|
||||
}
|
||||
|
||||
//Do maps first
|
||||
for ( int i = 0; i < CS_NUM_LEVELS; i++ )
|
||||
{
|
||||
Q_snprintf( q, sizeof( q ), "update maps set TerroristWins=TerroristWins+%d, CTWins=CTWins+%d where MapName = \"%s\";", stats.iTerroristVictories[i], stats.iCounterTVictories[i], pValidStatLevels[i] );
|
||||
|
||||
int retcode = ctx->mysql->Execute( q );
|
||||
if ( retcode != 0 )
|
||||
{
|
||||
printf( "Query:\n %s\n failed\n", q );
|
||||
return CUSTOMDATA_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
//Now do all weapons
|
||||
for ( int i = 0; i < WEAPON_MAX; i++ )
|
||||
{
|
||||
int iWeaponID = i;
|
||||
|
||||
//HACKHACK: Fix up incorrect data for the smoke grenades.
|
||||
if ( i == 0 && stats.iBlackMarketPurchases[i] != 0 )
|
||||
{
|
||||
iWeaponID = 9;
|
||||
}
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update weapons set Count=Count+%d where WeaponID = %d;", stats.iBlackMarketPurchases[i], iWeaponID );
|
||||
|
||||
int retcode = ctx->mysql->Execute( q );
|
||||
if ( retcode != 0 )
|
||||
{
|
||||
printf( "Query:\n %s\n failed\n", q );
|
||||
return CUSTOMDATA_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy\";", stats.iAutoBuyPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Re-Buy\";", stats.iReBuyPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: M4A1\";", stats.iAutoBuyM4A1Purchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: AK47\";", stats.iAutoBuyAK47Purchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: Famas\";", stats.iAutoBuyFamasPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: Galil\";", stats.iAutoBuyGalilPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: Suit\";", stats.iAutoBuyVestHelmPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
Q_snprintf( q, sizeof( q ), "update autobuy set Count=Count+%d where Purchase = \"Auto-Buy: Kev\";", stats.iAutoBuyVestPurchases );
|
||||
ctx->mysql->Execute( q );
|
||||
|
||||
|
||||
|
||||
return CUSTOMDATA_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# create database gamestats;
|
||||
# use gamestats;
|
||||
|
||||
create table ep1
|
||||
(
|
||||
UserID CHAR(16),
|
||||
PRIMARY KEY( UserID ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
hl2_chapter TINYINT,
|
||||
SecondsToCompleteGame INT, # Non-zero if user has completed game
|
||||
HighestMap CHAR(16),
|
||||
DXLevel INT,
|
||||
Deaths INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep1_maps
|
||||
(
|
||||
UserID CHAR(16),
|
||||
MapName CHAR(16),
|
||||
PRIMARY KEY( UserID,MapName ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
Deaths INT,
|
||||
) TYPE=MyISAM;
|
||||
@@ -0,0 +1,168 @@
|
||||
create database gamestats_ep2;
|
||||
use gamestats_ep2;
|
||||
|
||||
create table ep2
|
||||
(
|
||||
UserID CHAR(16),
|
||||
PRIMARY KEY( UserID ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
hl2_chapter TINYINT,
|
||||
SecondsToCompleteGame INT, # Non-zero if user has completed game
|
||||
HighestMap CHAR(20),
|
||||
DXLevel INT,
|
||||
Deaths INT
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_maps
|
||||
(
|
||||
UserID CHAR(16),
|
||||
MapName CHAR(20),
|
||||
PRIMARY KEY( UserID,MapName ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
Deaths INT
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_entities
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
Entity CHAR(32),
|
||||
PRIMARY KEY( UserID,Tag,MapName,Entity ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
BodyCount INT,
|
||||
KilledPlayer INT
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_deaths
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
DeathIndex INT,
|
||||
X SMALLINT,
|
||||
Y SMALLINT,
|
||||
Z SMALLINT,
|
||||
PRIMARY KEY ( UserID, Tag, MapName, X, Y, Z )
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_weapons
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
Weapon CHAR(32),
|
||||
PRIMARY KEY( UserID,Tag, MapName,Weapon ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Shots INT,
|
||||
Hits INT,
|
||||
Damage DOUBLE
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_saves
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
FIRSTDEATH INT, # index into ep2_deaths
|
||||
NUMDEATHS INT,
|
||||
X SMALLINT,
|
||||
Y SMALLINT,
|
||||
Z SMALLINT,
|
||||
HEALTH SMALLINT,
|
||||
SAVETYPE TINYINT, # 0 unknown, 1 autosave, 2 user save (quick or other)
|
||||
PRIMARY KEY( UseriD, Tag, MapName, FirstDeath, NumDeaths )
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_counters
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
PRIMARY KEY( UserID,Tag,MapName ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
CRATESSMASHED INT,
|
||||
OBJECTSPUNTED INT,
|
||||
VEHICULARHOMICIDES INT,
|
||||
DISTANCE_INVEHICLE BIGINT,
|
||||
DISTANCE_ONFOOT BIGINT,
|
||||
DISTANCE_ONFOOTSPRINTING BIGINT,
|
||||
FALLINGDEATHS INT,
|
||||
VEHICLE_OVERTURNED INT,
|
||||
LOADGAME_STILLALIVE INT,
|
||||
LOADS INT,
|
||||
SAVES INT,
|
||||
GODMODES INT,
|
||||
NOCLIPS INT,
|
||||
|
||||
DAMAGETAKEN DOUBLE
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table ep2_generic
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
StatName CHAR(16),
|
||||
PRIMARY KEY( UserID,Tag, MapName,StatName ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Count INT,
|
||||
Value DOUBLE,
|
||||
X SMALLINT,
|
||||
Y SMALLINT,
|
||||
Z SMALLINT
|
||||
) TYPE=MyISAM;
|
||||
@@ -0,0 +1,606 @@
|
||||
//========= 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# create database gamestats;
|
||||
# use gamestats;
|
||||
|
||||
create table portal
|
||||
(
|
||||
UserID CHAR(16),
|
||||
PRIMARY KEY( UserID ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
hl2_chapter TINYINT,
|
||||
SecondsToCompleteGame INT, # Non-zero if user has completed game
|
||||
HighestMap CHAR(16),
|
||||
DXLevel INT,
|
||||
Deaths INT,
|
||||
) TYPE=MyISAM;
|
||||
|
||||
create table portal_maps
|
||||
(
|
||||
UserID CHAR(16),
|
||||
MapName CHAR(16),
|
||||
PRIMARY KEY( UserID,MapName ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
Version TINYINT,
|
||||
Count INT,
|
||||
Seconds INT,
|
||||
HDR INT,
|
||||
Captions INT,
|
||||
Commentary INT,
|
||||
Easy INT,
|
||||
Medium INT,
|
||||
Hard INT,
|
||||
nonsteam TINYINT,
|
||||
cybercafe TINYINT,
|
||||
Deaths INT,
|
||||
) TYPE=MyISAM;
|
||||
@@ -0,0 +1,982 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// tagbuild.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#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 "KeyValues.h"
|
||||
#include "filesystem_helpers.h"
|
||||
#include "tier2/tier2.h"
|
||||
#include "filesystem.h"
|
||||
#include "base_gamestats_parse.h"
|
||||
#include "cbase.h"
|
||||
#include "gamestats.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
// roll-our-own symbol table class. Note we don't use CUtlSymbolTable because that and related classes have short int deeply baked in as index type, so can
|
||||
// only hold 64K entries. We sometimes need to process more than 64K files at a time.
|
||||
struct AnalysisData
|
||||
{
|
||||
AnalysisData()
|
||||
{
|
||||
symbols.SetLessFunc( CaselessStringLessThanIgnoreSlashes );
|
||||
}
|
||||
|
||||
~AnalysisData()
|
||||
{
|
||||
int i = symbols.FirstInorder();
|
||||
while ( i != symbols.InvalidIndex() )
|
||||
{
|
||||
const char *symbol = symbols[i];
|
||||
if ( symbol )
|
||||
{
|
||||
delete symbol;
|
||||
}
|
||||
i = symbols.NextInorder( i );
|
||||
}
|
||||
}
|
||||
|
||||
CUtlRBTree<const char*,int> symbols;
|
||||
};
|
||||
|
||||
static AnalysisData g_Analysis;
|
||||
|
||||
static bool describeonly = false;
|
||||
|
||||
typedef int (*DataParseFunc)( ParseContext_t * );
|
||||
typedef void (*PostImportFunc) ( IMySQL *sql );
|
||||
typedef bool (*ParseCurrentUserIDFunc)( char const *pchDataFile, char *pchUserID, size_t bufsize, time_t &modifiedtime );
|
||||
|
||||
extern int CS_ParseCustomGameStatsData( ParseContext_t *ctx );
|
||||
extern int Ep2_ParseCustomGameStatsData( ParseContext_t *ctx );
|
||||
extern int TF_ParseCustomGameStatsData( ParseContext_t *ctx );
|
||||
extern void TF_PostImport( IMySQL *sql );
|
||||
|
||||
int Default_ParseCustomGameStatsData( ParseContext_t *ctx );
|
||||
|
||||
extern bool Ep2_ParseCurrentUserID( char const *pchDataFile, char *pchUserID, size_t bufsize, time_t &modifiedtime );
|
||||
|
||||
struct DataParser_t
|
||||
{
|
||||
char const *pchGameName;
|
||||
DataParseFunc pfnParseFunc;
|
||||
PostImportFunc pfnPostImport;
|
||||
ParseCurrentUserIDFunc pfnParseUserID;
|
||||
};
|
||||
|
||||
static DataParser_t g_ParseFuncs[] =
|
||||
{
|
||||
{ "cstrike", CS_ParseCustomGameStatsData, NULL },
|
||||
{ "tf", TF_ParseCustomGameStatsData, TF_PostImport },
|
||||
// { "dods", Default_ParseCustomGameStatsData, NULL },
|
||||
// { "portal", Default_ParseCustomGameStatsData, NULL },
|
||||
{ "ep1", Default_ParseCustomGameStatsData, NULL }, // Just a STUB
|
||||
{ "ep2", Ep2_ParseCustomGameStatsData, NULL, Ep2_ParseCurrentUserID }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void printusage( void )
|
||||
{
|
||||
printf( "processgamestats:\n" );
|
||||
printf( "processgamestats game dbhost user password dbname rootdir\n" );
|
||||
printf( "processgamestats game datafile [describe only]\n\n" );
|
||||
printf( "valid gamenames:\n" );
|
||||
|
||||
for ( int i = 0 ; i < ARRAYSIZE( g_ParseFuncs ); ++i )
|
||||
{
|
||||
printf( " %s\n", g_ParseFuncs[ i ].pchGameName );
|
||||
}
|
||||
|
||||
// Exit app
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
void BuildFileList_R( CUtlVector< int >& files, char const *dir, char const *extension )
|
||||
{
|
||||
WIN32_FIND_DATA wfd;
|
||||
|
||||
char directory[ 256 ];
|
||||
char filename[ 256 ];
|
||||
HANDLE ff;
|
||||
|
||||
sprintf( directory, "%s\\*.*", dir );
|
||||
|
||||
if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
|
||||
return;
|
||||
|
||||
int extlen = strlen( extension );
|
||||
|
||||
do
|
||||
{
|
||||
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
|
||||
{
|
||||
|
||||
if ( wfd.cFileName[ 0 ] == '.' )
|
||||
continue;
|
||||
|
||||
// Recurse down directory
|
||||
sprintf( filename, "%s\\%s", dir, wfd.cFileName );
|
||||
BuildFileList_R( files, filename, extension );
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = strlen( wfd.cFileName );
|
||||
if ( len > extlen )
|
||||
{
|
||||
if ( !stricmp( &wfd.cFileName[ len - extlen ], extension ) )
|
||||
{
|
||||
char filename[ MAX_PATH ];
|
||||
Q_snprintf( filename, sizeof( filename ), "%s\\%s", dir, wfd.cFileName );
|
||||
_strlwr( filename );
|
||||
|
||||
Q_FixSlashes( filename );
|
||||
|
||||
char *symbol = strdup( filename );
|
||||
int sym = g_Analysis.symbols.Insert( symbol );
|
||||
|
||||
files.AddToTail( sym );
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ( FindNextFile( ff, &wfd ) );
|
||||
}
|
||||
|
||||
void BuildFileList( CUtlVector< int >& files, char const *rootdir, char const *extension )
|
||||
{
|
||||
files.RemoveAll();
|
||||
BuildFileList_R( files, rootdir, extension );
|
||||
}
|
||||
|
||||
void DescribeData( BasicGameStats_t &stats, const char *szStatsFileUserID, int iStatsFileVersion )
|
||||
{
|
||||
double averageSession = 0.0f;
|
||||
if ( stats.m_Summary.m_nCount > 0 )
|
||||
{
|
||||
averageSession = (double)stats.m_Summary.m_nSeconds / (double)stats.m_Summary.m_nCount;
|
||||
}
|
||||
|
||||
Msg( "---------------------------------------------------------------------------\n" );
|
||||
Msg( "%16.16s : %s\n", "User", szStatsFileUserID );
|
||||
Msg( " %16.16s: %8d\n", "Blob version", iStatsFileVersion );
|
||||
Msg( " %16.16s: %8d sessions\n", "Played", stats.m_Summary.m_nCount );
|
||||
Msg( " %16.16s: %8d seconds\n", "Total Time", stats.m_Summary.m_nSeconds );
|
||||
Msg( " %16.16s: %8.2f seconds\n", "Avg Session", averageSession );
|
||||
|
||||
Msg( " %16.16s: %8d\n", "Commentary", stats.m_Summary.m_nCommentary );
|
||||
Msg( " %16.16s: %8d\n", "HDR", stats.m_Summary.m_nHDR );
|
||||
Msg( " %16.16s: %8d\n", "Captions", stats.m_Summary.m_nCaptions );
|
||||
Msg( " %16.16s: %8d\n", "Easy", stats.m_Summary.m_nSkill[ 0 ] );
|
||||
Msg( " %16.16s: %8d\n", "Medium", stats.m_Summary.m_nSkill[ 1 ] );
|
||||
Msg( " %16.16s: %8d\n", "Hard", stats.m_Summary.m_nSkill[ 2 ] );
|
||||
Msg( " %16.16s: %8d seconds\n", "Completion time ", stats.m_nSecondsToCompleteGame );
|
||||
Msg( " %16.16s: %8d\n", "Number of deaths", stats.m_Summary.m_nDeaths );
|
||||
|
||||
Msg( " -- Maps played --\n" );
|
||||
|
||||
for ( int i = stats.m_MapTotals.First(); i != stats.m_MapTotals.InvalidIndex(); i = stats.m_MapTotals.Next( i ) )
|
||||
{
|
||||
char const *mapname = stats.m_MapTotals.GetElementName( i );
|
||||
BasicGameStatsRecord_t &rec = stats.m_MapTotals[ i ];
|
||||
|
||||
Msg( " %16.16s: %5d seconds in %3d sessions (%4d deaths)\n", mapname, rec.m_nSeconds, rec.m_nCount, rec.m_nDeaths );
|
||||
}
|
||||
}
|
||||
|
||||
#include <string>
|
||||
//-------------------------------------------------
|
||||
void v_escape_string (std::string& s)
|
||||
{
|
||||
if ( !s.size() )
|
||||
return;
|
||||
for ( unsigned int i = 0;i<s.size();i++ )
|
||||
{
|
||||
switch (s[i])
|
||||
{
|
||||
case '\0': /* Must be escaped for "mysql" */
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"0",1); i++;//lint !e534
|
||||
break;
|
||||
case '\n': /* Must be escaped for logs */
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"n",1); i++;//lint !e534
|
||||
break;
|
||||
case '\r':
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"r",1); i++;//lint !e534
|
||||
break;
|
||||
case '\\':
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"\\",1); i++;//lint !e534
|
||||
break;
|
||||
case '\"':
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"\"",1); i++;//lint !e534
|
||||
break;
|
||||
case '\'': /* Better safe than sorry */
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"\'",1); i++;//lint !e534
|
||||
break;
|
||||
case '\032': /* This gives problems on Win32 */
|
||||
s[i] = '\\';
|
||||
s.insert(i+1,"Z",1); i++;//lint !e534
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InsertData( CUtlDict< int, unsigned short >& mapOrder, IMySQL *sql, BasicGameStats_t &gs, const char *szStatsFileUserID, int iStatsFileVersion, const char *gamename, char const *tag = NULL )
|
||||
{
|
||||
if ( !sql )
|
||||
return;
|
||||
|
||||
char q[ 512 ];
|
||||
|
||||
std::string userid;
|
||||
userid = szStatsFileUserID;
|
||||
v_escape_string( userid );
|
||||
|
||||
int farthestPlayed = -1;
|
||||
std::string highestmap;
|
||||
|
||||
int namelen = 20;
|
||||
if ( !Q_stricmp( gamename, "ep1" ) )
|
||||
{
|
||||
namelen = 16;
|
||||
}
|
||||
|
||||
char finalname[ 64 ];
|
||||
|
||||
std::string finaltag;
|
||||
finaltag = tag ? tag : "";
|
||||
v_escape_string( finaltag );
|
||||
|
||||
// Deal with the maps
|
||||
for ( int i = gs.m_MapTotals.First(); i != gs.m_MapTotals.InvalidIndex(); i = gs.m_MapTotals.Next( i ) )
|
||||
{
|
||||
char const *pszMapName = gs.m_MapTotals.GetElementName( i );
|
||||
std::string mapname;
|
||||
mapname = pszMapName;
|
||||
v_escape_string( mapname );
|
||||
|
||||
Q_strncpy( finalname, mapname.c_str(), namelen );
|
||||
|
||||
int slot = mapOrder.Find( pszMapName );
|
||||
if ( slot != mapOrder.InvalidIndex() )
|
||||
{
|
||||
int order = mapOrder[ slot ];
|
||||
if ( order > farthestPlayed )
|
||||
{
|
||||
farthestPlayed = order;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( Q_stricmp( pszMapName, "devtest" ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BasicGameStatsRecord_t& rec = gs.m_MapTotals[ i ];
|
||||
|
||||
if ( tag )
|
||||
{
|
||||
Q_snprintf( q, sizeof( q ), "REPLACE into %s_maps (UserID,LastUpdate,Version,MapName,Tag,Count,Seconds,HDR,Captions,Commentary,Easy,Medium,Hard,nonsteam,cybercafe,Deaths) values (\"%s\",Now(),%d,\"%s\",\"%s\",%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d);",
|
||||
gamename,
|
||||
userid.c_str(),
|
||||
iStatsFileVersion,
|
||||
finalname,
|
||||
finaltag.c_str(),
|
||||
rec.m_nCount,
|
||||
rec.m_nSeconds,
|
||||
rec.m_nHDR,
|
||||
rec.m_nCaptions,
|
||||
rec.m_nCommentary,
|
||||
rec.m_nSkill[ 0 ],
|
||||
rec.m_nSkill[ 1 ],
|
||||
rec.m_nSkill[ 2 ],
|
||||
rec.m_bSteam ? 0 : 1,
|
||||
rec.m_bCyberCafe ? 1 : 0,
|
||||
rec.m_nDeaths );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_snprintf( q, sizeof( q ), "REPLACE into %s_maps (UserID,LastUpdate,Version,MapName,Count,Seconds,HDR,Captions,Commentary,Easy,Medium,Hard,nonsteam,cybercafe,Deaths) values (\"%s\",Now(),%d,\"%s\",%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d);",
|
||||
gamename,
|
||||
userid.c_str(),
|
||||
iStatsFileVersion,
|
||||
finalname,
|
||||
rec.m_nCount,
|
||||
rec.m_nSeconds,
|
||||
rec.m_nHDR,
|
||||
rec.m_nCaptions,
|
||||
rec.m_nCommentary,
|
||||
rec.m_nSkill[ 0 ],
|
||||
rec.m_nSkill[ 1 ],
|
||||
rec.m_nSkill[ 2 ],
|
||||
rec.m_bSteam ? 0 : 1,
|
||||
rec.m_bCyberCafe ? 1 : 0,
|
||||
rec.m_nDeaths );
|
||||
}
|
||||
|
||||
int retcode = sql->Execute( q );
|
||||
if ( retcode != 0 )
|
||||
{
|
||||
printf( "Query %s failed\n", q );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( farthestPlayed != -1 )
|
||||
{
|
||||
highestmap = mapOrder.GetElementName( farthestPlayed );
|
||||
}
|
||||
v_escape_string( highestmap );
|
||||
Q_strncpy( finalname, highestmap.c_str(), namelen );
|
||||
|
||||
if ( tag )
|
||||
{
|
||||
Q_snprintf( q, sizeof( q ), "REPLACE into %s (UserID,LastUpdate,Version,Tag,Count,Seconds,HDR,Captions,Commentary,Easy,Medium,Hard,SecondsToCompleteGame,HighestMap,nonsteam,cybercafe,hl2_chapter,dxlevel,Deaths) values (\"%s\",Now(),%d,\"%s\",%d,%d,%d,%d,%d,%d,%d,%d,%d,\"%s\",%d,%d,%d,%d,%d);",
|
||||
|
||||
gamename,
|
||||
userid.c_str(),
|
||||
iStatsFileVersion,
|
||||
finaltag.c_str(),
|
||||
gs.m_Summary.m_nCount,
|
||||
gs.m_Summary.m_nSeconds,
|
||||
gs.m_Summary.m_nHDR,
|
||||
gs.m_Summary.m_nCaptions,
|
||||
gs.m_Summary.m_nCommentary,
|
||||
gs.m_Summary.m_nSkill[ 0 ],
|
||||
gs.m_Summary.m_nSkill[ 1 ],
|
||||
gs.m_Summary.m_nSkill[ 2 ],
|
||||
gs.m_nSecondsToCompleteGame,
|
||||
finalname,
|
||||
gs.m_bSteam ? 0 : 1,
|
||||
gs.m_bCyberCafe ? 1 : 0,
|
||||
gs.m_nHL2ChaptureUnlocked,
|
||||
gs.m_nDXLevel,
|
||||
gs.m_Summary.m_nDeaths );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_snprintf( q, sizeof( q ), "REPLACE into %s (UserID,LastUpdate,Version,Count,Seconds,HDR,Captions,Commentary,Easy,Medium,Hard,SecondsToCompleteGame,HighestMap,nonsteam,cybercafe,hl2_chapter,dxlevel,Deaths) values (\"%s\",Now(),%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,\"%s\",%d,%d,%d,%d,%d);",
|
||||
|
||||
gamename,
|
||||
userid.c_str(),
|
||||
iStatsFileVersion,
|
||||
gs.m_Summary.m_nCount,
|
||||
gs.m_Summary.m_nSeconds,
|
||||
gs.m_Summary.m_nHDR,
|
||||
gs.m_Summary.m_nCaptions,
|
||||
gs.m_Summary.m_nCommentary,
|
||||
gs.m_Summary.m_nSkill[ 0 ],
|
||||
gs.m_Summary.m_nSkill[ 1 ],
|
||||
gs.m_Summary.m_nSkill[ 2 ],
|
||||
gs.m_nSecondsToCompleteGame,
|
||||
finalname,
|
||||
gs.m_bSteam ? 0 : 1,
|
||||
gs.m_bCyberCafe ? 1 : 0,
|
||||
gs.m_nHL2ChaptureUnlocked,
|
||||
gs.m_nDXLevel,
|
||||
gs.m_Summary.m_nDeaths );
|
||||
}
|
||||
|
||||
int retcode = sql->Execute( q );
|
||||
if ( retcode != 0 )
|
||||
{
|
||||
printf( "Query %s failed\n", q );
|
||||
return;
|
||||
}
|
||||
}
|
||||
CUtlDict< int, unsigned short > g_mapOrder;
|
||||
|
||||
void BuildMapList( void )
|
||||
{
|
||||
void *buffer = NULL;
|
||||
char *pFileList;
|
||||
FILE * pFile;
|
||||
pFile = fopen ("maplist.txt", "r");
|
||||
int i = 0;
|
||||
|
||||
if ( pFile )
|
||||
{
|
||||
long lSize;
|
||||
// obtain file size.
|
||||
fseek (pFile , 0 , SEEK_END);
|
||||
lSize = ftell (pFile);
|
||||
rewind (pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
buffer = (char*) malloc (lSize);
|
||||
if ( buffer != NULL )
|
||||
{
|
||||
// copy the file into the buffer.
|
||||
fread (buffer,1,lSize,pFile);
|
||||
pFileList = (char*)buffer;
|
||||
char szToken[1024];
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
pFileList = ParseFile( pFileList, szToken, false );
|
||||
|
||||
if ( pFileList == NULL )
|
||||
break;
|
||||
|
||||
g_mapOrder.Insert( szToken, i );
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
fclose( pFile );
|
||||
free( buffer );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Couldn't load maplist.txt for mod!!!\n" );
|
||||
}
|
||||
}
|
||||
|
||||
int Default_ParseCustomGameStatsData( ParseContext_t *ctx )
|
||||
{
|
||||
FILE *fp = fopen( ctx->file, "rb" );
|
||||
if ( fp )
|
||||
{
|
||||
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 );
|
||||
|
||||
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;
|
||||
|
||||
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 )) );
|
||||
|
||||
BasicGameStats_t stats;
|
||||
valid = stats.ParseFromBuffer( statsBuffer, iCurrentStatsFileVersion );
|
||||
|
||||
if ( describeonly )
|
||||
{
|
||||
DescribeData( stats, szCurrentStatsFileUserID, iCurrentStatsFileVersion );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( valid )
|
||||
{
|
||||
InsertData( g_mapOrder, ctx->mysql, stats, szCurrentStatsFileUserID, iCurrentStatsFileVersion, ctx->gamename );
|
||||
}
|
||||
else
|
||||
{
|
||||
++ctx->skipcount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check for custom data
|
||||
bool bHasCustomData = (valid && (statsBuffer.TellPut() != statsBuffer.TellGet()));
|
||||
|
||||
if( bHasCustomData )
|
||||
{
|
||||
if( describeonly )
|
||||
{
|
||||
//separate out the custom data and store it off for processing by other applications,
|
||||
//since they only wanted to 'describe' the data, just use a local temp and overwrite it each time
|
||||
|
||||
const char *szCustomDataOutputFileName = "customdata_temp.dat";
|
||||
|
||||
Msg( "\n\nFound custom data, dumping to %s\n", szCustomDataOutputFileName );
|
||||
|
||||
FILE *pCustomDataOutput = fopen( szCustomDataOutputFileName, "wb+" );
|
||||
if( pCustomDataOutput )
|
||||
{
|
||||
int iGetPosition = statsBuffer.TellGet();
|
||||
fwrite( (((unsigned char *)statsBuffer.Base()) + iGetPosition), statsBuffer.TellPut() - iGetPosition, 1, pCustomDataOutput );
|
||||
fclose( pCustomDataOutput );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//separate out the custom data and store it off for processing by other applications,
|
||||
//assume we will have multiple input stats files from the same user, so store custom data under their userid name and overwrite old data to avoid bloat
|
||||
if( ctx->bCustomDirectoryNotMade )
|
||||
{
|
||||
CreateDirectory( "customdatadumps", NULL );
|
||||
ctx->bCustomDirectoryNotMade = false;
|
||||
}
|
||||
|
||||
char szCustomDataOutputFileName[256];
|
||||
Q_snprintf( szCustomDataOutputFileName, sizeof( szCustomDataOutputFileName ), "customdatadumps/%s.dat", szCurrentStatsFileUserID );
|
||||
|
||||
FILE *pCustomDataOutput = fopen( szCustomDataOutputFileName, "wb+" );
|
||||
if( pCustomDataOutput )
|
||||
{
|
||||
int iGetPosition = statsBuffer.TellGet();
|
||||
fwrite( (((unsigned char *)statsBuffer.Base()) + iGetPosition), statsBuffer.TellPut() - iGetPosition, 1, pCustomDataOutput );
|
||||
fclose( pCustomDataOutput );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return CUSTOMDATA_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
CommandLine()->CreateCmdLine( argc, argv );
|
||||
|
||||
ParseContext_t ctx;
|
||||
|
||||
if ( argc < 7 && argc != 3 )
|
||||
{
|
||||
printusage();
|
||||
}
|
||||
|
||||
describeonly = argc == 3;
|
||||
|
||||
int gameArg = 1;
|
||||
int hostArg = 2;
|
||||
int usernameArg = 3;
|
||||
int pwArg = 4;
|
||||
int dbArg = 5;
|
||||
int dirArg = 6;
|
||||
if ( describeonly )
|
||||
{
|
||||
dirArg = 2;
|
||||
}
|
||||
|
||||
InitDefaultFileSystem();
|
||||
|
||||
BuildMapList();
|
||||
const char *gamename = argv[ gameArg ];
|
||||
DataParseFunc parseFunc = NULL;
|
||||
PostImportFunc postImportFunc = NULL;
|
||||
ParseCurrentUserIDFunc parseUserIDFunc = NULL;
|
||||
for ( int i = 0 ; i < ARRAYSIZE( g_ParseFuncs ); ++i )
|
||||
{
|
||||
if ( !Q_stricmp( g_ParseFuncs[ i ].pchGameName, gamename ) )
|
||||
{
|
||||
parseFunc = g_ParseFuncs[ i ].pfnParseFunc;
|
||||
postImportFunc = g_ParseFuncs[ i ].pfnPostImport;
|
||||
parseUserIDFunc = g_ParseFuncs[ i ].pfnParseUserID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !parseFunc )
|
||||
{
|
||||
printf( "Invalid game name '%s'\n", gamename );
|
||||
printusage();
|
||||
}
|
||||
|
||||
bool batchMode = true;
|
||||
|
||||
CUtlVector< int > files;
|
||||
if ( describeonly || Q_stristr( argv[ dirArg ], ".dat" ) )
|
||||
{
|
||||
char filename[ MAX_PATH ];
|
||||
Q_snprintf( filename, sizeof( filename ), "%s", argv[ dirArg ] );
|
||||
_strlwr( filename );
|
||||
Q_FixSlashes( filename );
|
||||
char *symbol = strdup( filename );
|
||||
int sym = g_Analysis.symbols.Insert( symbol );
|
||||
files.AddToTail( sym );
|
||||
|
||||
batchMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Building file list\n" );
|
||||
BuildFileList( files, argv[ dirArg ], "dat" );
|
||||
}
|
||||
|
||||
if ( !files.Count() )
|
||||
{
|
||||
printf( "No files to operate upon\n" );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
int c = files.Count();
|
||||
|
||||
// Cull list of files by looking for most recent version of user's stats and only keeping around those files
|
||||
if ( parseUserIDFunc )
|
||||
{
|
||||
struct CUserIDFileMapping
|
||||
{
|
||||
CUserIDFileMapping() :
|
||||
filename( UTL_INVAL_SYMBOL ), filemodifiedtime( 0 ), modcount( 1 )
|
||||
{
|
||||
userid[ 0 ] = 0;
|
||||
}
|
||||
|
||||
char userid[ 17 ];
|
||||
CUtlSymbol filename;
|
||||
time_t filemodifiedtime;
|
||||
int modcount;
|
||||
|
||||
static bool Less( const CUserIDFileMapping &lhs, const CUserIDFileMapping &rhs )
|
||||
{
|
||||
return Q_stricmp( lhs.userid, rhs.userid ) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CUtlRBTree< CUserIDFileMapping, int > userIDToFileMap( 0, 0, CUserIDFileMapping::Less );
|
||||
|
||||
int nDiscards = 0;
|
||||
int nSkips =0;
|
||||
int nMaxMod = 1;
|
||||
for ( int i = 0; i < c; ++i )
|
||||
{
|
||||
char const *fn = g_Analysis.symbols.Element( files[ i ] );
|
||||
|
||||
CUserIDFileMapping search;
|
||||
search.filename = files[ i ];
|
||||
if ( (*parseUserIDFunc)( fn, search.userid, sizeof( search.userid ), search.filemodifiedtime ) )
|
||||
{
|
||||
// Find map index
|
||||
int idx = userIDToFileMap.Find( search );
|
||||
if ( idx == userIDToFileMap.InvalidIndex() )
|
||||
{
|
||||
userIDToFileMap.Insert( search );
|
||||
}
|
||||
else
|
||||
{
|
||||
CUserIDFileMapping &update = userIDToFileMap[ idx ];
|
||||
if ( search.filemodifiedtime > update.filemodifiedtime )
|
||||
{
|
||||
update.filename = files[ i ];
|
||||
update.filemodifiedtime = search.filemodifiedtime;
|
||||
update.modcount++;
|
||||
if ( update.modcount > nMaxMod )
|
||||
{
|
||||
nMaxMod = update.modcount;
|
||||
}
|
||||
}
|
||||
++nDiscards;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++nSkips;
|
||||
}
|
||||
|
||||
if ( i > 0 && !( i % 100 ) )
|
||||
{
|
||||
printf( "Parsing user ID's: [%-6.6d/%-6.6d] %.2f %% complete\n", i, c, 100.0f * (float)i/(float)c );
|
||||
}
|
||||
}
|
||||
|
||||
Msg( "discarded %d of %d, remainder %d [%d skipped] max mod %d\n", nDiscards, c, userIDToFileMap.Count(), nSkips, nMaxMod );
|
||||
|
||||
// Now re-write files and count with pared down listing
|
||||
files.Purge();
|
||||
for( int i = userIDToFileMap.FirstInorder(); i != userIDToFileMap.InvalidIndex(); i = userIDToFileMap.NextInorder( i ) )
|
||||
{
|
||||
files.AddToTail( userIDToFileMap[ i ].filename );
|
||||
}
|
||||
c = files.Count();
|
||||
}
|
||||
|
||||
bool bTrySql = !describeonly;
|
||||
|
||||
bool bSqlOkay = false;
|
||||
|
||||
CSysModule *sql = NULL;
|
||||
CreateInterfaceFn factory = NULL;
|
||||
IMySQL *mysql = NULL;
|
||||
|
||||
if ( bTrySql )
|
||||
{
|
||||
// Now connect to steamweb and update the engineaccess table
|
||||
sql = Sys_LoadModule( "mysql_wrapper" );
|
||||
if ( sql )
|
||||
{
|
||||
factory = Sys_GetFactory( sql );
|
||||
if ( factory )
|
||||
{
|
||||
mysql = ( IMySQL * )factory( MYSQL_WRAPPER_VERSION_NAME, NULL );
|
||||
if ( mysql )
|
||||
{
|
||||
if ( mysql->InitMySQL( argv[ dbArg ], argv[ hostArg ], argv[ usernameArg ], argv[ pwArg ] ) )
|
||||
{
|
||||
bSqlOkay = true;
|
||||
if ( batchMode )
|
||||
{
|
||||
Msg( "Successfully connected to database %s on host %s, user %s\n",
|
||||
argv[ dbArg ], argv[ hostArg ], argv[ usernameArg ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "mysql->InitMySQL( %s, %s, %s, [password]) failed\n",
|
||||
argv[ dbArg ], argv[ hostArg ], argv[ usernameArg ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Unable to get MYSQL_WRAPPER_VERSION_NAME(%s) from mysql_wrapper\n", MYSQL_WRAPPER_VERSION_NAME );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Sys_GetFactory on mysql_wrapper failed\n" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Sys_LoadModule( mysql_wrapper ) failed\n" );
|
||||
}
|
||||
}
|
||||
|
||||
ctx.gamename = gamename;
|
||||
ctx.describeonly = describeonly;
|
||||
ctx.mysql = mysql;
|
||||
ctx.skipcount = 0;
|
||||
ctx.bCustomDirectoryNotMade = true;
|
||||
|
||||
if ( bSqlOkay || describeonly )
|
||||
{
|
||||
|
||||
for ( int i = 0; i < c; ++i )
|
||||
{
|
||||
char const *fn = g_Analysis.symbols.Element( files[ i ] );
|
||||
|
||||
ctx.file = fn;
|
||||
|
||||
int iCustomData = (*parseFunc)( &ctx );
|
||||
if ( iCustomData == CUSTOMDATA_SUCCESS )
|
||||
{
|
||||
if ( i > 0 && !( i % 100 ) )
|
||||
{
|
||||
printf( "Processing: [%-6.6d/%-6.6d] %.2f %% complete\n", i, c, 100.0f * (float)i/(float)c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ctx.skipcount > 0 )
|
||||
{
|
||||
printf( "Skipped %d samples which appear to be malformed or contain bogus data\n", ctx.skipcount );
|
||||
}
|
||||
|
||||
// if this game has a post-import function to call after all the files have been imported, call it now
|
||||
if ( bSqlOkay && postImportFunc )
|
||||
{
|
||||
postImportFunc( mysql );
|
||||
}
|
||||
}
|
||||
|
||||
if ( bSqlOkay )
|
||||
{
|
||||
if ( mysql )
|
||||
{
|
||||
mysql->Release();
|
||||
mysql = NULL;
|
||||
}
|
||||
|
||||
if ( sql )
|
||||
{
|
||||
Sys_UnloadModule( sql );
|
||||
sql = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void OverWriteCharsWeHate( char *pStr )
|
||||
{
|
||||
while( *pStr )
|
||||
{
|
||||
switch( *pStr )
|
||||
{
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\\':
|
||||
case '\"':
|
||||
case '\'':
|
||||
case '\032':
|
||||
case ';':
|
||||
*pStr = ' ';
|
||||
}
|
||||
pStr++;
|
||||
}
|
||||
}
|
||||
|
||||
void InsertKeyDataIntoTable( IMySQL *pSQL, time_t fileTime, char const *pTableName, char const *pPerfData, char const *pKeyWhiteList[], int nNumFields )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
// we don't need to worry about semicolons embedded in string fields because we supressed them
|
||||
// on the client. if some malicious person inserts them, the mandled field names will fail the
|
||||
// whitelist check, causing the record to be ignored.
|
||||
CUtlVector<char *> tokens;
|
||||
// split into tokens at non-quoated spaces or ;'s
|
||||
for(;;)
|
||||
{
|
||||
char const *pStr = pPerfData;
|
||||
if ( pStr[0] == 0 )
|
||||
break;
|
||||
while( pStr[0] && ( pStr[0] != ' ' ) && ( pStr[0] != ';' ) )
|
||||
{
|
||||
if ( pStr[0]=='"')
|
||||
{
|
||||
// skip to end quote
|
||||
char const *pEq = strchr( pStr + 1, '\"' );
|
||||
if ( ! pEq )
|
||||
{
|
||||
printf(" close quote with no open quote\n" );
|
||||
return;
|
||||
}
|
||||
pStr = pEq;
|
||||
}
|
||||
pStr++;
|
||||
}
|
||||
// got a field
|
||||
int nlen = pStr - pPerfData;
|
||||
if ( nlen > 2 )
|
||||
{
|
||||
char *pToken = new char[ nlen + 1 ];
|
||||
memcpy( pToken, pPerfData, nlen );
|
||||
pToken[nlen] = 0;
|
||||
tokens.AddToTail( pToken );
|
||||
}
|
||||
if ( pStr[0] )
|
||||
pStr++;
|
||||
pPerfData = pStr;
|
||||
}
|
||||
|
||||
bool bBadData = false;
|
||||
char fieldNameBuffer[1024];
|
||||
char fieldValueBuffer[2048];
|
||||
strcpy( fieldNameBuffer, "(CreationTimeStamp, " );
|
||||
Q_snprintf( fieldValueBuffer, ARRAYSIZE( fieldValueBuffer), "( %s,", szDate );
|
||||
for( int i = 0; i < tokens.Count(); i++ )
|
||||
{
|
||||
char *pKVData = tokens[i];
|
||||
char *pEqualsSign = strchr( pKVData, '=' );
|
||||
if (! pEqualsSign )
|
||||
{
|
||||
bBadData = true;
|
||||
break;
|
||||
}
|
||||
*pEqualsSign = 0; // *semicolon->null
|
||||
// check that the field is in the white list
|
||||
bool bFoundIt = false;
|
||||
for( int nCheck = 0; nCheck < nNumFields; nCheck++ )
|
||||
if ( strcmp( pKVData, pKeyWhiteList[nCheck] ) == 0 )
|
||||
{
|
||||
bFoundIt = true;
|
||||
break;
|
||||
}
|
||||
V_strncat( fieldNameBuffer, pKVData, sizeof( fieldNameBuffer ) );
|
||||
if ( i != tokens.Count() -1 )
|
||||
V_strncat( fieldNameBuffer, ",", sizeof( fieldNameBuffer ) );
|
||||
else
|
||||
V_strncat( fieldNameBuffer, ")", sizeof( fieldNameBuffer ) );
|
||||
char *pValue = pEqualsSign + 1;
|
||||
OverWriteCharsWeHate( pValue );
|
||||
if ( ( strlen( pValue ) < 1 ) || (! bFoundIt ) )
|
||||
{
|
||||
bBadData = true;
|
||||
break;
|
||||
}
|
||||
// kill lead + trail space
|
||||
if ( pValue[0] == ' ' )
|
||||
pValue++;
|
||||
if ( pValue[strlen(pValue) - 1 ] == ' ' )
|
||||
pValue[strlen( pValue ) - 1 ] =0;
|
||||
V_strncat( fieldValueBuffer, "'", sizeof( fieldValueBuffer ) );
|
||||
V_strncat( fieldValueBuffer, pValue, sizeof( fieldValueBuffer ) );
|
||||
if ( i != tokens.Count() -1 )
|
||||
V_strncat( fieldValueBuffer, "',", sizeof( fieldValueBuffer ) );
|
||||
else
|
||||
V_strncat( fieldValueBuffer, "')", sizeof( fieldValueBuffer ) );
|
||||
}
|
||||
if (! bBadData )
|
||||
{
|
||||
char sqlCommandBuffer[1024 + sizeof( fieldNameBuffer ) + sizeof( fieldValueBuffer ) ];
|
||||
sprintf( sqlCommandBuffer, "insert into %s %s values %s;", pTableName, fieldNameBuffer, fieldValueBuffer );
|
||||
// printf("cmd %s\n", sqlCommandBuffer);
|
||||
int retcode = pSQL->Execute( sqlCommandBuffer );
|
||||
if ( retcode != 0 )
|
||||
{
|
||||
printf( "command %s failed\n", sqlCommandBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
tokens.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
char const *s_PerfKeyList[] = {
|
||||
"AvgFps",
|
||||
"MinFps",
|
||||
"MaxFps",
|
||||
"CPUID",
|
||||
"CPUGhz",
|
||||
"NumCores",
|
||||
"GPUDrv",
|
||||
"GPUVendor",
|
||||
"GPUDeviceID",
|
||||
"GPUDriverVersion",
|
||||
"DxLvl",
|
||||
"Width",
|
||||
"Height",
|
||||
"MapName",
|
||||
"TotalLevelTime",
|
||||
"NumLevels"
|
||||
};
|
||||
|
||||
void ProcessPerfData( IMySQL *pSQL, time_t fileTime, char const *pTableName, char const *pPerfData )
|
||||
{
|
||||
InsertKeyDataIntoTable( pSQL, fileTime, pTableName, pPerfData, s_PerfKeyList, ARRAYSIZE( s_PerfKeyList) );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// PROCESSGAMESTATS.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;$SRCDIR\utils\vmpi;$SRCDIR\game\shared;$SRCDIR\game\server;$SRCDIR\game\shared\tf;$SRCDIR\game\shared\econ;$SRCDIR\gcsdk\steamextra"
|
||||
$EnableC++Exceptions "Yes (/EHsc)"
|
||||
$PreprocessorDefinitions "$BASE;GAME_DLL;TF_DLL;NO_STEAM"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Processgamestats"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "$SRCDIR\public\filesystem_helpers.h"
|
||||
$File "$SRCDIR\game\server\GameStats_BasicStatsFunctions.cpp"
|
||||
$File "processgamestats.cpp"
|
||||
|
||||
$Folder "Custom Stats"
|
||||
{
|
||||
$File "base_gamestats_parse.h"
|
||||
$File "cs_gamestats.h"
|
||||
$File "cstrike_gamestats_parse.cpp"
|
||||
$File "$SRCDIR\game\server\episodic\ep2_gamestats.h"
|
||||
$File "ep2_gamestats_parse.cpp"
|
||||
$File "$SRCDIR\game\server\tf\tf_gamestats.h"
|
||||
$File "tf_gamestats_parse.cpp"
|
||||
$File "$SRCDIR\game\shared\tf\tf_gamestats_shared.cpp"
|
||||
$File "$SRCDIR\game\shared\tf\tf_gamestats_shared.h"
|
||||
}
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\tier0\commonmacros.h"
|
||||
$File "$SRCDIR\public\tier0\dbg.h"
|
||||
$File "$SRCDIR\public\tier0\fasttimer.h"
|
||||
$File "$SRCDIR\game\shared\gamestats.h"
|
||||
$File "$SRCDIR\public\tier0\icommandline.h"
|
||||
$File "$SRCDIR\utils\vmpi\imysqlwrapper.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgoff.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgon.h"
|
||||
$File "$SRCDIR\public\tier0\platform.h"
|
||||
$File "$SRCDIR\public\tier0\protected_things.h"
|
||||
$File "$SRCDIR\public\string_t.h"
|
||||
$File "$SRCDIR\public\tier1\strtools.h"
|
||||
$File "$SRCDIR\public\tier1\utlmemory.h"
|
||||
$File "$SRCDIR\public\tier1\utlvector.h"
|
||||
$File "$SRCDIR\public\vstdlib\vstdlib.h"
|
||||
}
|
||||
|
||||
$Folder "Database Setup"
|
||||
{
|
||||
$File "cstrike_gamestats.db"
|
||||
$File "portal_gamestats.db"
|
||||
$File "ep1_gamestats.db"
|
||||
$File "ep2_gamestats.db"
|
||||
$File "tf_gamestats.db"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$Lib tier2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
create database if not exists gamestats_tf;
|
||||
use gamestats_tf;
|
||||
|
||||
create table if not exists tf_mapdata
|
||||
(
|
||||
MapName VARCHAR(64)
|
||||
);
|
||||
alter table tf_mapdata add INDEX(MapName);
|
||||
alter table tf_mapdata add column ServerID VARCHAR(16);
|
||||
alter table tf_mapdata add column TimeSubmitted DATETIME;
|
||||
alter table tf_mapdata add INDEX(TimeSubmitted);
|
||||
alter table tf_mapdata add column RoundsPlayed BIGINT;
|
||||
alter table tf_mapdata add column TotalTime BIGINT;
|
||||
alter table tf_mapdata add column BlueWins BIGINT;
|
||||
alter table tf_mapdata add column RedWins BIGINT;
|
||||
alter table tf_mapdata add column Stalemates BIGINT;
|
||||
|
||||
|
||||
create table if not exists tf_mapdata_rollup
|
||||
(
|
||||
MapName VARCHAR(64)
|
||||
);
|
||||
alter table tf_mapdata_rollup add INDEX(MapName);
|
||||
alter table tf_mapdata_rollup add column TimeSubmitted DATETIME;
|
||||
alter table tf_mapdata_rollup add INDEX(TimeSubmitted);
|
||||
alter table tf_mapdata_rollup add column RoundsPlayed BIGINT;
|
||||
alter table tf_mapdata_rollup add column TotalTime BIGINT;
|
||||
alter table tf_mapdata_rollup add column BlueWins BIGINT;
|
||||
alter table tf_mapdata_rollup add column RedWins BIGINT;
|
||||
alter table tf_mapdata_rollup add column Stalemates BIGINT;
|
||||
|
||||
|
||||
create table if not exists tf_classdata
|
||||
(
|
||||
MapName VARCHAR(64)
|
||||
);
|
||||
alter table tf_classdata add INDEX(MapName);
|
||||
alter table tf_classdata add column ServerID VARCHAR(16);
|
||||
alter table tf_classdata add column TimeSubmitted DATETIME;
|
||||
alter table tf_classdata add INDEX(TimeSubmitted);
|
||||
alter table tf_classdata add column Class TINYINT;
|
||||
alter table tf_classdata add column Spawns BIGINT;
|
||||
alter table tf_classdata add column TotalTime BIGINT;
|
||||
alter table tf_classdata add column Score BIGINT;
|
||||
alter table tf_classdata add column Kills BIGINT;
|
||||
alter table tf_classdata add column Deaths BIGINT;
|
||||
alter table tf_classdata add column Assists BIGINT;
|
||||
alter table tf_classdata add column Captures BIGINT;
|
||||
|
||||
create table if not exists tf_classdata_rollup
|
||||
(
|
||||
MapName VARCHAR(64)
|
||||
);
|
||||
alter table tf_classdata_rollup add INDEX(MapName);
|
||||
alter table tf_classdata_rollup add column TimeSubmitted DATETIME;
|
||||
alter table tf_classdata_rollup add INDEX(TimeSubmitted);
|
||||
alter table tf_classdata_rollup add column Class TINYINT;
|
||||
alter table tf_classdata_rollup add column Spawns BIGINT;
|
||||
alter table tf_classdata_rollup add column TotalTime BIGINT;
|
||||
alter table tf_classdata_rollup add column Score BIGINT;
|
||||
alter table tf_classdata_rollup add column Kills BIGINT;
|
||||
alter table tf_classdata_rollup add column Deaths BIGINT;
|
||||
alter table tf_classdata_rollup add column Assists BIGINT;
|
||||
alter table tf_classdata_rollup add column Captures BIGINT;
|
||||
|
||||
create table if not exists tf_weapondata
|
||||
(
|
||||
WeaponID TINYINT
|
||||
);
|
||||
alter table tf_weapondata add INDEX(WeaponID);
|
||||
alter table tf_weapondata add column ServerID VARCHAR(16);
|
||||
alter table tf_weapondata add column MapName VARCHAR(64);
|
||||
alter table tf_weapondata add INDEX(MapName);
|
||||
alter table tf_weapondata add column TimeSubmitted DATETIME;
|
||||
alter table tf_weapondata add INDEX(TimeSubmitted);
|
||||
alter table tf_weapondata add column ShotsFired BIGINT;
|
||||
alter table tf_weapondata add column ShotsFiredCrit BIGINT;
|
||||
alter table tf_weapondata add column ShotsHit BIGINT;
|
||||
alter table tf_weapondata add column DamageTotal BIGINT;
|
||||
alter table tf_weapondata add column HitsWithKnownDistance BIGINT;
|
||||
alter table tf_weapondata add column DistanceTotal BIGINT;
|
||||
|
||||
create table if not exists tf_weapondata_rollup
|
||||
(
|
||||
WeaponID TINYINT
|
||||
);
|
||||
alter table tf_weapondata_rollup add INDEX(WeaponID);
|
||||
alter table tf_weapondata_rollup add column MapName VARCHAR(64);
|
||||
alter table tf_weapondata_rollup add INDEX(MapName);
|
||||
alter table tf_weapondata_rollup add column TimeSubmitted DATETIME;
|
||||
alter table tf_weapondata_rollup add INDEX(TimeSubmitted);
|
||||
alter table tf_weapondata_rollup add column ShotsFired BIGINT;
|
||||
alter table tf_weapondata_rollup add column ShotsFiredCrit BIGINT;
|
||||
alter table tf_weapondata_rollup add column ShotsHit BIGINT;
|
||||
alter table tf_weapondata_rollup add column DamageTotal BIGINT;
|
||||
alter table tf_weapondata_rollup add column HitsWithKnownDistance BIGINT;
|
||||
alter table tf_weapondata_rollup add column DistanceTotal BIGINT;
|
||||
|
||||
create table if not exists tf_deaths
|
||||
(
|
||||
UserID CHAR(16),
|
||||
Tag CHAR(8),
|
||||
KEY( Tag ),
|
||||
MapName CHAR(20),
|
||||
MapVersion INT,
|
||||
KEY( MapVersion ),
|
||||
LastUpdate DATETIME,
|
||||
KEY( LastUpdate ),
|
||||
DeathIndex INT,
|
||||
X SMALLINT,
|
||||
Y SMALLINT,
|
||||
Z SMALLINT,
|
||||
PRIMARY KEY ( UserID, Tag, MapName, X, Y, Z )
|
||||
) TYPE=MyISAM;
|
||||
|
||||
|
||||
create table if not exists tf_perfdata
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
AvgFps FLOAT,
|
||||
MinFps FLOAT,
|
||||
MaxFps FLOAT,
|
||||
CPUID VARCHAR(64),
|
||||
CPUGhz FLOAT,
|
||||
NumCores INT,
|
||||
GPUDrv VARCHAR(64),
|
||||
GPUVendor INT,
|
||||
GPUDeviceID INT,
|
||||
GPUDriverVersion VARCHAR(25),
|
||||
DxLvl INT,
|
||||
Width INT,
|
||||
Height INT,
|
||||
MapName VARCHAR(64),
|
||||
TotalLevelTime INT,
|
||||
NumLevels SMALLINT,
|
||||
INDEX(CreationTimeStamp),
|
||||
INDEX(AvgFps),
|
||||
INDEX(GPUDrv),
|
||||
INDEX(Width),
|
||||
INDEX(MapName)
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fps
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
INDEX(CreationTimeStamp),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fpsdist
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
BaseFps FLOAT,
|
||||
GPUDrv VARCHAR(64),
|
||||
INDEX(CreationTimeStamp),
|
||||
INDEX(GPUDrv)
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fpsmap
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
MapName VARCHAR(64),
|
||||
INDEX(CreationTimeStamp),
|
||||
INDEX(MapName),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fpscard
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
GPUDrv VARCHAR(64),
|
||||
INDEX(CreationTimeStamp),
|
||||
INDEX(GPUDrv),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fpswidth
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
Width INT,
|
||||
INDEX(CreationTimeStamp),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_fpscardwidth
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
Width INT,
|
||||
GPUDrv VARCHAR(64),
|
||||
INDEX(CreationTimeStamp),
|
||||
INDEX(GPUDrv),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_cpudist
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
NumEntries BIGINT,
|
||||
AvgFps FLOAT,
|
||||
CPUID VARCHAR(64),
|
||||
NumCores INT,
|
||||
BaseCPUGhz FLOAT,
|
||||
GPUVendor INT,
|
||||
INDEX(CreationTimeStamp),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_playtime
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
AvgSession FLOAT,
|
||||
AvgLevels FLOAT,
|
||||
INDEX(CreationTimeStamp),
|
||||
);
|
||||
|
||||
create table if not exists tf_perfdata_rollup_playtimedist
|
||||
(
|
||||
CreationTimeStamp DATETIME,
|
||||
BaseSession FLOAT,
|
||||
NumEntries INT,
|
||||
INDEX(CreationTimeStamp),
|
||||
);
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user