467 lines
14 KiB
C++
467 lines
14 KiB
C++
|
//========= 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++ <20> 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;
|
|||
|
}
|