diff options
Diffstat (limited to 'utils/tfstats/plrpersist.cpp')
| -rw-r--r-- | utils/tfstats/plrpersist.cpp | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/utils/tfstats/plrpersist.cpp b/utils/tfstats/plrpersist.cpp new file mode 100644 index 0000000..5a3b86e --- /dev/null +++ b/utils/tfstats/plrpersist.cpp @@ -0,0 +1,419 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//------------------------------------------------------------------------------------------------------ +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#pragma warning (disable:4786) +#include "PlrPersist.h" +#include "TextFile.h" + +#include <map> +#include <string> +using namespace std; + +//------------------------------------------------------------------------------------------------------ +// Function: CPlrPersist::generate +// Purpose: fills in the fields of this with the data in the given CPlayer object +// Input: cp - the player object to get data from +//------------------------------------------------------------------------------------------------------ +void CPlrPersist::generate(CPlayer& cp) +{ + kills=deaths=timeon=0; + valid=true; + WONID=cp.WONID; + matches=1; + + lastplayed=cp.logofftime; + + //do perteam stuff + CTimeIndexedList<int>::iterator teamiter=cp.teams.begin(); + for (teamiter;teamiter!=cp.teams.end();++teamiter) + { + int tdt=teamiter->data; + kills+=cp.perteam[teamiter->data].kills; + deaths+=cp.perteam[teamiter->data].deaths; + timeon+=cp.perteam[teamiter->data].timeon; + + map<string,int>::iterator it; + it=cp.perteam[teamiter->data].weaponKills.begin(); + for (it;it!=cp.perteam[teamiter->data].weaponKills.end();++it) + { + string name=it->first; + int kills=it->second; + weapmap[name]+=kills; + } + } + + CTimeIndexedList<player_class>::iterator clsit=cp.allclassesplayed.begin(); + for (clsit;clsit!=cp.allclassesplayed.end();++clsit) + { + string classname=plrClassNames[clsit->data]; + classmap[classname]+=cp.allclassesplayed.howLong(clsit->data); + } + + CTimeIndexedList<string>::iterator nameiter; + for (nameiter=cp.aliases.begin();nameiter!=cp.aliases.end();++nameiter) + { + nickmap[nameiter->data]+=cp.aliases.howLong(nameiter->data); + } + + pair<time_t,time_t> startstop; + startstop.first=cp.logontime; + startstop.second=cp.logofftime; + + playtimes.push_back(startstop); +} + +//------------------------------------------------------------------------------------------------------ +// Function: CPlrPersist::merge +// Purpose: merges the stats of another CPlrPersist object into this one. +// This is the key operation of this class. This is how player stats are kept up +// to date over time. If the two data files have playtimes that overlap, they +// are not merged (unless the mergeOverlaps flag is true) +// Input: other - the CPlrPersist object that we want to merge into this one +// mergeOverlaps - if true, overlapping playtimes are ignored. +//------------------------------------------------------------------------------------------------------ +void CPlrPersist::merge(CPlrPersist& other,bool mergeOverlaps) +{ + if (!other.valid) + return; //don't modify + if (WONID!=other.WONID) + { + g_pApp->warning("merging stats for two different WONIDs (%lu, %lu)",WONID,other.WONID); + + } + else + { + //do playtimes first to see if overlaps occur + list<pair<time_t,time_t> >::iterator itOther=other.playtimes.begin(); + for (itOther;itOther!=other.playtimes.end();++itOther) + { + list<pair<time_t,time_t> >::iterator overlap=timesOverlap(itOther->first,itOther->second); + time_t overlapSecond=overlap->second; + time_t overlapFirst=overlap->first; + if (mergeOverlaps || overlap==playtimes.end()) + playtimes.push_back(*itOther); + else + { + g_pApp->warning("not merging stats for WON ID# %lu, playtime ranges overlap\n\t((%lu-%lu) overlaps with (%lu-%lu))",WONID,itOther->first,itOther->second,overlap->first,overlap->second); + return; + } + } + } + + + matches+=other.matches; + kills+=other.kills; + deaths+=other.deaths; + timeon+=other.timeon; + if (other.lastplayed > lastplayed) + lastplayed=other.lastplayed; + + //do names + map<string,int>::iterator it; + it=other.nickmap.begin(); + for (it;it!=other.nickmap.end();++it) + { + string name=it->first; + int time=it->second; + nickmap[name]+=time; + } + + //do weapons + it=other.weapmap.begin(); + for (it;it!=other.weapmap.end();++it) + { + string name=it->first; + int kills=it->second; + weapmap[name]+=kills; + } + + //do classes + it=other.classmap.begin(); + for (it;it!=other.classmap.end();++it) + { + string name=it->first; + int time=it->second; + classmap[name]+=time; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function: CPlrPersist::read +// Purpose: fills in the fields of this by reading data out of a file +// Input: f - the file from which to read the data +//------------------------------------------------------------------------------------------------------ +void CPlrPersist::read(CTextFile& f) +{ + if (!f.isValid()) + { + kills=deaths=timeon=0; WONID=-1; + valid=false; + return; + } + + if(WONID==-1) + { + //parse it out of f; + string s=f.fileName(); + char buf[100]; + int startpos=s.find_last_of(g_pApp->os->pathSeperator()); + int endpos=s.find_last_of("."); + if (endpos == -1) + return; + if (startpos==-1) + startpos=0; + + s.copy(buf,(endpos-startpos),startpos); + buf[endpos-startpos]=0; + WONID=strtoul(buf,NULL,10); + if (!WONID) + { + WONID=-1; + valid=false; + return; + } + + + } + + + + valid=false; + + if (!f.eof()) kills=f.readInt(); else return; + if (!f.eof()) deaths=f.readInt(); else return; + if (!f.eof()) timeon=f.readULong(); else return; + if (!f.eof()) matches=f.readInt(); else return; + if (!f.eof()) lastplayed=f.readULong(); else return; + + string next; + + if (!f.eof()) + { + f.discard("names"); + next= f.peekNextString(); + while ( next!="endnames") + { + string name=f.readString(); + int timeon=f.readInt(); + nickmap[name]=timeon; + next=f.peekNextString(); + } + f.discard("endnames"); + } else return; + + if (!f.eof()) + { + f.discard("weapons"); + next= f.peekNextString(); + while (next!="endweapons") + { + string name=f.readString(); + int kills=f.readInt(); + weapmap[name]=kills; + next=f.peekNextString(); + } + f.discard("endweapons"); + } else return; + + if (!f.eof()) + { + f.discard("classes"); + next= f.peekNextString(); + while (next!="endclasses") + { + string name=f.readString(); + int timeused=f.readInt(); + classmap[name]=timeused; + next=f.peekNextString(); + } + f.discard("endclasses"); + } else return; + + if (!f.eof()) + { + f.discard("playtimes"); + next= f.peekNextString(); + while (next!="endplaytimes") + { + pair<time_t,time_t> startstop; + startstop.first=f.readULong(); + startstop.second=f.readULong(); + playtimes.push_back(startstop); + next=f.peekNextString(); + } + f.discard("endplaytimes"); + } else return; + + valid=true; + +} + +//------------------------------------------------------------------------------------------------------ +// Function: CPlrPersist::read +// Purpose: converts the WONID to a file name (<wonid>.tfs) and passes execution +// off to the above read function. +// Input: WONID - the WONID of the player whose datafile we want to read +//------------------------------------------------------------------------------------------------------ +void CPlrPersist::read(unsigned long WONID) +{ + + string file=g_pApp->playerDirectory; + char buf[100]; + file+=g_pApp->os->ultoa(WONID,buf,10); + file+=".tfs"; + + this->WONID=WONID; + + CTextFile f(file.c_str()); + read(f); +} + + + +void CPlrPersist::write() +{ + string file=g_pApp->playerDirectory; + char buf[100]; + file+=g_pApp->os->ultoa(WONID,buf,10); + file+=".tfs"; + + FILE* fout=fopen(file.c_str(),"wt"); + + fprintf(fout,"%li //kills\n",kills); + fprintf(fout,"%li //deaths\n",deaths); + fprintf(fout,"%lu //timeon\n",timeon); + fprintf(fout,"%li //matches played\n",matches); + fprintf(fout,"%lu //last played\n",lastplayed); + + map<string,int>::iterator it; + + fprintf(fout,"names\n"); + it=nickmap.begin(); + for (it;it!=nickmap.end();++it) + { + string name=it->first; + int time=it->second; + fprintf(fout,"\t\"%s\" %li //has used the name \"%s\" for %02li:%02li:%02li\n",name.c_str(),time,name.c_str(),Util::time_t2hours(time),Util::time_t2mins(time),Util::time_t2secs(time)); + } + fprintf(fout,"endnames\n"); + + fprintf(fout,"weapons\n"); + it=weapmap.begin(); + for (it;it!=weapmap.end();++it) + { + string name=it->first; + int kills=it->second; + fprintf(fout,"\t\"%s\" %li //has killed %li people with \"%s\"\n",name.c_str(),kills,kills,name.c_str()); + } + fprintf(fout,"endweapons\n"); + + fprintf(fout,"classes\n"); + it=classmap.begin(); + for (it;it!=classmap.end();++it) + { + string name=it->first; + int time=it->second; + fprintf(fout,"\t\"%s\" %li //has played as a \"%s\" for %02li:%02li:%02li\n",name.c_str(),time,name.c_str(),Util::time_t2hours(time),Util::time_t2mins(time),Util::time_t2secs(time)); + } + fprintf(fout,"endclasses\n"); + + fprintf(fout,"playtimes\n"); + list<pair<time_t,time_t> >::iterator it2=playtimes.begin(); + for (it2;it2!=playtimes.end();++it2) + { + char buf[500]; + time_t t1=it2->first; + time_t t2=it2->second; + bool doesOverlap; + + list<pair<time_t,time_t> >::iterator overlap=timesOverlap(it2->first,it2->second,false); + doesOverlap= overlap!=playtimes.end(); + + + fprintf(fout,"\t%lu %lu //played from %s.",it2->first,it2->second,Util::makeDurationString(it2->first,it2->second,buf," to ")); + if (doesOverlap) + fprintf(fout,"Warning! overlaps with time range (%lu-%lu)",overlap->first,overlap->second); + fprintf(fout,"\n"); + + } + fprintf(fout,"endplaytimes\n"); + + + fclose(fout); + +} + + +list<pair<time_t,time_t> >::iterator CPlrPersist::timesOverlap(time_t start, time_t end,bool testself) +{ + list<pair<time_t,time_t> >::iterator it; + it=playtimes.begin(); + for (it;it!=playtimes.end();++it) + { + time_t itFirst=it->first; + time_t itSecond=it->second; + if (start == it->first && end == it->second) + { + if (testself) + break; + } + //if start is in current range + else if (start >= it->first && start <= it->second) + break; + + //if end is in current range + else if (end >= it->first && end <= it->second) + break; + + //if the start is before this range and end is after + else if (start <= it->first && end >= it->second) + break; + } + return it; +} + +string CPlrPersist::faveString(map<string,int>& theMap) +{ + string retstr; + time_t max=0; + map<string,int>::iterator it=theMap.begin(); + for (it;it!=theMap.end();++it) + { + if (it->second > max) + { + max=it->second; + retstr=it->first; + } + } + return retstr; +} + +string CPlrPersist::faveName() +{ + return faveString(nickmap); +} +string CPlrPersist::faveWeap() +{ + string s=faveString(weapmap); + faveweapkills=weapmap[s]; + return s; +} +string CPlrPersist::faveClass() +{ + return faveString(classmap); +} + +double CPlrPersist::rank() +{ + return ((double)((double)kills - (double)deaths) * 1000.0) / (double)timeon; +} |