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.
420 lines
10 KiB
420 lines
10 KiB
5 years ago
|
//========= 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;
|
||
|
}
|