|
|
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
|
// |
|
|
// Purpose: |
|
|
// |
|
|
// $NoKeywords: $ |
|
|
// |
|
|
//=============================================================================// |
|
|
#pragma warning (disable:4786) |
|
|
//=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. =========== |
|
|
// |
|
|
// The copyright to the contents herein is the property of Valve, L.L.C. |
|
|
// The contents may be used and/or copied only with the written permission of |
|
|
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in |
|
|
// the agreement/contract under which the contents have been supplied. |
|
|
// |
|
|
// Purpose: Implementation of the TFStatsApplication class. |
|
|
// |
|
|
// $Workfile: $ |
|
|
// $Date: $ |
|
|
// |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// $Log: $ |
|
|
// |
|
|
// $NoKeywords: $ |
|
|
//============================================================================= |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <string.h> |
|
|
#include <stdarg.h> |
|
|
#include "TFStatsApplication.h" |
|
|
#include "TFStatsReport.h" |
|
|
#include "LogEvent.h" |
|
|
#include "ScoreBoard.h" |
|
|
#include "WhoKilledWho.h" |
|
|
#include "memdbg.h" |
|
|
#include "awards.h" |
|
|
#include "MatchResults.h" |
|
|
#include "DialogueReadout.h" |
|
|
#include "cvars.h" |
|
|
#include "html.h" |
|
|
#include "TextFile.h" |
|
|
#include "CustomAward.h" |
|
|
#include "PlayerSpecifics.h" |
|
|
#include "util.h" |
|
|
#include "AllPlayersStats.h" |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
CTFStatsApplication* g_pApp; //global! |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::printUsage |
|
|
// Purpose: Prints out how to use the program |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::printUsage() |
|
|
{ |
|
|
|
|
|
printf("TFStats version %li.%li\n",majorVer,minorVer); |
|
|
printf("\nUSAGE:\n"); |
|
|
#ifdef WIN32 |
|
|
printf("TFstatsRT.exe <log file name> <optional switches>\n"); |
|
|
#else |
|
|
printf("TFstats_l <log file name> <optional switches>\n"); |
|
|
#endif |
|
|
printf("the available switches are detailed in the documentation. (TFStats.htm): \n"); |
|
|
printf("---\n"); |
|
|
printf("After TFStats is done, several new HTML files will be in the current directory,\n"); |
|
|
printf("point your browser to the index.html in this directory and it will do the rest.\n"); |
|
|
#ifdef WIN32 |
|
|
printf("(you should just be able to do \"start index.html\")\n"); |
|
|
#endif |
|
|
printf("TFStats uses Regex++:\nRegex++ <EFBFBD> 1998-9 Dr John Maddock.\n"); |
|
|
|
|
|
} |
|
|
|
|
|
#include "CustomAwardList.h" |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::DoAwards |
|
|
// Purpose: This reports all of the awards |
|
|
// Input: MatchResultsPage - the page on which to write the awards' HTML |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::DoAwards(CHTMLFile& MatchResultsPage) |
|
|
{ |
|
|
int chresult=os->chdir(g_pApp->outputDirectory.c_str()); |
|
|
|
|
|
MatchResultsPage.p(); |
|
|
MatchResultsPage.write("<img src=\"%s/awards.gif\">\n",g_pApp->supportHTTPPath.c_str()); |
|
|
MatchResultsPage.div("awards"); |
|
|
|
|
|
//do custom awards first, they're usually the important ones for the match |
|
|
CCustomAwardList* pCust; |
|
|
os->chdir(ruleDirectory.c_str()); |
|
|
pCust=CCustomAwardList::readCustomAwards(g_pMatchInfo->mapName()); |
|
|
os->chdir(outputDirectory.c_str()); |
|
|
if(pCust) |
|
|
{ |
|
|
|
|
|
CCustomAwardIterator it; |
|
|
for (it=pCust->begin();it!=pCust->end();++it) |
|
|
{ |
|
|
(*it)->report(MatchResultsPage); |
|
|
} |
|
|
|
|
|
delete pCust; |
|
|
MatchResultsPage.hr(450,true); |
|
|
} |
|
|
os->chdir(outputDirectory.c_str()); |
|
|
|
|
|
//do scout award here. |
|
|
CSurvivalistAward csrva; csrva.report(MatchResultsPage); |
|
|
|
|
|
//sniper award |
|
|
CSharpshooterAward cssa; cssa.report(MatchResultsPage); |
|
|
|
|
|
//soldier award |
|
|
CRocketryAward cra; cra.report(MatchResultsPage); |
|
|
|
|
|
//demoman awards |
|
|
CGrenadierAward cga; cga.report(MatchResultsPage); |
|
|
CDemolitionsAward cda; cda.report(MatchResultsPage); |
|
|
|
|
|
//medic awards |
|
|
CCureAward cca;cca.report(MatchResultsPage); |
|
|
CBiologicalWarfareAward cbwa; cbwa.report(MatchResultsPage); |
|
|
|
|
|
//HW award |
|
|
CAssaultCannonAward caca;caca.report(MatchResultsPage); |
|
|
|
|
|
//pyro award |
|
|
CFlamethrowerAward cfa;cfa.report(MatchResultsPage); |
|
|
|
|
|
//spy award |
|
|
CKnifeAward cka;cka.report(MatchResultsPage); |
|
|
|
|
|
//engineer awards |
|
|
CBestSentryAward cbsa; cbsa.report(MatchResultsPage); |
|
|
CSentryRebuildAward cba2;cba2.report(MatchResultsPage); |
|
|
|
|
|
|
|
|
MatchResultsPage.hr(450,true); |
|
|
|
|
|
//non class specific |
|
|
CKamikazeAward ckami;ckami.report(MatchResultsPage); |
|
|
CTalkativeAward cta;cta.report(MatchResultsPage); |
|
|
CTeamKillAward ctka; ctka.report(MatchResultsPage); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MatchResultsPage.enddiv(); |
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::DoMatchResults |
|
|
// Purpose: This creates the MatchResults page of the report (the main page) |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::DoMatchResults() |
|
|
{ |
|
|
int chresult=os->chdir(g_pApp->outputDirectory.c_str()); |
|
|
CHTMLFile MatchResultsPage("MatchResults.html","results"); |
|
|
CMatchResults cmr; |
|
|
cmr.report(MatchResultsPage); |
|
|
DoAwards(MatchResultsPage); |
|
|
|
|
|
CScoreboard cs; |
|
|
cs.report(MatchResultsPage); |
|
|
|
|
|
|
|
|
CWhoKilledWho cwkw; cwkw.report(MatchResultsPage); |
|
|
|
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::parseCmdLineArg |
|
|
// Purpose: This parses variables and values out of passed in commandline args |
|
|
// Input: in - the full command line argument |
|
|
// var - the output buffer in which to place the name of the variable |
|
|
// val - the output buffer in which to place the value of the variable |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::parseCmdLineArg(const char* in, char* var, char* val) |
|
|
{ |
|
|
char* pEq=strchr(in,'='); |
|
|
if (!pEq || pEq==in) |
|
|
{ |
|
|
var[0]=val[0]=0; |
|
|
} |
|
|
else |
|
|
{ |
|
|
*pEq=0; |
|
|
strcpy(var,in); |
|
|
strcpy(val,pEq+1); |
|
|
Util::str2lowercase(var,var); |
|
|
Util::str2lowercase(val,val); |
|
|
*pEq='='; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::ParseCommandLine |
|
|
// Purpose: this parses the commandline into the cmdLineSwitches map. Also |
|
|
// recognizes certain switches and sets variables and creates directories |
|
|
// accordingly to their values |
|
|
// Input: argc - count of arguments from commandline |
|
|
// argv[] - the arguments |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::ParseCommandLine(int argc,const char* argv[]) |
|
|
{ |
|
|
char var[100]; |
|
|
char val[100]; |
|
|
|
|
|
//first find if we want to display startup info |
|
|
bool displayStartupInfo=false; |
|
|
for(int i=2;i<argc;i++) |
|
|
{ |
|
|
parseCmdLineArg(argv[i],var,val); |
|
|
if (!var[0]) |
|
|
fatalError("Malformed switch, required format is <variable>=<value> with no spaces"); |
|
|
|
|
|
cmdLineSwitches[var]=val; |
|
|
if (stricmp(var,"displaystartupinfo")==0) |
|
|
if (stricmp(val,"yes")==0) |
|
|
{ |
|
|
displayStartupInfo=true; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (displayStartupInfo) |
|
|
{ |
|
|
printf("%21s %-21s\n","Command line","Switches"); |
|
|
printf("-------------------------------------------\n"); |
|
|
} |
|
|
for(i=2;i<argc;i++) |
|
|
{ |
|
|
parseCmdLineArg(argv[i],var,val); |
|
|
if (!var[0]) |
|
|
fatalError("Malformed switch, required format is <variable>=<value> with no spaces"); |
|
|
|
|
|
if (displayStartupInfo) |
|
|
printf("%20s = %-20s\n",var,val); |
|
|
cmdLineSwitches[var]=val; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
outputDirectory=os->removeDirSlash(cmdLineSwitches["outputdir"]); |
|
|
makeAndSaveDirectory(outputDirectory); |
|
|
|
|
|
ruleDirectory=os->removeDirSlash(cmdLineSwitches["ruledir"]); |
|
|
makeAndSaveDirectory(ruleDirectory); |
|
|
|
|
|
if (cmdLineSwitches["eliminateoldplayers"]=="yes") |
|
|
{ |
|
|
eliminateOldPlayers=true; |
|
|
elimDays=atoi(cmdLineSwitches["oldplayercutoff"].c_str()); |
|
|
} |
|
|
else |
|
|
{ |
|
|
eliminateOldPlayers=false; |
|
|
elimDays=0; |
|
|
} |
|
|
|
|
|
|
|
|
if (cmdLineSwitches["usesupportdir"]=="yes") |
|
|
{ |
|
|
supportDirectory=os->removeDirSlash(cmdLineSwitches["supportdir"]); |
|
|
supportHTTPPath=os->removeDirSlash(cmdLineSwitches["supporthttppath"]); |
|
|
} |
|
|
else |
|
|
{ |
|
|
supportDirectory=g_pApp->outputDirectory+g_pApp->os->pathSeperator(); |
|
|
supportDirectory+="support"; |
|
|
supportHTTPPath="support"; |
|
|
} |
|
|
makeAndSaveDirectory(supportDirectory); |
|
|
|
|
|
|
|
|
if (cmdLineSwitches["persistplayerstats"]=="yes") |
|
|
{ |
|
|
playerDirectory=os->removeDirSlash(cmdLineSwitches["playerdir"]); |
|
|
playerHTTPPath=os->removeDirSlash(cmdLineSwitches["playerhttppath"]); |
|
|
} |
|
|
else |
|
|
{ |
|
|
playerDirectory=g_pApp->outputDirectory+g_pApp->os->pathSeperator(); |
|
|
playerDirectory+="players"; |
|
|
playerHTTPPath="players"; |
|
|
} |
|
|
makeAndSaveDirectory(playerDirectory); |
|
|
makeDirectory(playerDirectory+"support"); |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::main |
|
|
// Purpose: The REAL main of the program |
|
|
// Input: argc - count of arguments from commandline |
|
|
// argv[] - the arguments |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::main(int argc,const char* argv[]) |
|
|
{ |
|
|
if (argc<=1){printUsage();return;} |
|
|
g_pApp=this; |
|
|
|
|
|
//TODO: move this into OS interface |
|
|
TFStats_setNewHandler(); |
|
|
|
|
|
inputDirectory="."; |
|
|
makeAndSaveDirectory(inputDirectory); |
|
|
|
|
|
//this call also sets up various directories to be used |
|
|
ParseCommandLine(argc,argv); |
|
|
|
|
|
Util::initFriendlyWeaponNameTable(); |
|
|
|
|
|
//LogFile is read in here. |
|
|
//MUST do this before we chdir to the output directory |
|
|
os->chdir(inputDirectory.c_str()); |
|
|
CEventList* plogfile=NULL; |
|
|
plogfile=CEventList::readEventList(argv[1]); |
|
|
if (!plogfile) |
|
|
fatalError("No valid data found in logfile %s\n",argv[1]); |
|
|
|
|
|
|
|
|
os->chdir(outputDirectory.c_str()); |
|
|
//make match information object |
|
|
g_pMatchInfo= new CMatchInfo(plogfile); |
|
|
|
|
|
CTFStatsReport matchreport; |
|
|
|
|
|
|
|
|
|
|
|
matchreport.genImages(); |
|
|
|
|
|
|
|
|
matchreport.genJavaScript(); |
|
|
matchreport.genStyleSheet(); |
|
|
|
|
|
if (cmdLineSwitches["persistplayerstats"]=="yes") |
|
|
matchreport.genAllPlayersStyleSheet(); |
|
|
|
|
|
matchreport.genIndex(); |
|
|
|
|
|
os->chdir(outputDirectory.c_str()); |
|
|
matchreport.genTopFrame(); |
|
|
matchreport.genNavFrame(); |
|
|
|
|
|
|
|
|
DoMatchResults(); |
|
|
|
|
|
|
|
|
CCVarList ccvl; |
|
|
ccvl.makeHTMLPage("cvars.html","Server Settings"); |
|
|
|
|
|
CDialogueReadout cdr; |
|
|
cdr.makeHTMLPage("dialogue.html","Dialogue"); |
|
|
|
|
|
CPlayerSpecifics cps; |
|
|
cps.makeHTMLPage("players.html","Players"); |
|
|
|
|
|
|
|
|
if (cmdLineSwitches["persistplayerstats"]=="yes") |
|
|
{ |
|
|
if(!g_pMatchInfo->isLanGame()) |
|
|
{ |
|
|
g_pApp->os->chdir(g_pApp->playerDirectory.c_str()); |
|
|
CAllPlayersStats caps; |
|
|
caps.makeHTMLPage("allplayers.html","All Players"); |
|
|
} |
|
|
else |
|
|
{ |
|
|
g_pApp->warning("Lan Game detected, player stats will not be saved"); |
|
|
} |
|
|
} |
|
|
printf("TFStats completed successfully\n\n\n"); |
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::makeAndSaveDirectory |
|
|
// Purpose: Creates a directory and writes the full directory path back into the |
|
|
// passed in string. In effect, this turns a potentially relative path into an |
|
|
// absolute path, and ensures the directory exists. |
|
|
// Input: dir - the string that contains the pathname, and upon return will contain |
|
|
// the absolute pathname |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::makeAndSaveDirectory(string& dir) |
|
|
{ |
|
|
char startpath[500]; |
|
|
char fullpath[500]; |
|
|
os->getcwd(startpath,500); |
|
|
if (!os->makeHier(dir)) |
|
|
{ |
|
|
fatalError("Failed to make directory \"%s\", aborting. (Reason: %s)",dir.c_str(),strerror(errno)); |
|
|
} |
|
|
os->chdir(dir.c_str()); |
|
|
os->getcwd(fullpath,500); |
|
|
dir=fullpath; |
|
|
os->addDirSlash(dir); |
|
|
os->chdir(startpath); |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::makeDirectory |
|
|
// Purpose: Creates a directory. |
|
|
// Input: dir - the directory to create |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::makeDirectory(string& dir) |
|
|
{ |
|
|
char startpath[500]; |
|
|
os->getcwd(startpath,500); |
|
|
|
|
|
if (!os->makeHier(dir)) |
|
|
{ |
|
|
fatalError("Failed to make directory \"%s\". (Reason: %s)",dir.c_str(),strerror(errno)); |
|
|
} |
|
|
os->chdir(startpath); |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::fatalError |
|
|
// Purpose: prints an error message and exits |
|
|
// Input: fmt - the format string |
|
|
// ... - optional arguments |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::fatalError(char* fmt,...) |
|
|
{ |
|
|
va_list v; |
|
|
va_start(v,fmt); |
|
|
fprintf(stderr,"Fatal Error: "); |
|
|
vfprintf(stderr,fmt,v); |
|
|
fprintf(stderr,"\n***Aborting. \n"); |
|
|
exit(-1); |
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::warning |
|
|
// Purpose: prints a warning message and returns. (program doesn't exit) |
|
|
// Input: fmt - format string |
|
|
// ... - optional arguments |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
void CTFStatsApplication::warning(char* fmt,...) |
|
|
{ |
|
|
va_list v; |
|
|
va_start(v,fmt); |
|
|
fprintf(stderr,"Warning: "); |
|
|
vfprintf(stderr,fmt,v); |
|
|
fprintf(stderr,"\n"); |
|
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
// Function: CTFStatsApplication::getCutoffSeconds |
|
|
// Purpose: Turns the number of cutoff days into seconds. Cutoff days are how |
|
|
// many days players can be absent from the server and still have their stats |
|
|
// reported in the all-player stats |
|
|
// Output: time_t the number of cutoff seconds |
|
|
//------------------------------------------------------------------------------------------------------ |
|
|
time_t CTFStatsApplication::getCutoffSeconds() |
|
|
{ |
|
|
return 60*60*24*elimDays; |
|
|
} |