Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #pragma warning (disable:4786)
  9. //=========== (C) Copyright 1999 Valve, L.L.C. All rights reserved. ===========
  10. //
  11. // The copyright to the contents herein is the property of Valve, L.L.C.
  12. // The contents may be used and/or copied only with the written permission of
  13. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  14. // the agreement/contract under which the contents have been supplied.
  15. //
  16. // Purpose: Implementation of the TFStatsApplication class.
  17. //
  18. // $Workfile: $
  19. // $Date: $
  20. //
  21. //------------------------------------------------------------------------------------------------------
  22. // $Log: $
  23. //
  24. // $NoKeywords: $
  25. //=============================================================================
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <stdarg.h>
  29. #include "TFStatsApplication.h"
  30. #include "TFStatsReport.h"
  31. #include "LogEvent.h"
  32. #include "ScoreBoard.h"
  33. #include "WhoKilledWho.h"
  34. #include "memdbg.h"
  35. #include "awards.h"
  36. #include "MatchResults.h"
  37. #include "DialogueReadout.h"
  38. #include "cvars.h"
  39. #include "html.h"
  40. #include "TextFile.h"
  41. #include "CustomAward.h"
  42. #include "PlayerSpecifics.h"
  43. #include "util.h"
  44. #include "AllPlayersStats.h"
  45. #include <errno.h>
  46. CTFStatsApplication* g_pApp; //global!
  47. //------------------------------------------------------------------------------------------------------
  48. // Function: CTFStatsApplication::printUsage
  49. // Purpose: Prints out how to use the program
  50. //------------------------------------------------------------------------------------------------------
  51. void CTFStatsApplication::printUsage()
  52. {
  53. printf("TFStats version %li.%li\n",majorVer,minorVer);
  54. printf("\nUSAGE:\n");
  55. #ifdef WIN32
  56. printf("TFstatsRT.exe <log file name> <optional switches>\n");
  57. #else
  58. printf("TFstats_l <log file name> <optional switches>\n");
  59. #endif
  60. printf("the available switches are detailed in the documentation. (TFStats.htm): \n");
  61. printf("---\n");
  62. printf("After TFStats is done, several new HTML files will be in the current directory,\n");
  63. printf("point your browser to the index.html in this directory and it will do the rest.\n");
  64. #ifdef WIN32
  65. printf("(you should just be able to do \"start index.html\")\n");
  66. #endif
  67. printf("TFStats uses Regex++:\nRegex++ � 1998-9 Dr John Maddock.\n");
  68. }
  69. #include "CustomAwardList.h"
  70. //------------------------------------------------------------------------------------------------------
  71. // Function: CTFStatsApplication::DoAwards
  72. // Purpose: This reports all of the awards
  73. // Input: MatchResultsPage - the page on which to write the awards' HTML
  74. //------------------------------------------------------------------------------------------------------
  75. void CTFStatsApplication::DoAwards(CHTMLFile& MatchResultsPage)
  76. {
  77. int chresult=os->chdir(g_pApp->outputDirectory.c_str());
  78. MatchResultsPage.p();
  79. MatchResultsPage.write("<img src=\"%s/awards.gif\">\n",g_pApp->supportHTTPPath.c_str());
  80. MatchResultsPage.div("awards");
  81. //do custom awards first, they're usually the important ones for the match
  82. CCustomAwardList* pCust;
  83. os->chdir(ruleDirectory.c_str());
  84. pCust=CCustomAwardList::readCustomAwards(g_pMatchInfo->mapName());
  85. os->chdir(outputDirectory.c_str());
  86. if(pCust)
  87. {
  88. CCustomAwardIterator it;
  89. for (it=pCust->begin();it!=pCust->end();++it)
  90. {
  91. (*it)->report(MatchResultsPage);
  92. }
  93. delete pCust;
  94. MatchResultsPage.hr(450,true);
  95. }
  96. os->chdir(outputDirectory.c_str());
  97. //do scout award here.
  98. CSurvivalistAward csrva; csrva.report(MatchResultsPage);
  99. //sniper award
  100. CSharpshooterAward cssa; cssa.report(MatchResultsPage);
  101. //soldier award
  102. CRocketryAward cra; cra.report(MatchResultsPage);
  103. //demoman awards
  104. CGrenadierAward cga; cga.report(MatchResultsPage);
  105. CDemolitionsAward cda; cda.report(MatchResultsPage);
  106. //medic awards
  107. CCureAward cca;cca.report(MatchResultsPage);
  108. CBiologicalWarfareAward cbwa; cbwa.report(MatchResultsPage);
  109. //HW award
  110. CAssaultCannonAward caca;caca.report(MatchResultsPage);
  111. //pyro award
  112. CFlamethrowerAward cfa;cfa.report(MatchResultsPage);
  113. //spy award
  114. CKnifeAward cka;cka.report(MatchResultsPage);
  115. //engineer awards
  116. CBestSentryAward cbsa; cbsa.report(MatchResultsPage);
  117. CSentryRebuildAward cba2;cba2.report(MatchResultsPage);
  118. MatchResultsPage.hr(450,true);
  119. //non class specific
  120. CKamikazeAward ckami;ckami.report(MatchResultsPage);
  121. CTalkativeAward cta;cta.report(MatchResultsPage);
  122. CTeamKillAward ctka; ctka.report(MatchResultsPage);
  123. MatchResultsPage.enddiv();
  124. }
  125. //------------------------------------------------------------------------------------------------------
  126. // Function: CTFStatsApplication::DoMatchResults
  127. // Purpose: This creates the MatchResults page of the report (the main page)
  128. //------------------------------------------------------------------------------------------------------
  129. void CTFStatsApplication::DoMatchResults()
  130. {
  131. int chresult=os->chdir(g_pApp->outputDirectory.c_str());
  132. CHTMLFile MatchResultsPage("MatchResults.html","results");
  133. CMatchResults cmr;
  134. cmr.report(MatchResultsPage);
  135. DoAwards(MatchResultsPage);
  136. CScoreboard cs;
  137. cs.report(MatchResultsPage);
  138. CWhoKilledWho cwkw; cwkw.report(MatchResultsPage);
  139. }
  140. //------------------------------------------------------------------------------------------------------
  141. // Function: CTFStatsApplication::parseCmdLineArg
  142. // Purpose: This parses variables and values out of passed in commandline args
  143. // Input: in - the full command line argument
  144. // var - the output buffer in which to place the name of the variable
  145. // val - the output buffer in which to place the value of the variable
  146. //------------------------------------------------------------------------------------------------------
  147. void CTFStatsApplication::parseCmdLineArg(const char* in, char* var, char* val)
  148. {
  149. char* pEq=strchr(in,'=');
  150. if (!pEq || pEq==in)
  151. {
  152. var[0]=val[0]=0;
  153. }
  154. else
  155. {
  156. *pEq=0;
  157. strcpy(var,in);
  158. strcpy(val,pEq+1);
  159. Util::str2lowercase(var,var);
  160. Util::str2lowercase(val,val);
  161. *pEq='=';
  162. }
  163. }
  164. //------------------------------------------------------------------------------------------------------
  165. // Function: CTFStatsApplication::ParseCommandLine
  166. // Purpose: this parses the commandline into the cmdLineSwitches map. Also
  167. // recognizes certain switches and sets variables and creates directories
  168. // accordingly to their values
  169. // Input: argc - count of arguments from commandline
  170. // argv[] - the arguments
  171. //------------------------------------------------------------------------------------------------------
  172. void CTFStatsApplication::ParseCommandLine(int argc,const char* argv[])
  173. {
  174. char var[100];
  175. char val[100];
  176. //first find if we want to display startup info
  177. bool displayStartupInfo=false;
  178. for(int i=2;i<argc;i++)
  179. {
  180. parseCmdLineArg(argv[i],var,val);
  181. if (!var[0])
  182. fatalError("Malformed switch, required format is <variable>=<value> with no spaces");
  183. cmdLineSwitches[var]=val;
  184. if (stricmp(var,"displaystartupinfo")==0)
  185. if (stricmp(val,"yes")==0)
  186. {
  187. displayStartupInfo=true;
  188. break;
  189. }
  190. }
  191. if (displayStartupInfo)
  192. {
  193. printf("%21s %-21s\n","Command line","Switches");
  194. printf("-------------------------------------------\n");
  195. }
  196. for(i=2;i<argc;i++)
  197. {
  198. parseCmdLineArg(argv[i],var,val);
  199. if (!var[0])
  200. fatalError("Malformed switch, required format is <variable>=<value> with no spaces");
  201. if (displayStartupInfo)
  202. printf("%20s = %-20s\n",var,val);
  203. cmdLineSwitches[var]=val;
  204. }
  205. outputDirectory=os->removeDirSlash(cmdLineSwitches["outputdir"]);
  206. makeAndSaveDirectory(outputDirectory);
  207. ruleDirectory=os->removeDirSlash(cmdLineSwitches["ruledir"]);
  208. makeAndSaveDirectory(ruleDirectory);
  209. if (cmdLineSwitches["eliminateoldplayers"]=="yes")
  210. {
  211. eliminateOldPlayers=true;
  212. elimDays=atoi(cmdLineSwitches["oldplayercutoff"].c_str());
  213. }
  214. else
  215. {
  216. eliminateOldPlayers=false;
  217. elimDays=0;
  218. }
  219. if (cmdLineSwitches["usesupportdir"]=="yes")
  220. {
  221. supportDirectory=os->removeDirSlash(cmdLineSwitches["supportdir"]);
  222. supportHTTPPath=os->removeDirSlash(cmdLineSwitches["supporthttppath"]);
  223. }
  224. else
  225. {
  226. supportDirectory=g_pApp->outputDirectory+g_pApp->os->pathSeperator();
  227. supportDirectory+="support";
  228. supportHTTPPath="support";
  229. }
  230. makeAndSaveDirectory(supportDirectory);
  231. if (cmdLineSwitches["persistplayerstats"]=="yes")
  232. {
  233. playerDirectory=os->removeDirSlash(cmdLineSwitches["playerdir"]);
  234. playerHTTPPath=os->removeDirSlash(cmdLineSwitches["playerhttppath"]);
  235. }
  236. else
  237. {
  238. playerDirectory=g_pApp->outputDirectory+g_pApp->os->pathSeperator();
  239. playerDirectory+="players";
  240. playerHTTPPath="players";
  241. }
  242. makeAndSaveDirectory(playerDirectory);
  243. makeDirectory(playerDirectory+"support");
  244. }
  245. //------------------------------------------------------------------------------------------------------
  246. // Function: CTFStatsApplication::main
  247. // Purpose: The REAL main of the program
  248. // Input: argc - count of arguments from commandline
  249. // argv[] - the arguments
  250. //------------------------------------------------------------------------------------------------------
  251. void CTFStatsApplication::main(int argc,const char* argv[])
  252. {
  253. if (argc<=1){printUsage();return;}
  254. g_pApp=this;
  255. //TODO: move this into OS interface
  256. TFStats_setNewHandler();
  257. inputDirectory=".";
  258. makeAndSaveDirectory(inputDirectory);
  259. //this call also sets up various directories to be used
  260. ParseCommandLine(argc,argv);
  261. Util::initFriendlyWeaponNameTable();
  262. //LogFile is read in here.
  263. //MUST do this before we chdir to the output directory
  264. os->chdir(inputDirectory.c_str());
  265. CEventList* plogfile=NULL;
  266. plogfile=CEventList::readEventList(argv[1]);
  267. if (!plogfile)
  268. fatalError("No valid data found in logfile %s\n",argv[1]);
  269. os->chdir(outputDirectory.c_str());
  270. //make match information object
  271. g_pMatchInfo= new CMatchInfo(plogfile);
  272. CTFStatsReport matchreport;
  273. matchreport.genImages();
  274. matchreport.genJavaScript();
  275. matchreport.genStyleSheet();
  276. if (cmdLineSwitches["persistplayerstats"]=="yes")
  277. matchreport.genAllPlayersStyleSheet();
  278. matchreport.genIndex();
  279. os->chdir(outputDirectory.c_str());
  280. matchreport.genTopFrame();
  281. matchreport.genNavFrame();
  282. DoMatchResults();
  283. CCVarList ccvl;
  284. ccvl.makeHTMLPage("cvars.html","Server Settings");
  285. CDialogueReadout cdr;
  286. cdr.makeHTMLPage("dialogue.html","Dialogue");
  287. CPlayerSpecifics cps;
  288. cps.makeHTMLPage("players.html","Players");
  289. if (cmdLineSwitches["persistplayerstats"]=="yes")
  290. {
  291. if(!g_pMatchInfo->isLanGame())
  292. {
  293. g_pApp->os->chdir(g_pApp->playerDirectory.c_str());
  294. CAllPlayersStats caps;
  295. caps.makeHTMLPage("allplayers.html","All Players");
  296. }
  297. else
  298. {
  299. g_pApp->warning("Lan Game detected, player stats will not be saved");
  300. }
  301. }
  302. printf("TFStats completed successfully\n\n\n");
  303. }
  304. //------------------------------------------------------------------------------------------------------
  305. // Function: CTFStatsApplication::makeAndSaveDirectory
  306. // Purpose: Creates a directory and writes the full directory path back into the
  307. // passed in string. In effect, this turns a potentially relative path into an
  308. // absolute path, and ensures the directory exists.
  309. // Input: dir - the string that contains the pathname, and upon return will contain
  310. // the absolute pathname
  311. //------------------------------------------------------------------------------------------------------
  312. void CTFStatsApplication::makeAndSaveDirectory(string& dir)
  313. {
  314. char startpath[500];
  315. char fullpath[500];
  316. os->getcwd(startpath,500);
  317. if (!os->makeHier(dir))
  318. {
  319. fatalError("Failed to make directory \"%s\", aborting. (Reason: %s)",dir.c_str(),strerror(errno));
  320. }
  321. os->chdir(dir.c_str());
  322. os->getcwd(fullpath,500);
  323. dir=fullpath;
  324. os->addDirSlash(dir);
  325. os->chdir(startpath);
  326. }
  327. //------------------------------------------------------------------------------------------------------
  328. // Function: CTFStatsApplication::makeDirectory
  329. // Purpose: Creates a directory.
  330. // Input: dir - the directory to create
  331. //------------------------------------------------------------------------------------------------------
  332. void CTFStatsApplication::makeDirectory(string& dir)
  333. {
  334. char startpath[500];
  335. os->getcwd(startpath,500);
  336. if (!os->makeHier(dir))
  337. {
  338. fatalError("Failed to make directory \"%s\". (Reason: %s)",dir.c_str(),strerror(errno));
  339. }
  340. os->chdir(startpath);
  341. }
  342. //------------------------------------------------------------------------------------------------------
  343. // Function: CTFStatsApplication::fatalError
  344. // Purpose: prints an error message and exits
  345. // Input: fmt - the format string
  346. // ... - optional arguments
  347. //------------------------------------------------------------------------------------------------------
  348. void CTFStatsApplication::fatalError(char* fmt,...)
  349. {
  350. va_list v;
  351. va_start(v,fmt);
  352. fprintf(stderr,"Fatal Error: ");
  353. vfprintf(stderr,fmt,v);
  354. fprintf(stderr,"\n***Aborting. \n");
  355. exit(-1);
  356. }
  357. //------------------------------------------------------------------------------------------------------
  358. // Function: CTFStatsApplication::warning
  359. // Purpose: prints a warning message and returns. (program doesn't exit)
  360. // Input: fmt - format string
  361. // ... - optional arguments
  362. //------------------------------------------------------------------------------------------------------
  363. void CTFStatsApplication::warning(char* fmt,...)
  364. {
  365. va_list v;
  366. va_start(v,fmt);
  367. fprintf(stderr,"Warning: ");
  368. vfprintf(stderr,fmt,v);
  369. fprintf(stderr,"\n");
  370. }
  371. //------------------------------------------------------------------------------------------------------
  372. // Function: CTFStatsApplication::getCutoffSeconds
  373. // Purpose: Turns the number of cutoff days into seconds. Cutoff days are how
  374. // many days players can be absent from the server and still have their stats
  375. // reported in the all-player stats
  376. // Output: time_t the number of cutoff seconds
  377. //------------------------------------------------------------------------------------------------------
  378. time_t CTFStatsApplication::getCutoffSeconds()
  379. {
  380. return 60*60*24*elimDays;
  381. }