|
|
//========= 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; }
|