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.

460 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implemenatation of CMatchInfo
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //------------------------------------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //=============================================================================//
  13. #include "MatchInfo.h"
  14. CMatchInfo* g_pMatchInfo=NULL; //global information about the match.
  15. //------------------------------------------------------------------------------------------------------
  16. // Function: CMatchInfo::generate
  17. // Purpose: generates the match info structure from the log file
  18. //------------------------------------------------------------------------------------------------------
  19. void CMatchInfo::generate()
  20. {
  21. if (plogfile->empty())
  22. g_pApp->fatalError("No data in log file!\nPlease ensure that you are running TFstats on a valid log file!");
  23. CEventListIterator it=plogfile->begin();
  24. logopentime=(*it)->getTime();
  25. for (it;it!=plogfile->end();++it)
  26. {
  27. const CLogEvent* curr=(*it);
  28. switch(curr->getType())
  29. {
  30. case CLogEvent::CONNECT:
  31. {
  32. int sid=curr->getArgument(0)->asPlayerGetSvrPID();
  33. unsigned long WONid=curr->getArgument(0)->asPlayerGetWONID();
  34. string plrName=curr->getArgument(0)->asPlayerGetName();
  35. string ipAddress=curr->getArgument(1)->getStringValue();
  36. PID pid;
  37. PID foundpid=-1;
  38. if (WONid!=-1)
  39. {
  40. bLanGame=false;
  41. pid=pidMap[sid]=WONid;
  42. }
  43. else
  44. {
  45. bLanGame=true;
  46. CPlayerList::iterator it=players.begin();
  47. for (it;it!=players.end();++it)
  48. {
  49. PID currpid=it->first;
  50. CPlayer& cp=it->second;
  51. if (cp.ipAddress==ipAddress)
  52. {
  53. foundpid=currpid;
  54. break;
  55. }
  56. }
  57. if (it==players.end()) //if no ip addresses matched match by name
  58. {
  59. it = players.begin();
  60. for (it;it!=players.end();++it)
  61. {
  62. PID currpid=it->first;
  63. CPlayer& cp=it->second;
  64. if (cp.aliases.contains(plrName))
  65. {
  66. foundpid=currpid;
  67. break;
  68. }
  69. }
  70. }
  71. }
  72. if (foundpid != -1)
  73. {
  74. pid=pidMap[sid]=foundpid;
  75. }
  76. else
  77. {
  78. pid=pidMap[sid]=sid;
  79. }
  80. //printf("Checkpoint %lu\n",__LINE__);
  81. //printf("pid=%lu\n",pid);
  82. if (players[pid].pid==-1)
  83. players[pid].pid=pid;
  84. players[pid].ipAddress=ipAddress;
  85. players[pid].svrPID=sid;
  86. players[pid].WONID=WONid;
  87. //keep the pseudonym list updated
  88. players[pid].nameFound(curr->getTime(),plrName);
  89. }
  90. break;
  91. case CLogEvent::ENTER_GAME:
  92. {
  93. int sid=curr->getArgument(0)->asPlayerGetSvrPID();
  94. //PID pid=curr->getArgument(0)->asPlayerGetFullPID();
  95. unsigned long WONid=curr->getArgument(0)->asPlayerGetWONID();
  96. PID pid;
  97. if (WONid!=-1)
  98. {
  99. bLanGame=false;
  100. pid=pidMap[sid]=WONid;
  101. }
  102. else
  103. {
  104. bLanGame=true;
  105. //they may have matched based on IP or name.
  106. //so check if the player structure pointed to by
  107. //the sid is valid, if so, don't reassign pid
  108. pid=pidMap[sid];
  109. if (players[pid].ipAddress=="")
  110. pid=pidMap[sid]=sid;
  111. }
  112. players[pid].svrPID=sid;
  113. players[pid].WONID=WONid;
  114. players[pid].pid=pid;
  115. string nm=curr->getArgument(0)->asPlayerGetName();
  116. //keep the pseudonym list updated
  117. players[pid].nameFound(curr->getTime(),nm);
  118. }
  119. break;
  120. case CLogEvent::CLASS_CHANGE:
  121. {
  122. PID pid=curr->getArgument(0)->asPlayerGetPID();
  123. time_t changetime=curr->getTime();
  124. player_class newpc=playerClassNameToClassID(curr->getArgument(1)->getStringValue());
  125. //keep the pseudonym list updated
  126. players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  127. string plrname=curr->getArgument(0)->asPlayerGetName();
  128. players[pid].allclassesplayed.add(changetime,newpc);
  129. int currTeam=players[pid].teams.atTime(changetime);
  130. players[pid].perteam[currTeam].classesplayed.add(changetime,newpc);
  131. }
  132. break;
  133. case CLogEvent::NAME_CHANGE:
  134. {
  135. //keep the pseudonym list updated
  136. players[curr->getArgument(0)->asPlayerGetPID()].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
  137. }
  138. break;
  139. case CLogEvent::SUICIDE:
  140. {
  141. PID pid=(*it)->getArgument(0)->asPlayerGetPID();
  142. int team=players[pid].teams.atTime((*it)->getTime());
  143. // players[pid].perteam[team].kills++;
  144. players[pid].perteam[team].deaths++;
  145. players[pid].perteam[team].suicides++;
  146. //keep the pseudonym list updated
  147. players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  148. }
  149. break;
  150. case CLogEvent::FRAG:
  151. case CLogEvent::TEAM_FRAG:
  152. {
  153. PID killerid=(*it)->getArgument(0)->asPlayerGetPID();
  154. PID killedid=(*it)->getArgument(1)->asPlayerGetPID();
  155. int killerTeam=players[killerid].teams.atTime((*it)->getTime());
  156. int killedTeam=players[killedid].teams.atTime((*it)->getTime());
  157. CPlayer& p1=players[killerid];
  158. CPlayer& p2=players[killedid];
  159. if (curr->getType() == CLogEvent::TEAM_FRAG)
  160. {
  161. players[killerid].perteam[killerTeam].teamkills++;
  162. players[killedid].perteam[killedTeam].teamkilled++;
  163. }
  164. else if (curr->getType() == CLogEvent::FRAG)
  165. {
  166. string weapName=(*it)->getArgument(2)->getStringValue();
  167. bool countKill=true;
  168. //gotta account for timer/infection double kills for medics!
  169. if (weapName=="infection")
  170. {
  171. //test to see if the previous event was a timer from the same player, and a kill, and with the timer.
  172. CEventListIterator it2=it;
  173. if ((--it2)!=plogfile->begin())
  174. {
  175. if ((*it2)->getType() == CLogEvent::FRAG)
  176. if ((*it2)->getArgument(2)->getStringValue()=="timer")
  177. if ((*it2)->getArgument(0)->asPlayerGetPID()==killerid)
  178. countKill=false;
  179. }
  180. }
  181. if (countKill)
  182. {
  183. players[killerid].perteam[killerTeam].weaponKills[weapName]++;
  184. players[killerid].perteam[killerTeam].kills++;
  185. players[killedid].perteam[killedTeam].deaths++;
  186. }
  187. }
  188. //keep the pseudonym list updated
  189. players[killerid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  190. players[killedid].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
  191. }
  192. break;
  193. case CLogEvent::TEAM_JOIN:
  194. {
  195. int team=curr->getArgument(1)->getFloatValue();
  196. team--; //teams are logged as 1-4. tfstats stores them as 0-3
  197. PID pid=curr->getArgument(0)->asPlayerGetPID();
  198. CPlayer& p=players[pid];
  199. team_exists[team]=true;
  200. int oldteam=team;
  201. if(p.teams.anythingAtTime(curr->getTime()-1))
  202. oldteam=p.teams.atTime(curr->getTime()-1);
  203. else //if this is the first team join, count them as in the game
  204. players[pid].logontime=curr->getTime();
  205. //keep the pseudonym list updated
  206. players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  207. players[pid].teams.add(curr->getTime(),team);
  208. if (p.allclassesplayed.anythingAtTime(curr->getTime()))
  209. {
  210. player_class plrcurrclass=players[pid].allclassesplayed.atTime(curr->getTime());
  211. players[pid].perteam[oldteam].classesplayed.cut(curr->getTime());
  212. players[pid].perteam[team].classesplayed.add(curr->getTime(),plrcurrclass);
  213. }
  214. }
  215. break;
  216. case CLogEvent::TEAM_RENAME:
  217. {
  218. int teamid=curr->getArgument(0)->getFloatValue()-1;
  219. string tname=curr->getArgument(1)->getStringValue();
  220. teamnames[teamid]=tname;
  221. }
  222. break;
  223. case CLogEvent::SERVER_NAME:
  224. {
  225. servername=curr->getArgument(0)->getStringValue();
  226. }
  227. break;
  228. case CLogEvent::SERVER_SPAWN:
  229. {
  230. mapname=curr->getArgument(0)->getStringValue();
  231. }
  232. break;
  233. case CLogEvent::DISCONNECT:
  234. {
  235. PID pid=curr->getArgument(0)->asPlayerGetPID();
  236. players[pid].logofftime=curr->getTime();
  237. players[pid].allclassesplayed.endTime=curr->getTime();
  238. players[pid].allclassesplayed.cut(curr->getTime());
  239. players[pid].teams.cut(curr->getTime());
  240. players[pid].aliases.cut(curr->getTime());
  241. int currTeam=players[pid].teams.atTime(curr->getTime());
  242. players[pid].perteam[currTeam].classesplayed.cut(curr->getTime());
  243. //keep the pseudonym list updated
  244. if (pid!=-1) //sometimes disconnect messages have -1 for the pid
  245. players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  246. }
  247. break;
  248. case CLogEvent::NAMED_BROADCAST:
  249. {
  250. //keep the pseudonym list updated
  251. const CLogEventArgument* pArg=curr->getArgument(1);
  252. PID pid=pArg->asPlayerGetPID();
  253. players[pid].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
  254. }
  255. break;
  256. case CLogEvent::NAMED_GOAL_ACTIVATE:
  257. {
  258. //keep the pseudonym list updated
  259. players[curr->getArgument(0)->asPlayerGetPID()].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
  260. }
  261. break;
  262. case CLogEvent::LOG_CLOSED:
  263. {
  264. logclosetime=curr->getTime();
  265. }
  266. break;
  267. }
  268. #ifdef _DEBUG
  269. #ifdef _PARSEDEBUG
  270. printf("%s:\n",CLogEvent::TypeNames[(int)curr->getType()]);
  271. fflush(stdout);
  272. printf("\t%s\n",curr->m_StrippedText);
  273. fflush(stdout);
  274. for (int i=0;curr->getArgument(i);i++)
  275. {
  276. if (i==0)
  277. printf("\t\targs: ");
  278. fflush(stdout);
  279. printf("\"%s\" ",curr->getArgument(i)->getStringValue());
  280. }
  281. printf("\n");
  282. #endif
  283. #endif
  284. }
  285. if (logclosetime==0 && !plogfile->empty())
  286. {
  287. CEventListIterator it=plogfile->end();
  288. --it;
  289. logclosetime=(*it)->getTime();
  290. }
  291. map<PID,CPlayer>::iterator it2;
  292. for(it2=players.begin();it2!=players.end();++it2)
  293. {
  294. CPlayer& p=(*it2).second;
  295. if (p.aliases.endTime < logclosetime)
  296. p.aliases.endTime=logclosetime;
  297. p.name=p.aliases.favourite();
  298. if (p.allclassesplayed.endTime < logclosetime)
  299. p.allclassesplayed.endTime=logclosetime;
  300. if (p.teams.endTime < logclosetime)
  301. p.teams.endTime=logclosetime;
  302. for (int i=0;i<MAX_TEAMS;i++)
  303. {
  304. //if you have no kills you have to play on a team at least 30 seconds to be counted part of it
  305. //also give a one-suicide grace so they can killthemselves to get onto another team?
  306. if (p.teams.howLong(i) < 30 && p.perteam[i].kills==0)// && p.perteam[i].deaths < 1)
  307. p.teams.remove(i);
  308. CTimeIndexedList<player_class>* v= &p.perteam[i].classesplayed;
  309. p.perteam[i].classesplayed.endTime=logclosetime;
  310. time_t t=p.teams.howLong(i);
  311. p.perteam[i].timeon=t;
  312. }
  313. }
  314. }
  315. //------------------------------------------------------------------------------------------------------
  316. // Function: CMatchInfo::getPlayerID
  317. // Purpose: resolves a player name to that players PID
  318. // Input: name - the name
  319. // Output: PID the PID
  320. //------------------------------------------------------------------------------------------------------
  321. PID CMatchInfo::getPlayerID(string name)
  322. {
  323. CPlayerListIterator it;
  324. //ugh! O(n)
  325. for (it=playerBegin();it!=playerEnd();++it)
  326. {
  327. PID id=(*it).first;
  328. CPlayer curr=(*it).second;
  329. if (curr.name == name)
  330. return id;
  331. }
  332. return -1;
  333. }
  334. /*
  335. unsigned long CMatchInfo::getPlayerWONID(string name)
  336. {
  337. CPlayerListIterator it;
  338. //ugh! O(n)
  339. for (it=playerBegin();it!=playerEnd();++it)
  340. {
  341. CPlayer curr=(*it).second;
  342. if (curr.name == name)
  343. return curr.WONID;
  344. }
  345. return 0xffffffff;
  346. }
  347. */
  348. //------------------------------------------------------------------------------------------------------
  349. // Function: CMatchInfo::CMatchInfo
  350. // Purpose: Constructor
  351. // Input: plf - the log file
  352. // Output:
  353. //------------------------------------------------------------------------------------------------------
  354. CMatchInfo::CMatchInfo(CEventList* plf)
  355. :numPlrs(0),logclosetime(0),plogfile(plf)
  356. {
  357. teamnames[0]="Blue";
  358. teamnames[1]="Red";
  359. teamnames[2]="Yellow";
  360. teamnames[3]="Green";
  361. team_exists[0]=team_exists[1]=team_exists[2]=team_exists[3]=false;
  362. generate();
  363. }
  364. //------------------------------------------------------------------------------------------------------
  365. // Function: CMatchInfo::teamID
  366. // Purpose: resolves a team name to its ID
  367. // Input: teamname - the team name
  368. // Output: int the ID of the team
  369. //------------------------------------------------------------------------------------------------------
  370. int CMatchInfo::teamID(string teamname)
  371. {
  372. for (int i=0;i<MAX_TEAMS;i++)
  373. if (stricmp(teamname.c_str(),teamnames[i].c_str())==0)
  374. return i;
  375. return -1;
  376. }
  377. //------------------------------------------------------------------------------------------------------
  378. // Function: CMatchInfo::getTimeOn
  379. // Purpose: returns how long the specified player has been playing
  380. // Input: pid - the player being queried
  381. // Output: time_t the time he/she played
  382. //------------------------------------------------------------------------------------------------------
  383. time_t CMatchInfo::getTimeOn(PID pid)
  384. {
  385. CPlayer& p=players[pid];
  386. if (p.logofftime==0)
  387. p.logofftime=logclosetime;
  388. time_t timeon=p.logofftime-p.logontime;
  389. return timeon;
  390. }