Team Fortress 2 Source Code as on 22/4/2020
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.

419 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. //
  8. //------------------------------------------------------------------------------------------------------
  9. // $Log: $
  10. //
  11. // $NoKeywords: $
  12. //=============================================================================//
  13. #pragma warning (disable:4786)
  14. #include "PlrPersist.h"
  15. #include "TextFile.h"
  16. #include <map>
  17. #include <string>
  18. using namespace std;
  19. //------------------------------------------------------------------------------------------------------
  20. // Function: CPlrPersist::generate
  21. // Purpose: fills in the fields of this with the data in the given CPlayer object
  22. // Input: cp - the player object to get data from
  23. //------------------------------------------------------------------------------------------------------
  24. void CPlrPersist::generate(CPlayer& cp)
  25. {
  26. kills=deaths=timeon=0;
  27. valid=true;
  28. WONID=cp.WONID;
  29. matches=1;
  30. lastplayed=cp.logofftime;
  31. //do perteam stuff
  32. CTimeIndexedList<int>::iterator teamiter=cp.teams.begin();
  33. for (teamiter;teamiter!=cp.teams.end();++teamiter)
  34. {
  35. int tdt=teamiter->data;
  36. kills+=cp.perteam[teamiter->data].kills;
  37. deaths+=cp.perteam[teamiter->data].deaths;
  38. timeon+=cp.perteam[teamiter->data].timeon;
  39. map<string,int>::iterator it;
  40. it=cp.perteam[teamiter->data].weaponKills.begin();
  41. for (it;it!=cp.perteam[teamiter->data].weaponKills.end();++it)
  42. {
  43. string name=it->first;
  44. int kills=it->second;
  45. weapmap[name]+=kills;
  46. }
  47. }
  48. CTimeIndexedList<player_class>::iterator clsit=cp.allclassesplayed.begin();
  49. for (clsit;clsit!=cp.allclassesplayed.end();++clsit)
  50. {
  51. string classname=plrClassNames[clsit->data];
  52. classmap[classname]+=cp.allclassesplayed.howLong(clsit->data);
  53. }
  54. CTimeIndexedList<string>::iterator nameiter;
  55. for (nameiter=cp.aliases.begin();nameiter!=cp.aliases.end();++nameiter)
  56. {
  57. nickmap[nameiter->data]+=cp.aliases.howLong(nameiter->data);
  58. }
  59. pair<time_t,time_t> startstop;
  60. startstop.first=cp.logontime;
  61. startstop.second=cp.logofftime;
  62. playtimes.push_back(startstop);
  63. }
  64. //------------------------------------------------------------------------------------------------------
  65. // Function: CPlrPersist::merge
  66. // Purpose: merges the stats of another CPlrPersist object into this one.
  67. // This is the key operation of this class. This is how player stats are kept up
  68. // to date over time. If the two data files have playtimes that overlap, they
  69. // are not merged (unless the mergeOverlaps flag is true)
  70. // Input: other - the CPlrPersist object that we want to merge into this one
  71. // mergeOverlaps - if true, overlapping playtimes are ignored.
  72. //------------------------------------------------------------------------------------------------------
  73. void CPlrPersist::merge(CPlrPersist& other,bool mergeOverlaps)
  74. {
  75. if (!other.valid)
  76. return; //don't modify
  77. if (WONID!=other.WONID)
  78. {
  79. g_pApp->warning("merging stats for two different WONIDs (%lu, %lu)",WONID,other.WONID);
  80. }
  81. else
  82. {
  83. //do playtimes first to see if overlaps occur
  84. list<pair<time_t,time_t> >::iterator itOther=other.playtimes.begin();
  85. for (itOther;itOther!=other.playtimes.end();++itOther)
  86. {
  87. list<pair<time_t,time_t> >::iterator overlap=timesOverlap(itOther->first,itOther->second);
  88. time_t overlapSecond=overlap->second;
  89. time_t overlapFirst=overlap->first;
  90. if (mergeOverlaps || overlap==playtimes.end())
  91. playtimes.push_back(*itOther);
  92. else
  93. {
  94. 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);
  95. return;
  96. }
  97. }
  98. }
  99. matches+=other.matches;
  100. kills+=other.kills;
  101. deaths+=other.deaths;
  102. timeon+=other.timeon;
  103. if (other.lastplayed > lastplayed)
  104. lastplayed=other.lastplayed;
  105. //do names
  106. map<string,int>::iterator it;
  107. it=other.nickmap.begin();
  108. for (it;it!=other.nickmap.end();++it)
  109. {
  110. string name=it->first;
  111. int time=it->second;
  112. nickmap[name]+=time;
  113. }
  114. //do weapons
  115. it=other.weapmap.begin();
  116. for (it;it!=other.weapmap.end();++it)
  117. {
  118. string name=it->first;
  119. int kills=it->second;
  120. weapmap[name]+=kills;
  121. }
  122. //do classes
  123. it=other.classmap.begin();
  124. for (it;it!=other.classmap.end();++it)
  125. {
  126. string name=it->first;
  127. int time=it->second;
  128. classmap[name]+=time;
  129. }
  130. }
  131. //------------------------------------------------------------------------------------------------------
  132. // Function: CPlrPersist::read
  133. // Purpose: fills in the fields of this by reading data out of a file
  134. // Input: f - the file from which to read the data
  135. //------------------------------------------------------------------------------------------------------
  136. void CPlrPersist::read(CTextFile& f)
  137. {
  138. if (!f.isValid())
  139. {
  140. kills=deaths=timeon=0; WONID=-1;
  141. valid=false;
  142. return;
  143. }
  144. if(WONID==-1)
  145. {
  146. //parse it out of f;
  147. string s=f.fileName();
  148. char buf[100];
  149. int startpos=s.find_last_of(g_pApp->os->pathSeperator());
  150. int endpos=s.find_last_of(".");
  151. if (endpos == -1)
  152. return;
  153. if (startpos==-1)
  154. startpos=0;
  155. s.copy(buf,(endpos-startpos),startpos);
  156. buf[endpos-startpos]=0;
  157. WONID=strtoul(buf,NULL,10);
  158. if (!WONID)
  159. {
  160. WONID=-1;
  161. valid=false;
  162. return;
  163. }
  164. }
  165. valid=false;
  166. if (!f.eof()) kills=f.readInt(); else return;
  167. if (!f.eof()) deaths=f.readInt(); else return;
  168. if (!f.eof()) timeon=f.readULong(); else return;
  169. if (!f.eof()) matches=f.readInt(); else return;
  170. if (!f.eof()) lastplayed=f.readULong(); else return;
  171. string next;
  172. if (!f.eof())
  173. {
  174. f.discard("names");
  175. next= f.peekNextString();
  176. while ( next!="endnames")
  177. {
  178. string name=f.readString();
  179. int timeon=f.readInt();
  180. nickmap[name]=timeon;
  181. next=f.peekNextString();
  182. }
  183. f.discard("endnames");
  184. } else return;
  185. if (!f.eof())
  186. {
  187. f.discard("weapons");
  188. next= f.peekNextString();
  189. while (next!="endweapons")
  190. {
  191. string name=f.readString();
  192. int kills=f.readInt();
  193. weapmap[name]=kills;
  194. next=f.peekNextString();
  195. }
  196. f.discard("endweapons");
  197. } else return;
  198. if (!f.eof())
  199. {
  200. f.discard("classes");
  201. next= f.peekNextString();
  202. while (next!="endclasses")
  203. {
  204. string name=f.readString();
  205. int timeused=f.readInt();
  206. classmap[name]=timeused;
  207. next=f.peekNextString();
  208. }
  209. f.discard("endclasses");
  210. } else return;
  211. if (!f.eof())
  212. {
  213. f.discard("playtimes");
  214. next= f.peekNextString();
  215. while (next!="endplaytimes")
  216. {
  217. pair<time_t,time_t> startstop;
  218. startstop.first=f.readULong();
  219. startstop.second=f.readULong();
  220. playtimes.push_back(startstop);
  221. next=f.peekNextString();
  222. }
  223. f.discard("endplaytimes");
  224. } else return;
  225. valid=true;
  226. }
  227. //------------------------------------------------------------------------------------------------------
  228. // Function: CPlrPersist::read
  229. // Purpose: converts the WONID to a file name (<wonid>.tfs) and passes execution
  230. // off to the above read function.
  231. // Input: WONID - the WONID of the player whose datafile we want to read
  232. //------------------------------------------------------------------------------------------------------
  233. void CPlrPersist::read(unsigned long WONID)
  234. {
  235. string file=g_pApp->playerDirectory;
  236. char buf[100];
  237. file+=g_pApp->os->ultoa(WONID,buf,10);
  238. file+=".tfs";
  239. this->WONID=WONID;
  240. CTextFile f(file.c_str());
  241. read(f);
  242. }
  243. void CPlrPersist::write()
  244. {
  245. string file=g_pApp->playerDirectory;
  246. char buf[100];
  247. file+=g_pApp->os->ultoa(WONID,buf,10);
  248. file+=".tfs";
  249. FILE* fout=fopen(file.c_str(),"wt");
  250. fprintf(fout,"%li //kills\n",kills);
  251. fprintf(fout,"%li //deaths\n",deaths);
  252. fprintf(fout,"%lu //timeon\n",timeon);
  253. fprintf(fout,"%li //matches played\n",matches);
  254. fprintf(fout,"%lu //last played\n",lastplayed);
  255. map<string,int>::iterator it;
  256. fprintf(fout,"names\n");
  257. it=nickmap.begin();
  258. for (it;it!=nickmap.end();++it)
  259. {
  260. string name=it->first;
  261. int time=it->second;
  262. 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));
  263. }
  264. fprintf(fout,"endnames\n");
  265. fprintf(fout,"weapons\n");
  266. it=weapmap.begin();
  267. for (it;it!=weapmap.end();++it)
  268. {
  269. string name=it->first;
  270. int kills=it->second;
  271. fprintf(fout,"\t\"%s\" %li //has killed %li people with \"%s\"\n",name.c_str(),kills,kills,name.c_str());
  272. }
  273. fprintf(fout,"endweapons\n");
  274. fprintf(fout,"classes\n");
  275. it=classmap.begin();
  276. for (it;it!=classmap.end();++it)
  277. {
  278. string name=it->first;
  279. int time=it->second;
  280. 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));
  281. }
  282. fprintf(fout,"endclasses\n");
  283. fprintf(fout,"playtimes\n");
  284. list<pair<time_t,time_t> >::iterator it2=playtimes.begin();
  285. for (it2;it2!=playtimes.end();++it2)
  286. {
  287. char buf[500];
  288. time_t t1=it2->first;
  289. time_t t2=it2->second;
  290. bool doesOverlap;
  291. list<pair<time_t,time_t> >::iterator overlap=timesOverlap(it2->first,it2->second,false);
  292. doesOverlap= overlap!=playtimes.end();
  293. fprintf(fout,"\t%lu %lu //played from %s.",it2->first,it2->second,Util::makeDurationString(it2->first,it2->second,buf," to "));
  294. if (doesOverlap)
  295. fprintf(fout,"Warning! overlaps with time range (%lu-%lu)",overlap->first,overlap->second);
  296. fprintf(fout,"\n");
  297. }
  298. fprintf(fout,"endplaytimes\n");
  299. fclose(fout);
  300. }
  301. list<pair<time_t,time_t> >::iterator CPlrPersist::timesOverlap(time_t start, time_t end,bool testself)
  302. {
  303. list<pair<time_t,time_t> >::iterator it;
  304. it=playtimes.begin();
  305. for (it;it!=playtimes.end();++it)
  306. {
  307. time_t itFirst=it->first;
  308. time_t itSecond=it->second;
  309. if (start == it->first && end == it->second)
  310. {
  311. if (testself)
  312. break;
  313. }
  314. //if start is in current range
  315. else if (start >= it->first && start <= it->second)
  316. break;
  317. //if end is in current range
  318. else if (end >= it->first && end <= it->second)
  319. break;
  320. //if the start is before this range and end is after
  321. else if (start <= it->first && end >= it->second)
  322. break;
  323. }
  324. return it;
  325. }
  326. string CPlrPersist::faveString(map<string,int>& theMap)
  327. {
  328. string retstr;
  329. time_t max=0;
  330. map<string,int>::iterator it=theMap.begin();
  331. for (it;it!=theMap.end();++it)
  332. {
  333. if (it->second > max)
  334. {
  335. max=it->second;
  336. retstr=it->first;
  337. }
  338. }
  339. return retstr;
  340. }
  341. string CPlrPersist::faveName()
  342. {
  343. return faveString(nickmap);
  344. }
  345. string CPlrPersist::faveWeap()
  346. {
  347. string s=faveString(weapmap);
  348. faveweapkills=weapmap[s];
  349. return s;
  350. }
  351. string CPlrPersist::faveClass()
  352. {
  353. return faveString(classmap);
  354. }
  355. double CPlrPersist::rank()
  356. {
  357. return ((double)((double)kills - (double)deaths) * 1000.0) / (double)timeon;
  358. }