summaryrefslogtreecommitdiff
path: root/utils/tfstats/plrpersist.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/tfstats/plrpersist.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/tfstats/plrpersist.cpp')
-rw-r--r--utils/tfstats/plrpersist.cpp419
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;
+}