summaryrefslogtreecommitdiff
path: root/utils/tfstats/matchinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/tfstats/matchinfo.cpp')
-rw-r--r--utils/tfstats/matchinfo.cpp460
1 files changed, 460 insertions, 0 deletions
diff --git a/utils/tfstats/matchinfo.cpp b/utils/tfstats/matchinfo.cpp
new file mode 100644
index 0000000..323e442
--- /dev/null
+++ b/utils/tfstats/matchinfo.cpp
@@ -0,0 +1,460 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implemenatation of CMatchInfo
+//
+// $Workfile: $
+// $Date: $
+//
+//------------------------------------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "MatchInfo.h"
+
+CMatchInfo* g_pMatchInfo=NULL; //global information about the match.
+
+//------------------------------------------------------------------------------------------------------
+// Function: CMatchInfo::generate
+// Purpose: generates the match info structure from the log file
+//------------------------------------------------------------------------------------------------------
+void CMatchInfo::generate()
+{
+ if (plogfile->empty())
+ g_pApp->fatalError("No data in log file!\nPlease ensure that you are running TFstats on a valid log file!");
+
+ CEventListIterator it=plogfile->begin();
+ logopentime=(*it)->getTime();
+ for (it;it!=plogfile->end();++it)
+ {
+ const CLogEvent* curr=(*it);
+ switch(curr->getType())
+ {
+ case CLogEvent::CONNECT:
+ {
+ int sid=curr->getArgument(0)->asPlayerGetSvrPID();
+ unsigned long WONid=curr->getArgument(0)->asPlayerGetWONID();
+ string plrName=curr->getArgument(0)->asPlayerGetName();
+ string ipAddress=curr->getArgument(1)->getStringValue();
+ PID pid;
+ PID foundpid=-1;
+ if (WONid!=-1)
+ {
+ bLanGame=false;
+ pid=pidMap[sid]=WONid;
+ }
+ else
+ {
+ bLanGame=true;
+
+ CPlayerList::iterator it=players.begin();
+ for (it;it!=players.end();++it)
+ {
+ PID currpid=it->first;
+ CPlayer& cp=it->second;
+ if (cp.ipAddress==ipAddress)
+ {
+ foundpid=currpid;
+ break;
+ }
+ }
+ if (it==players.end()) //if no ip addresses matched match by name
+ {
+ it = players.begin();
+ for (it;it!=players.end();++it)
+ {
+ PID currpid=it->first;
+ CPlayer& cp=it->second;
+ if (cp.aliases.contains(plrName))
+ {
+ foundpid=currpid;
+ break;
+ }
+ }
+ }
+ }
+ if (foundpid != -1)
+ {
+ pid=pidMap[sid]=foundpid;
+ }
+ else
+ {
+ pid=pidMap[sid]=sid;
+ }
+ //printf("Checkpoint %lu\n",__LINE__);
+ //printf("pid=%lu\n",pid);
+ if (players[pid].pid==-1)
+ players[pid].pid=pid;
+
+ players[pid].ipAddress=ipAddress;
+ players[pid].svrPID=sid;
+ players[pid].WONID=WONid;
+ //keep the pseudonym list updated
+ players[pid].nameFound(curr->getTime(),plrName);
+
+ }
+ break;
+ case CLogEvent::ENTER_GAME:
+ {
+ int sid=curr->getArgument(0)->asPlayerGetSvrPID();
+ //PID pid=curr->getArgument(0)->asPlayerGetFullPID();
+ unsigned long WONid=curr->getArgument(0)->asPlayerGetWONID();
+
+ PID pid;
+ if (WONid!=-1)
+ {
+ bLanGame=false;
+ pid=pidMap[sid]=WONid;
+ }
+ else
+ {
+ bLanGame=true;
+
+ //they may have matched based on IP or name.
+ //so check if the player structure pointed to by
+ //the sid is valid, if so, don't reassign pid
+ pid=pidMap[sid];
+ if (players[pid].ipAddress=="")
+ pid=pidMap[sid]=sid;
+ }
+
+ players[pid].svrPID=sid;
+ players[pid].WONID=WONid;
+ players[pid].pid=pid;
+ string nm=curr->getArgument(0)->asPlayerGetName();
+
+ //keep the pseudonym list updated
+ players[pid].nameFound(curr->getTime(),nm);
+ }
+ break;
+ case CLogEvent::CLASS_CHANGE:
+ {
+ PID pid=curr->getArgument(0)->asPlayerGetPID();
+ time_t changetime=curr->getTime();
+
+ player_class newpc=playerClassNameToClassID(curr->getArgument(1)->getStringValue());
+
+ //keep the pseudonym list updated
+ players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+ string plrname=curr->getArgument(0)->asPlayerGetName();
+
+ players[pid].allclassesplayed.add(changetime,newpc);
+
+ int currTeam=players[pid].teams.atTime(changetime);
+ players[pid].perteam[currTeam].classesplayed.add(changetime,newpc);
+
+ }
+ break;
+ case CLogEvent::NAME_CHANGE:
+ {
+ //keep the pseudonym list updated
+ players[curr->getArgument(0)->asPlayerGetPID()].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
+ }
+ break;
+ case CLogEvent::SUICIDE:
+ {
+ PID pid=(*it)->getArgument(0)->asPlayerGetPID();
+ int team=players[pid].teams.atTime((*it)->getTime());
+
+ // players[pid].perteam[team].kills++;
+ players[pid].perteam[team].deaths++;
+ players[pid].perteam[team].suicides++;
+
+ //keep the pseudonym list updated
+ players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+ }
+ break;
+ case CLogEvent::FRAG:
+ case CLogEvent::TEAM_FRAG:
+ {
+ PID killerid=(*it)->getArgument(0)->asPlayerGetPID();
+ PID killedid=(*it)->getArgument(1)->asPlayerGetPID();
+ int killerTeam=players[killerid].teams.atTime((*it)->getTime());
+ int killedTeam=players[killedid].teams.atTime((*it)->getTime());
+
+
+ CPlayer& p1=players[killerid];
+ CPlayer& p2=players[killedid];
+
+
+ if (curr->getType() == CLogEvent::TEAM_FRAG)
+ {
+ players[killerid].perteam[killerTeam].teamkills++;
+ players[killedid].perteam[killedTeam].teamkilled++;
+ }
+ else if (curr->getType() == CLogEvent::FRAG)
+ {
+ string weapName=(*it)->getArgument(2)->getStringValue();
+
+ bool countKill=true;
+
+ //gotta account for timer/infection double kills for medics!
+ if (weapName=="infection")
+ {
+ //test to see if the previous event was a timer from the same player, and a kill, and with the timer.
+ CEventListIterator it2=it;
+ if ((--it2)!=plogfile->begin())
+ {
+ if ((*it2)->getType() == CLogEvent::FRAG)
+ if ((*it2)->getArgument(2)->getStringValue()=="timer")
+ if ((*it2)->getArgument(0)->asPlayerGetPID()==killerid)
+ countKill=false;
+ }
+ }
+ if (countKill)
+ {
+
+ players[killerid].perteam[killerTeam].weaponKills[weapName]++;
+ players[killerid].perteam[killerTeam].kills++;
+ players[killedid].perteam[killedTeam].deaths++;
+ }
+ }
+
+ //keep the pseudonym list updated
+ players[killerid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+ players[killedid].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
+
+ }
+ break;
+ case CLogEvent::TEAM_JOIN:
+ {
+ int team=curr->getArgument(1)->getFloatValue();
+ team--; //teams are logged as 1-4. tfstats stores them as 0-3
+ PID pid=curr->getArgument(0)->asPlayerGetPID();
+
+
+ CPlayer& p=players[pid];
+ team_exists[team]=true;
+
+ int oldteam=team;
+ if(p.teams.anythingAtTime(curr->getTime()-1))
+ oldteam=p.teams.atTime(curr->getTime()-1);
+ else //if this is the first team join, count them as in the game
+ players[pid].logontime=curr->getTime();
+
+ //keep the pseudonym list updated
+ players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+
+ players[pid].teams.add(curr->getTime(),team);
+
+ if (p.allclassesplayed.anythingAtTime(curr->getTime()))
+ {
+ player_class plrcurrclass=players[pid].allclassesplayed.atTime(curr->getTime());
+ players[pid].perteam[oldteam].classesplayed.cut(curr->getTime());
+ players[pid].perteam[team].classesplayed.add(curr->getTime(),plrcurrclass);
+ }
+ }
+ break;
+
+ case CLogEvent::TEAM_RENAME:
+ {
+ int teamid=curr->getArgument(0)->getFloatValue()-1;
+ string tname=curr->getArgument(1)->getStringValue();
+ teamnames[teamid]=tname;
+ }
+ break;
+
+ case CLogEvent::SERVER_NAME:
+ {
+ servername=curr->getArgument(0)->getStringValue();
+ }
+ break;
+ case CLogEvent::SERVER_SPAWN:
+ {
+ mapname=curr->getArgument(0)->getStringValue();
+ }
+ break;
+ case CLogEvent::DISCONNECT:
+ {
+ PID pid=curr->getArgument(0)->asPlayerGetPID();
+ players[pid].logofftime=curr->getTime();
+ players[pid].allclassesplayed.endTime=curr->getTime();
+ players[pid].allclassesplayed.cut(curr->getTime());
+ players[pid].teams.cut(curr->getTime());
+ players[pid].aliases.cut(curr->getTime());
+
+ int currTeam=players[pid].teams.atTime(curr->getTime());
+ players[pid].perteam[currTeam].classesplayed.cut(curr->getTime());
+
+ //keep the pseudonym list updated
+ if (pid!=-1) //sometimes disconnect messages have -1 for the pid
+ players[pid].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+
+ }
+ break;
+ case CLogEvent::NAMED_BROADCAST:
+ {
+ //keep the pseudonym list updated
+ const CLogEventArgument* pArg=curr->getArgument(1);
+ PID pid=pArg->asPlayerGetPID();
+ players[pid].nameFound(curr->getTime(),curr->getArgument(1)->asPlayerGetName());
+ }
+ break;
+ case CLogEvent::NAMED_GOAL_ACTIVATE:
+ {
+ //keep the pseudonym list updated
+ players[curr->getArgument(0)->asPlayerGetPID()].nameFound(curr->getTime(),curr->getArgument(0)->asPlayerGetName());
+ }
+ break;
+ case CLogEvent::LOG_CLOSED:
+ {
+ logclosetime=curr->getTime();
+ }
+ break;
+ }
+
+#ifdef _DEBUG
+#ifdef _PARSEDEBUG
+
+ printf("%s:\n",CLogEvent::TypeNames[(int)curr->getType()]);
+ fflush(stdout);
+ printf("\t%s\n",curr->m_StrippedText);
+ fflush(stdout);
+ for (int i=0;curr->getArgument(i);i++)
+ {
+ if (i==0)
+ printf("\t\targs: ");
+ fflush(stdout);
+ printf("\"%s\" ",curr->getArgument(i)->getStringValue());
+ }
+ printf("\n");
+#endif
+#endif
+ }
+
+
+ if (logclosetime==0 && !plogfile->empty())
+ {
+ CEventListIterator it=plogfile->end();
+ --it;
+ logclosetime=(*it)->getTime();
+ }
+
+ map<PID,CPlayer>::iterator it2;
+ for(it2=players.begin();it2!=players.end();++it2)
+ {
+ CPlayer& p=(*it2).second;
+
+
+ if (p.aliases.endTime < logclosetime)
+ p.aliases.endTime=logclosetime;
+
+ p.name=p.aliases.favourite();
+
+ if (p.allclassesplayed.endTime < logclosetime)
+ p.allclassesplayed.endTime=logclosetime;
+
+ if (p.teams.endTime < logclosetime)
+ p.teams.endTime=logclosetime;
+
+ for (int i=0;i<MAX_TEAMS;i++)
+ {
+ //if you have no kills you have to play on a team at least 30 seconds to be counted part of it
+ //also give a one-suicide grace so they can killthemselves to get onto another team?
+ if (p.teams.howLong(i) < 30 && p.perteam[i].kills==0)// && p.perteam[i].deaths < 1)
+ p.teams.remove(i);
+
+ CTimeIndexedList<player_class>* v= &p.perteam[i].classesplayed;
+ p.perteam[i].classesplayed.endTime=logclosetime;
+
+ time_t t=p.teams.howLong(i);
+ p.perteam[i].timeon=t;
+
+ }
+
+ }
+}
+
+
+//------------------------------------------------------------------------------------------------------
+// Function: CMatchInfo::getPlayerID
+// Purpose: resolves a player name to that players PID
+// Input: name - the name
+// Output: PID the PID
+//------------------------------------------------------------------------------------------------------
+PID CMatchInfo::getPlayerID(string name)
+{
+ CPlayerListIterator it;
+
+ //ugh! O(n)
+ for (it=playerBegin();it!=playerEnd();++it)
+ {
+ PID id=(*it).first;
+ CPlayer curr=(*it).second;
+
+ if (curr.name == name)
+ return id;
+ }
+ return -1;
+}
+
+/*
+unsigned long CMatchInfo::getPlayerWONID(string name)
+{
+ CPlayerListIterator it;
+
+ //ugh! O(n)
+ for (it=playerBegin();it!=playerEnd();++it)
+ {
+ CPlayer curr=(*it).second;
+
+ if (curr.name == name)
+ return curr.WONID;
+ }
+ return 0xffffffff;
+}
+*/
+//------------------------------------------------------------------------------------------------------
+// Function: CMatchInfo::CMatchInfo
+// Purpose: Constructor
+// Input: plf - the log file
+// Output:
+//------------------------------------------------------------------------------------------------------
+CMatchInfo::CMatchInfo(CEventList* plf)
+:numPlrs(0),logclosetime(0),plogfile(plf)
+{
+ teamnames[0]="Blue";
+ teamnames[1]="Red";
+ teamnames[2]="Yellow";
+ teamnames[3]="Green";
+ team_exists[0]=team_exists[1]=team_exists[2]=team_exists[3]=false;
+
+
+ generate();
+
+}
+
+
+
+//------------------------------------------------------------------------------------------------------
+// Function: CMatchInfo::teamID
+// Purpose: resolves a team name to its ID
+// Input: teamname - the team name
+// Output: int the ID of the team
+//------------------------------------------------------------------------------------------------------
+int CMatchInfo::teamID(string teamname)
+{
+ for (int i=0;i<MAX_TEAMS;i++)
+ if (stricmp(teamname.c_str(),teamnames[i].c_str())==0)
+ return i;
+ return -1;
+}
+
+
+//------------------------------------------------------------------------------------------------------
+// Function: CMatchInfo::getTimeOn
+// Purpose: returns how long the specified player has been playing
+// Input: pid - the player being queried
+// Output: time_t the time he/she played
+//------------------------------------------------------------------------------------------------------
+time_t CMatchInfo::getTimeOn(PID pid)
+{
+ CPlayer& p=players[pid];
+
+ if (p.logofftime==0)
+ p.logofftime=logclosetime;
+
+ time_t timeon=p.logofftime-p.logontime;
+
+ return timeon;
+}