PersistentStorageThread.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PersistentStorageThread.cpp //////////////////////////////////////////////////////
  24. // GameSpy Persistent Storage thread
  25. // This thread communicates with GameSpy's persistent storage server
  26. // and talks through a message queue with the rest of
  27. // the game.
  28. // Author: Matthew D. Campbell, July 2002
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "Common/UserPreferences.h"
  31. #include "Common/PlayerTemplate.h"
  32. #include "GameNetwork/GameSpy/PersistentStorageThread.h"
  33. #include "mutex.h"
  34. #include "thread.h"
  35. #include "Common/StackDump.h"
  36. #include "Common/SubsystemInterface.h"
  37. #ifdef _INTERNAL
  38. // for occasional debugging...
  39. //#pragma optimize("", off)
  40. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  41. #endif
  42. //-------------------------------------------------------------------------
  43. PSRequest::PSRequest()
  44. {
  45. player.reset();
  46. requestType = PSREQUEST_READPLAYERSTATS;
  47. addDiscon = addDesync = FALSE;
  48. lastHouse = -1;
  49. }
  50. //-------------------------------------------------------------------------
  51. #define DEBUG_MAP(x) for (it = stats.x.begin(); it != stats.x.end(); ++it) \
  52. { \
  53. if (it->second > 0) \
  54. { \
  55. DEBUG_LOG(("%s(%d): %d\n", #x, it->first, it->second)); \
  56. } \
  57. }
  58. static void debugDumpPlayerStats( const PSPlayerStats& stats )
  59. {
  60. DEBUG_LOG(("-----------------------------------------\n"));
  61. DEBUG_LOG(("Tracking player stats for player %d:\n", stats.id));
  62. PerGeneralMap::const_iterator it;
  63. DEBUG_MAP(wins);
  64. DEBUG_MAP(losses);
  65. DEBUG_MAP(games);
  66. DEBUG_MAP(duration);
  67. DEBUG_MAP(unitsKilled);
  68. DEBUG_MAP(unitsLost);
  69. DEBUG_MAP(unitsBuilt);
  70. DEBUG_MAP(buildingsKilled);
  71. DEBUG_MAP(buildingsLost);
  72. DEBUG_MAP(buildingsBuilt);
  73. DEBUG_MAP(earnings);
  74. DEBUG_MAP(techCaptured);
  75. DEBUG_MAP(discons);
  76. DEBUG_MAP(desyncs);
  77. DEBUG_MAP(surrenders);
  78. DEBUG_MAP(gamesOf2p);
  79. DEBUG_MAP(gamesOf3p);
  80. DEBUG_MAP(gamesOf4p);
  81. DEBUG_MAP(gamesOf5p);
  82. DEBUG_MAP(gamesOf6p);
  83. DEBUG_MAP(gamesOf7p);
  84. DEBUG_MAP(gamesOf8p);
  85. DEBUG_MAP(customGames);
  86. DEBUG_MAP(QMGames);
  87. if (stats.locale > 0)
  88. {
  89. DEBUG_LOG(("Locale: %d\n", stats.locale));
  90. }
  91. if (stats.gamesAsRandom > 0)
  92. {
  93. DEBUG_LOG(("gamesAsRandom: %d\n", stats.gamesAsRandom));
  94. }
  95. if (stats.options.length())
  96. {
  97. DEBUG_LOG(("Options: %s\n", stats.options.c_str()));
  98. }
  99. if (stats.systemSpec.length())
  100. {
  101. DEBUG_LOG(("systemSpec: %s\n", stats.systemSpec.c_str()));
  102. }
  103. if (stats.lastFPS > 0.0f)
  104. {
  105. DEBUG_LOG(("lastFPS: %g\n", stats.lastFPS));
  106. }
  107. if (stats.battleHonors > 0)
  108. {
  109. DEBUG_LOG(("battleHonors: %x\n", stats.battleHonors));
  110. }
  111. if (stats.challengeMedals > 0)
  112. {
  113. DEBUG_LOG(("challengeMedals: %x\n", stats.challengeMedals));
  114. }
  115. if (stats.lastGeneral >= 0)
  116. {
  117. DEBUG_LOG(("lastGeneral: %d\n", stats.lastGeneral));
  118. }
  119. if (stats.gamesInRowWithLastGeneral >= 0)
  120. {
  121. DEBUG_LOG(("gamesInRowWithLastGeneral: %d\n", stats.gamesInRowWithLastGeneral));
  122. }
  123. if (stats.builtSCUD >= 0)
  124. {
  125. DEBUG_LOG(("builtSCUD: %d\n", stats.builtSCUD));
  126. }
  127. if (stats.builtNuke >= 0)
  128. {
  129. DEBUG_LOG(("builtNuke: %d\n", stats.builtNuke));
  130. }
  131. if (stats.builtParticleCannon >= 0)
  132. {
  133. DEBUG_LOG(("builtParticleCannon: %d\n", stats.builtParticleCannon));
  134. }
  135. if (stats.winsInARow >= 0)
  136. {
  137. DEBUG_LOG(("winsInARow: %d\n", stats.winsInARow));
  138. }
  139. if (stats.maxWinsInARow >= 0)
  140. {
  141. DEBUG_LOG(("maxWinsInARow: %d\n", stats.maxWinsInARow));
  142. }
  143. if (stats.disconsInARow >= 0)
  144. {
  145. DEBUG_LOG(("disconsInARow: %d\n", stats.disconsInARow));
  146. }
  147. if (stats.maxDisconsInARow >= 0)
  148. {
  149. DEBUG_LOG(("maxDisconsInARow: %d\n", stats.maxDisconsInARow));
  150. }
  151. if (stats.lossesInARow >= 0)
  152. {
  153. DEBUG_LOG(("lossesInARow: %d\n", stats.lossesInARow));
  154. }
  155. if (stats.maxLossesInARow >= 0)
  156. {
  157. DEBUG_LOG(("maxLossesInARow: %d\n", stats.maxLossesInARow));
  158. }
  159. if (stats.desyncsInARow >= 0)
  160. {
  161. DEBUG_LOG(("desyncsInARow: %d\n", stats.desyncsInARow));
  162. }
  163. if (stats.maxDesyncsInARow >= 0)
  164. {
  165. DEBUG_LOG(("maxDesyncsInARow: %d\n", stats.maxDesyncsInARow));
  166. }
  167. if (stats.lastLadderPort >= 0)
  168. {
  169. DEBUG_LOG(("lastLadderPort: %d\n", stats.lastLadderPort));
  170. }
  171. if (stats.lastLadderHost.length())
  172. {
  173. DEBUG_LOG(("lastLadderHost: %s\n", stats.lastLadderHost.c_str()));
  174. }
  175. }
  176. //-------------------------------------------------------------------------
  177. #define INCORPORATE_MAP(x) for (it = other.x.begin(); it != other.x.end(); ++it) \
  178. { \
  179. if (it->second > 0) \
  180. { \
  181. x[it->first] = it->second; \
  182. } \
  183. }
  184. void PSPlayerStats::incorporate( const PSPlayerStats& other )
  185. {
  186. PerGeneralMap::const_iterator it;
  187. INCORPORATE_MAP(wins);
  188. INCORPORATE_MAP(losses);
  189. INCORPORATE_MAP(games);
  190. INCORPORATE_MAP(duration);
  191. INCORPORATE_MAP(unitsKilled);
  192. INCORPORATE_MAP(unitsLost);
  193. INCORPORATE_MAP(unitsBuilt);
  194. INCORPORATE_MAP(buildingsKilled);
  195. INCORPORATE_MAP(buildingsLost);
  196. INCORPORATE_MAP(buildingsBuilt);
  197. INCORPORATE_MAP(earnings);
  198. INCORPORATE_MAP(techCaptured);
  199. //GS Clear all disconnects so that we don't retain any that were
  200. //previously reported as 1 by updateAdditionalGameSpyDisconnections
  201. discons.clear();
  202. INCORPORATE_MAP(discons);
  203. INCORPORATE_MAP(desyncs);
  204. INCORPORATE_MAP(surrenders);
  205. INCORPORATE_MAP(gamesOf2p);
  206. INCORPORATE_MAP(gamesOf3p);
  207. INCORPORATE_MAP(gamesOf4p);
  208. INCORPORATE_MAP(gamesOf5p);
  209. INCORPORATE_MAP(gamesOf6p);
  210. INCORPORATE_MAP(gamesOf7p);
  211. INCORPORATE_MAP(gamesOf8p);
  212. INCORPORATE_MAP(customGames);
  213. INCORPORATE_MAP(QMGames);
  214. if (other.locale > 0)
  215. {
  216. locale = other.locale;
  217. }
  218. if (other.gamesAsRandom > 0)
  219. {
  220. gamesAsRandom = other.gamesAsRandom;
  221. }
  222. if (other.options.length())
  223. {
  224. options = other.options;
  225. }
  226. if (other.systemSpec.length())
  227. {
  228. systemSpec = other.systemSpec;
  229. }
  230. if (other.lastFPS > 0.0f)
  231. {
  232. lastFPS = other.lastFPS;
  233. }
  234. if (other.battleHonors > 0)
  235. {
  236. battleHonors |= other.battleHonors;
  237. }
  238. if (other.challengeMedals > 0)
  239. {
  240. challengeMedals |= other.challengeMedals;
  241. }
  242. if (other.lastGeneral >= 0)
  243. {
  244. lastGeneral = other.lastGeneral;
  245. }
  246. if (other.gamesInRowWithLastGeneral >= 0)
  247. {
  248. gamesInRowWithLastGeneral = other.gamesInRowWithLastGeneral;
  249. }
  250. if (other.builtParticleCannon >= 0)
  251. {
  252. builtParticleCannon = other.builtParticleCannon;
  253. }
  254. if (other.builtNuke >= 0)
  255. {
  256. builtNuke = other.builtNuke;
  257. }
  258. if (other.builtSCUD >= 0)
  259. {
  260. builtSCUD = other.builtSCUD;
  261. }
  262. if (other.winsInARow >= 0)
  263. {
  264. winsInARow = other.winsInARow;
  265. }
  266. if (other.maxWinsInARow >= 0)
  267. {
  268. maxWinsInARow = other.maxWinsInARow;
  269. }
  270. if (other.lossesInARow >= 0)
  271. {
  272. lossesInARow = other.lossesInARow;
  273. }
  274. if (other.maxLossesInARow >= 0)
  275. {
  276. maxLossesInARow = other.maxLossesInARow;
  277. }
  278. if (other.disconsInARow >= 0)
  279. {
  280. disconsInARow = other.disconsInARow;
  281. }
  282. if (other.maxDisconsInARow >= 0)
  283. {
  284. maxDisconsInARow = other.maxDisconsInARow;
  285. }
  286. if (other.desyncsInARow >= 0)
  287. {
  288. desyncsInARow = other.desyncsInARow;
  289. }
  290. if (other.maxDesyncsInARow >= 0)
  291. {
  292. maxDesyncsInARow = other.maxDesyncsInARow;
  293. }
  294. if (other.lastLadderPort >= 0)
  295. {
  296. lastLadderPort = other.lastLadderPort;
  297. }
  298. if (other.lastLadderHost.length())
  299. {
  300. lastLadderHost = other.lastLadderHost;
  301. }
  302. }
  303. PSPlayerStats::PSPlayerStats( const PSPlayerStats& other )
  304. {
  305. incorporate(other);
  306. id = other.id;
  307. locale = other.locale;
  308. gamesAsRandom = other.gamesAsRandom;
  309. options = other.options;
  310. systemSpec = other.systemSpec;
  311. lastFPS = other.lastFPS;
  312. lastGeneral = other.lastGeneral;
  313. gamesInRowWithLastGeneral = other.gamesInRowWithLastGeneral;
  314. builtParticleCannon = other.builtParticleCannon;
  315. builtNuke = other.builtNuke;
  316. builtSCUD = other.builtSCUD;
  317. challengeMedals = other.challengeMedals;
  318. battleHonors = other.battleHonors;
  319. winsInARow = other.winsInARow;
  320. maxWinsInARow = other.maxWinsInARow;
  321. lossesInARow = other.lossesInARow;
  322. maxLossesInARow = other.maxLossesInARow;
  323. disconsInARow = other.disconsInARow;
  324. maxDisconsInARow = other.maxDisconsInARow;
  325. desyncsInARow = other.desyncsInARow;
  326. maxDesyncsInARow = other.maxDesyncsInARow;
  327. lastLadderHost = other.lastLadderHost;
  328. lastLadderPort = other.lastLadderPort;
  329. }
  330. //-------------------------------------------------------------------------
  331. typedef std::queue<PSRequest> RequestQueue;
  332. typedef std::queue<PSResponse> ResponseQueue;
  333. class PSThreadClass;
  334. class GameSpyPSMessageQueue : public GameSpyPSMessageQueueInterface
  335. {
  336. public:
  337. virtual ~GameSpyPSMessageQueue();
  338. GameSpyPSMessageQueue();
  339. virtual void startThread( void );
  340. virtual void endThread( void );
  341. virtual Bool isThreadRunning( void );
  342. virtual void addRequest( const PSRequest& req );
  343. virtual Bool getRequest( PSRequest& req );
  344. virtual void addResponse( const PSResponse& resp );
  345. virtual Bool getResponse( PSResponse& resp );
  346. virtual void trackPlayerStats( PSPlayerStats stats );
  347. virtual PSPlayerStats findPlayerStatsByID( Int id );
  348. PSThreadClass* getThread( void );
  349. Int getLocalPlayerID(void) { return m_localPlayerID; }
  350. void setLocalPlayerID(Int localPlayerID) { m_localPlayerID = localPlayerID; }
  351. std::string getEmail() { return m_email; }
  352. std::string getNick() { return m_nick; }
  353. std::string getPassword() { return m_password; }
  354. void setEmail(std::string email) { m_email = email; }
  355. void setNick(std::string nick) { m_nick = nick; }
  356. void setPassword(std::string password) { m_password = password; }
  357. private:
  358. MutexClass m_requestMutex;
  359. MutexClass m_responseMutex;
  360. RequestQueue m_requests;
  361. ResponseQueue m_responses;
  362. PSThreadClass *m_thread;
  363. Int m_localPlayerID;
  364. std::string m_email;
  365. std::string m_nick;
  366. std::string m_password;
  367. std::map<Int, PSPlayerStats> m_playerStats;
  368. };
  369. GameSpyPSMessageQueueInterface* GameSpyPSMessageQueueInterface::createNewMessageQueue( void )
  370. {
  371. return NEW GameSpyPSMessageQueue;
  372. }
  373. GameSpyPSMessageQueueInterface *TheGameSpyPSMessageQueue = NULL;
  374. #define MESSAGE_QUEUE ((GameSpyPSMessageQueue *)TheGameSpyPSMessageQueue)
  375. //-------------------------------------------------------------------------
  376. class PSThreadClass : public ThreadClass
  377. {
  378. public:
  379. PSThreadClass() : ThreadClass()
  380. {
  381. m_loginOK = m_sawLocalData = m_doneTryingToLogin = false;
  382. m_opCount = 0;
  383. }
  384. void Thread_Function();
  385. void persAuthCallback( Bool val ) { m_loginOK = val; m_doneTryingToLogin = true; }
  386. void decrOpCount( void ) { --m_opCount; }
  387. void incrOpCount( void ) { ++m_opCount; }
  388. Int getOpCount( void ) { return m_opCount; }
  389. Bool sawLocalPlayerData( void ) { return m_sawLocalData; }
  390. void gotLocalPlayerData( void ) { m_sawLocalData = TRUE; }
  391. private:
  392. Bool tryConnect( void );
  393. Bool tryLogin( Int id, std::string nick, std::string password, std::string email );
  394. Bool m_loginOK;
  395. Bool m_doneTryingToLogin;
  396. Int m_opCount;
  397. Bool m_sawLocalData;
  398. };
  399. //-------------------------------------------------------------------------
  400. GameSpyPSMessageQueue::GameSpyPSMessageQueue()
  401. {
  402. m_thread = NULL;
  403. m_localPlayerID = 0;
  404. }
  405. GameSpyPSMessageQueue::~GameSpyPSMessageQueue()
  406. {
  407. endThread();
  408. }
  409. void GameSpyPSMessageQueue::startThread( void )
  410. {
  411. if (!m_thread)
  412. {
  413. m_thread = NEW PSThreadClass;
  414. m_thread->Execute();
  415. }
  416. else
  417. {
  418. if (!m_thread->Is_Running())
  419. {
  420. m_thread->Execute();
  421. }
  422. }
  423. }
  424. void GameSpyPSMessageQueue::endThread( void )
  425. {
  426. if (m_thread)
  427. delete m_thread;
  428. m_thread = NULL;
  429. }
  430. Bool GameSpyPSMessageQueue::isThreadRunning( void )
  431. {
  432. return (m_thread) ? m_thread->Is_Running() : false;
  433. }
  434. void GameSpyPSMessageQueue::addRequest( const PSRequest& req )
  435. {
  436. MutexClass::LockClass m(m_requestMutex);
  437. if (m.Failed())
  438. return;
  439. m_requests.push(req);
  440. }
  441. Bool GameSpyPSMessageQueue::getRequest( PSRequest& req )
  442. {
  443. MutexClass::LockClass m(m_requestMutex, 0);
  444. if (m.Failed())
  445. return false;
  446. if (m_requests.empty())
  447. return false;
  448. req = m_requests.front();
  449. m_requests.pop();
  450. return true;
  451. }
  452. void GameSpyPSMessageQueue::addResponse( const PSResponse& resp )
  453. {
  454. MutexClass::LockClass m(m_responseMutex);
  455. if (m.Failed())
  456. return;
  457. m_responses.push(resp);
  458. }
  459. Bool GameSpyPSMessageQueue::getResponse( PSResponse& resp )
  460. {
  461. MutexClass::LockClass m(m_responseMutex, 0);
  462. if (m.Failed())
  463. return false;
  464. if (m_responses.empty())
  465. return false;
  466. resp = m_responses.front();
  467. m_responses.pop();
  468. return true;
  469. }
  470. PSThreadClass* GameSpyPSMessageQueue::getThread( void )
  471. {
  472. return m_thread;
  473. }
  474. void GameSpyPSMessageQueue::trackPlayerStats( PSPlayerStats stats )
  475. {
  476. #ifdef DEBUG_LOGGING
  477. debugDumpPlayerStats( stats );
  478. DEBUG_ASSERTCRASH(stats.id != 0, ("Tracking stats with ID of 0\n"));
  479. #endif
  480. PSPlayerStats newStats;
  481. std::map<Int, PSPlayerStats>::iterator it = m_playerStats.find(stats.id);
  482. if (it != m_playerStats.end())
  483. {
  484. newStats = it->second;
  485. newStats.incorporate(stats);
  486. m_playerStats[stats.id] = newStats;
  487. }
  488. else
  489. {
  490. m_playerStats[stats.id] = stats;
  491. }
  492. }
  493. PSPlayerStats GameSpyPSMessageQueue::findPlayerStatsByID( Int id )
  494. {
  495. std::map<Int, PSPlayerStats>::iterator it = m_playerStats.find(id);
  496. if (it != m_playerStats.end())
  497. {
  498. return it->second;
  499. }
  500. PSPlayerStats empty;
  501. empty.id = 0;
  502. return empty;
  503. }
  504. //-------------------------------------------------------------------------
  505. Bool PSThreadClass::tryConnect( void )
  506. {
  507. Int result;
  508. DEBUG_LOG(("m_opCount = %d - opening connection\n", m_opCount));
  509. if (IsStatsConnected())
  510. {
  511. DEBUG_LOG(("connection already open!\n"));
  512. return true;
  513. }
  514. // this may block for 1-2 seconds (according to GS) so it's nice we're not in the UI thread :)
  515. result = InitStatsConnection(0);
  516. #ifdef DEBUG_LOGGING
  517. static const char *retValStrings[6] = {
  518. "GE_NOERROR",
  519. "GE_NOSOCKET",
  520. "GE_NODNS",
  521. "GE_NOCONNECT",
  522. "GE_BUSY",
  523. "GE_DATAERROR"
  524. };
  525. #endif // DEBUG_LOGGING
  526. if (result != GE_NOERROR)
  527. {
  528. DEBUG_LOG(("InitStatsConnection() returned %d (%s)\n", result, retValStrings[result]));
  529. return false;
  530. }
  531. else
  532. {
  533. DEBUG_LOG(("InitStatsConnection() succeeded\n"));
  534. }
  535. return true;
  536. }
  537. static void persAuthCallback(int localid, int profileid, int authenticated, char *errmsg, void *instance)
  538. {
  539. PSThreadClass *t = (PSThreadClass *)instance;
  540. DEBUG_LOG(("Auth callback: localid: %d profileid: %d auth: %d err: %s\n",localid, profileid, authenticated, errmsg));
  541. if (t)
  542. t->persAuthCallback(authenticated != 0);
  543. }
  544. Bool PSThreadClass::tryLogin( Int id, std::string nick, std::string password, std::string email )
  545. {
  546. char validate[33];
  547. DEBUG_LOG(("PSThreadClass::tryLogin id = %d, nick = %s, password = %s, email = %s\n", id, nick.c_str(), password.c_str(), email.c_str()));
  548. /***********
  549. We'll go ahead and start the authentication, using a Presence & Messaging SDK
  550. profileid / password. To generate the new validation token, we'll need to pass
  551. in the password for the profile we are authenticating.
  552. Again, if this is done in a client/server setting, with the Persistent Storage
  553. access being done on the server, and the P&M SDK is used on the client, the
  554. server will need to send the challenge (GetChallenge(NULL)) to the client, the
  555. client will create the validation token using GenerateAuth, and send it
  556. back to the server for use in PreAuthenticatePlayerPM
  557. ***********/
  558. char *munkeeHack = strdup(password.c_str()); // GenerateAuth takes a char*, not a const char* :P
  559. GenerateAuth(GetChallenge(NULL), munkeeHack, validate);
  560. free (munkeeHack);
  561. /************
  562. After we get the validation token, we pass it and the profileid of the user
  563. we are authenticating into PreAuthenticatePlayerPM.
  564. We pass the same authentication callback as for the first user, but a different
  565. localid this time.
  566. ************/
  567. m_loginOK = false;
  568. m_doneTryingToLogin = false;
  569. PreAuthenticatePlayerPM(id, id, validate, ::persAuthCallback, this);
  570. while (!m_doneTryingToLogin && IsStatsConnected())
  571. PersistThink();
  572. DEBUG_LOG(("Persistant Storage Login success %d\n", m_loginOK));
  573. return m_loginOK;
  574. }
  575. static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance)
  576. {
  577. DEBUG_LOG(("Data get callback: localid: %d profileid: %d success: %d len: %d data: %s\n",localid, profileid, success, len, data));
  578. PSThreadClass *t = (PSThreadClass *)instance;
  579. if (!t)
  580. return;
  581. t->decrOpCount();
  582. PSResponse resp;
  583. if (!success)
  584. {
  585. resp.responseType = PSResponse::PSRESPONSE_COULDNOTCONNECT;
  586. resp.player.id = profileid;
  587. TheGameSpyPSMessageQueue->addResponse(resp);
  588. if (!t->getOpCount() && !t->sawLocalPlayerData())
  589. {
  590. // we haven't gotten stats for ourselves - try again
  591. DEBUG_LOG(("Requesting retry for reading local player's stats\n"));
  592. PSRequest req;
  593. req.requestType = PSRequest::PSREQUEST_READPLAYERSTATS;
  594. req.player.id = MESSAGE_QUEUE->getLocalPlayerID();
  595. TheGameSpyPSMessageQueue->addRequest(req);
  596. }
  597. return;
  598. }
  599. if (profileid == MESSAGE_QUEUE->getLocalPlayerID())
  600. {
  601. t->gotLocalPlayerData();
  602. DEBUG_LOG(("getPersistentDataCallback() - got local player info\n"));
  603. // check if we have discons we should update on the server
  604. UserPreferences pref;
  605. AsciiString userPrefFilename;
  606. userPrefFilename.format("GeneralsOnline\\MiscPref%d.ini", MESSAGE_QUEUE->getLocalPlayerID());
  607. DEBUG_LOG(("using the file %s\n", userPrefFilename.str()));
  608. pref.load(userPrefFilename);
  609. Int addedInDesyncs2 = pref.getInt("0", 0);
  610. DEBUG_LOG(("addedInDesyncs2 = %d\n", addedInDesyncs2));
  611. if (addedInDesyncs2 < 0)
  612. addedInDesyncs2 = 10;
  613. Int addedInDesyncs3 = pref.getInt("1", 0);
  614. DEBUG_LOG(("addedInDesyncs3 = %d\n", addedInDesyncs3));
  615. if (addedInDesyncs3 < 0)
  616. addedInDesyncs3 = 10;
  617. Int addedInDesyncs4 = pref.getInt("2", 0);
  618. DEBUG_LOG(("addedInDesyncs4 = %d\n", addedInDesyncs4));
  619. if (addedInDesyncs4 < 0)
  620. addedInDesyncs4 = 10;
  621. Int addedInDiscons2 = pref.getInt("3", 0);
  622. DEBUG_LOG(("addedInDiscons2 = %d\n", addedInDiscons2));
  623. if (addedInDiscons2 < 0)
  624. addedInDiscons2 = 10;
  625. Int addedInDiscons3 = pref.getInt("4", 0);
  626. DEBUG_LOG(("addedInDiscons3 = %d\n", addedInDiscons3));
  627. if (addedInDiscons3 < 0)
  628. addedInDiscons3 = 10;
  629. Int addedInDiscons4 = pref.getInt("5", 0);
  630. DEBUG_LOG(("addedInDiscons4 = %d\n", addedInDiscons4));
  631. if (addedInDiscons4 < 0)
  632. addedInDiscons4 = 10;
  633. DEBUG_LOG(("addedInDesync=%d,%d,%d, addedInDiscon=%d,%d,%d\n",
  634. addedInDesyncs2, addedInDesyncs3, addedInDesyncs4,
  635. addedInDiscons2, addedInDiscons3, addedInDiscons4));
  636. if (addedInDesyncs2 || addedInDesyncs3 || addedInDesyncs4 || addedInDiscons2 || addedInDiscons3 || addedInDiscons4)
  637. {
  638. DEBUG_LOG(("We have a previous discon we can attempt to update! Bummer...\n"));
  639. PSRequest req;
  640. req.requestType = PSRequest::PSREQUEST_UPDATEPLAYERSTATS;
  641. req.email = MESSAGE_QUEUE->getEmail();
  642. req.nick = MESSAGE_QUEUE->getNick();
  643. req.password = MESSAGE_QUEUE->getPassword();
  644. req.player = GameSpyPSMessageQueueInterface::parsePlayerKVPairs((len)?data:"");
  645. req.player.id = profileid;
  646. req.addDesync = FALSE;
  647. req.addDiscon = FALSE;
  648. req.lastHouse = 0;
  649. TheGameSpyPSMessageQueue->addRequest(req);
  650. }
  651. }
  652. resp.responseType = PSResponse::PSRESPONSE_PLAYERSTATS;
  653. resp.player = GameSpyPSMessageQueueInterface::parsePlayerKVPairs((len)?data:"");
  654. resp.player.id = profileid;
  655. TheGameSpyPSMessageQueue->addResponse(resp);
  656. }
  657. static void setPersistentDataLocaleCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance)
  658. {
  659. DEBUG_LOG(("Data save callback: localid: %d profileid: %d success: %d\n", localid, profileid, success));
  660. PSThreadClass *t = (PSThreadClass *)instance;
  661. if (!t)
  662. return;
  663. t->decrOpCount();
  664. }
  665. static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance)
  666. {
  667. DEBUG_LOG(("Data save callback: localid: %d profileid: %d success: %d\n", localid, profileid, success));
  668. PSThreadClass *t = (PSThreadClass *)instance;
  669. if (!t)
  670. return;
  671. if (success)
  672. {
  673. UserPreferences pref;
  674. AsciiString userPrefFilename;
  675. userPrefFilename.format("GeneralsOnline\\MiscPref%d.ini", profileid);
  676. DEBUG_LOG(("setPersistentDataCallback - writing stats to file %s\n", userPrefFilename.str()));
  677. pref.load(userPrefFilename);
  678. pref.clear();
  679. pref.write();
  680. }
  681. t->decrOpCount();
  682. }
  683. struct CDAuthInfo
  684. {
  685. Bool success;
  686. Bool done;
  687. Int id;
  688. };
  689. void preAuthCDCallback(int localid, int profileid, int authenticated, char *errmsg, void *instance)
  690. {
  691. DEBUG_LOG(("preAuthCDCallback(): profileid: %d auth: %d err: %s\n", profileid, authenticated, errmsg));
  692. CDAuthInfo *authInfo = (CDAuthInfo *)instance;
  693. authInfo->success = authenticated;
  694. authInfo->done = TRUE;
  695. authInfo->id = profileid;
  696. }
  697. static void getPreorderCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance)
  698. {
  699. PSThreadClass *t = (PSThreadClass *)instance;
  700. if (!t)
  701. return;
  702. t->decrOpCount();
  703. PSResponse resp;
  704. if (!success)
  705. {
  706. DEBUG_LOG(("Failed getPreorderCallback()\n"));
  707. return;
  708. }
  709. resp.responseType = PSResponse::PSRESPONSE_PREORDER;
  710. resp.preorder = (data && strcmp(data, "\\preorder\\1") == 0);
  711. DEBUG_LOG(("getPreorderCallback() - data was '%s'\n", data));
  712. TheGameSpyPSMessageQueue->addResponse(resp);
  713. }
  714. void PSThreadClass::Thread_Function()
  715. {
  716. try {
  717. _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
  718. /*********
  719. First step, set our game authentication info
  720. We could do:
  721. strcpy(gcd_gamename,"ccgenerals");
  722. strcpy(gcd_secret_key,"h5T2f6");
  723. or
  724. strcpy(gcd_gamename,"ccgeneralsb");
  725. strcpy(gcd_secret_key,"g3T9s2");
  726. ...but this is more secure:
  727. **********/
  728. /**
  729. gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e';
  730. gcd_gamename[4]='n';gcd_gamename[5]='e';gcd_gamename[6]='r';gcd_gamename[7]='a';
  731. gcd_gamename[8]='l';gcd_gamename[9]='s';gcd_gamename[10]='b';gcd_gamename[11]='\0';
  732. gcd_secret_key[0]='g';gcd_secret_key[1]='3';gcd_secret_key[2]='T';gcd_secret_key[3]='9';
  733. gcd_secret_key[4]='s';gcd_secret_key[5]='2';gcd_secret_key[6]='\0';
  734. /**/
  735. gcd_gamename[0]='c';gcd_gamename[1]='c';gcd_gamename[2]='g';gcd_gamename[3]='e';
  736. gcd_gamename[4]='n';gcd_gamename[5]='e';gcd_gamename[6]='r';gcd_gamename[7]='a';
  737. gcd_gamename[8]='l';gcd_gamename[9]='s';gcd_gamename[10]='\0';
  738. gcd_secret_key[0]='h';gcd_secret_key[1]='5';gcd_secret_key[2]='T';gcd_secret_key[3]='2';
  739. gcd_secret_key[4]='f';gcd_secret_key[5]='6';gcd_secret_key[6]='\0';
  740. /**/
  741. //strcpy(StatsServerHostname, "sdkdev.gamespy.com");
  742. PSRequest req;
  743. while ( running )
  744. {
  745. // deal with requests
  746. if (TheGameSpyPSMessageQueue->getRequest(req))
  747. {
  748. switch (req.requestType)
  749. {
  750. case PSRequest::PSREQUEST_SENDGAMERESTOGAMESPY:
  751. {
  752. if (tryConnect())
  753. {
  754. NewGame(0);
  755. #ifdef DEBUG_LOGGING
  756. Int res =
  757. #endif // DEBUG_LOGGING
  758. SendGameSnapShot(NULL, req.results.c_str(), SNAP_FINAL);
  759. DEBUG_LOG(("Just sent game results - res was %d\n", res));
  760. FreeGame(NULL);
  761. }
  762. }
  763. break;
  764. case PSRequest::PSREQUEST_READPLAYERSTATS:
  765. {
  766. Bool initialConnection = FALSE;
  767. if (!MESSAGE_QUEUE->getLocalPlayerID())
  768. {
  769. MESSAGE_QUEUE->setLocalPlayerID(req.player.id); // first request is for ourselves
  770. MESSAGE_QUEUE->setEmail(req.email);
  771. MESSAGE_QUEUE->setNick(req.nick);
  772. MESSAGE_QUEUE->setPassword(req.password);
  773. DEBUG_LOG(("Setting email/nick/password = %s/%s/%s\n", req.email.c_str(), req.nick.c_str(), req.password.c_str()));
  774. initialConnection = TRUE;
  775. }
  776. DEBUG_LOG(("Processing PSRequest::PSREQUEST_READPLAYERSTATS\n"));
  777. if (tryConnect())
  778. {
  779. DEBUG_LOG(("Successful login\n"));
  780. incrOpCount();
  781. GetPersistDataValues(0, req.player.id, pd_public_rw, 0, "", getPersistentDataCallback, this);
  782. }
  783. else
  784. {
  785. DEBUG_LOG(("Unsuccessful login - retry=%d\n", initialConnection));
  786. PSResponse resp;
  787. resp.responseType = PSResponse::PSRESPONSE_COULDNOTCONNECT;
  788. resp.player.id = req.player.id;
  789. TheGameSpyPSMessageQueue->addResponse(resp);
  790. if (initialConnection)
  791. {
  792. // we haven't gotten stats for ourselves - try again
  793. DEBUG_LOG(("Requesting retry for reading local player's stats\n"));
  794. PSRequest req;
  795. req.requestType = PSRequest::PSREQUEST_READPLAYERSTATS;
  796. req.player.id = MESSAGE_QUEUE->getLocalPlayerID();
  797. TheGameSpyPSMessageQueue->addRequest(req);
  798. }
  799. }
  800. }
  801. break;
  802. case PSRequest::PSREQUEST_UPDATEPLAYERLOCALE:
  803. {
  804. DEBUG_LOG(("Processing PSRequest::PSREQUEST_UPDATEPLAYERLOCALE\n"));
  805. if (tryConnect() && tryLogin(req.player.id, req.nick, req.password, req.email))
  806. {
  807. char kvbuf[256];
  808. sprintf(kvbuf, "\\locale\\%d", req.player.locale);
  809. incrOpCount();
  810. SetPersistDataValues(0, req.player.id, pd_public_rw, 0, kvbuf, setPersistentDataLocaleCallback, this);
  811. }
  812. }
  813. break;
  814. case PSRequest::PSREQUEST_UPDATEPLAYERSTATS:
  815. {
  816. /*
  817. ** NOTE THAT THIS IS HIGHLY DEPENDENT ON INI ORDERING FOR THE PLAYERTEMPLATES!!!
  818. */
  819. DEBUG_LOG(("Processing PSRequest::PSREQUEST_UPDATEPLAYERSTATS\n"));
  820. UserPreferences pref;
  821. AsciiString userPrefFilename;
  822. userPrefFilename.format("GeneralsOnline\\MiscPref%d.ini", MESSAGE_QUEUE->getLocalPlayerID());
  823. DEBUG_LOG(("using the file %s\n", userPrefFilename.str()));
  824. pref.load(userPrefFilename);
  825. Int addedInDesyncs2 = pref.getInt("0", 0);
  826. DEBUG_LOG(("addedInDesyncs2 = %d\n", addedInDesyncs2));
  827. if (addedInDesyncs2 < 0)
  828. addedInDesyncs2 = 10;
  829. Int addedInDesyncs3 = pref.getInt("1", 0);
  830. DEBUG_LOG(("addedInDesyncs3 = %d\n", addedInDesyncs3));
  831. if (addedInDesyncs3 < 0)
  832. addedInDesyncs3 = 10;
  833. Int addedInDesyncs4 = pref.getInt("2", 0);
  834. DEBUG_LOG(("addedInDesyncs4 = %d\n", addedInDesyncs4));
  835. if (addedInDesyncs4 < 0)
  836. addedInDesyncs4 = 10;
  837. Int addedInDiscons2 = pref.getInt("3", 0);
  838. DEBUG_LOG(("addedInDiscons2 = %d\n", addedInDiscons2));
  839. if (addedInDiscons2 < 0)
  840. addedInDiscons2 = 10;
  841. Int addedInDiscons3 = pref.getInt("4", 0);
  842. DEBUG_LOG(("addedInDiscons3 = %d\n", addedInDiscons3));
  843. if (addedInDiscons3 < 0)
  844. addedInDiscons3 = 10;
  845. Int addedInDiscons4 = pref.getInt("5", 0);
  846. DEBUG_LOG(("addedInDiscons4 = %d\n", addedInDiscons4));
  847. if (addedInDiscons4 < 0)
  848. addedInDiscons4 = 10;
  849. DEBUG_LOG(("req.addDesync=%d, req.addDiscon=%d, addedInDesync=%d,%d,%d, addedInDiscon=%d,%d,%d\n",
  850. req.addDesync, req.addDiscon, addedInDesyncs2, addedInDesyncs3, addedInDesyncs4,
  851. addedInDiscons2, addedInDiscons3, addedInDiscons4));
  852. if (req.addDesync || req.addDiscon)
  853. {
  854. AsciiString val;
  855. if (req.lastHouse == 2)
  856. {
  857. val.format("%d", addedInDesyncs2 + req.addDesync);
  858. pref["0"] = val;
  859. val.format("%d", addedInDiscons2 + req.addDiscon);
  860. pref["3"] = val;
  861. DEBUG_LOG(("house 2 req.addDesync || req.addDiscon: %d %d\n",
  862. addedInDesyncs2 + req.addDesync, addedInDiscons2 + req.addDiscon));
  863. }
  864. else if (req.lastHouse == 3)
  865. {
  866. val.format("%d", addedInDesyncs3 + req.addDesync);
  867. pref["1"] = val;
  868. val.format("%d", addedInDiscons3 + req.addDiscon);
  869. pref["4"] = val;
  870. DEBUG_LOG(("house 3 req.addDesync || req.addDiscon: %d %d\n",
  871. addedInDesyncs3 + req.addDesync, addedInDiscons3 + req.addDiscon));
  872. }
  873. else
  874. {
  875. val.format("%d", addedInDesyncs4 + req.addDesync);
  876. pref["2"] = val;
  877. val.format("%d", addedInDiscons4 + req.addDiscon);
  878. pref["5"] = val;
  879. DEBUG_LOG(("house 4 req.addDesync || req.addDiscon: %d %d\n",
  880. addedInDesyncs4 + req.addDesync, addedInDiscons4 + req.addDiscon));
  881. }
  882. pref.write();
  883. if (req.password.size() == 0)
  884. return;
  885. }
  886. if (!req.player.id)
  887. {
  888. DEBUG_LOG(("Bailing because ID is NULL!\n"));
  889. return;
  890. }
  891. req.player.desyncs[2] += addedInDesyncs2;
  892. req.player.games[2] += addedInDesyncs2;
  893. req.player.discons[2] += addedInDiscons2;
  894. req.player.games[2] += addedInDiscons2;
  895. req.player.desyncs[3] += addedInDesyncs3;
  896. req.player.games[3] += addedInDesyncs3;
  897. req.player.discons[3] += addedInDiscons3;
  898. req.player.games[3] += addedInDiscons3;
  899. req.player.desyncs[4] += addedInDesyncs4;
  900. req.player.games[4] += addedInDesyncs4;
  901. req.player.discons[4] += addedInDiscons4;
  902. req.player.games[4] += addedInDiscons4;
  903. DEBUG_LOG(("House2: %d/%d/%d, House3: %d/%d/%d, House4: %d/%d/%d\n",
  904. req.player.desyncs[2], req.player.discons[2], req.player.games[2],
  905. req.player.desyncs[3], req.player.discons[3], req.player.games[3],
  906. req.player.desyncs[4], req.player.discons[4], req.player.games[4]
  907. ));
  908. if (tryConnect() && tryLogin(req.player.id, req.nick, req.password, req.email))
  909. {
  910. DEBUG_LOG(("Logged in!\n"));
  911. if (TheGameSpyPSMessageQueue)
  912. TheGameSpyPSMessageQueue->trackPlayerStats(req.player);
  913. char *munkeeHack = strdup(GameSpyPSMessageQueueInterface::formatPlayerKVPairs(req.player).c_str()); // GS takes a char* for some reason
  914. incrOpCount();
  915. DEBUG_LOG(("Setting values %s\n", munkeeHack));
  916. SetPersistDataValues(0, req.player.id, pd_public_rw, 0, munkeeHack, setPersistentDataCallback, this);
  917. free(munkeeHack);
  918. }
  919. else
  920. {
  921. DEBUG_LOG(("Cannot connect!\n"));
  922. //if (IsStatsConnected())
  923. //CloseStatsConnection();
  924. }
  925. }
  926. break;
  927. case PSRequest::PSREQUEST_READCDKEYSTATS:
  928. {
  929. DEBUG_LOG(("Processing PSRequest::PSREQUEST_READCDKEYSTATS\n"));
  930. if (tryConnect())
  931. {
  932. incrOpCount();
  933. CDAuthInfo cdAuthInfo;
  934. cdAuthInfo.done = FALSE;
  935. cdAuthInfo.success = FALSE;
  936. cdAuthInfo.id = 0;
  937. char cdkeyHash[33] = "";
  938. char validationToken[33] = "";
  939. char *munkeeHack = strdup(req.cdkey.c_str()); // GenerateAuth takes a char*, not a const char* :P
  940. GenerateAuth(GetChallenge(NULL), munkeeHack, validationToken); // validation token
  941. GenerateAuth("", munkeeHack, cdkeyHash); // cdkey hash
  942. free (munkeeHack);
  943. PreAuthenticatePlayerCD( 0, "preorder", cdkeyHash, validationToken, preAuthCDCallback , &cdAuthInfo);
  944. while (running && IsStatsConnected() && !cdAuthInfo.done)
  945. PersistThink();
  946. DEBUG_LOG(("Looking for preorder status for %d (success=%d, done=%d) from CDKey %s with hash %s\n",
  947. cdAuthInfo.id, cdAuthInfo.success, cdAuthInfo.done, req.cdkey.c_str(), cdkeyHash));
  948. if (cdAuthInfo.done && cdAuthInfo.success)
  949. GetPersistDataValues(0, cdAuthInfo.id, pd_public_ro, 0, "\\preorder", getPreorderCallback, this);
  950. else
  951. decrOpCount();
  952. }
  953. }
  954. break;
  955. }
  956. }
  957. // update the network
  958. if (IsStatsConnected())
  959. {
  960. PersistThink();
  961. if (m_opCount <= 0)
  962. {
  963. DEBUG_ASSERTCRASH(m_opCount == 0, ("Negative operations pending!!!"));
  964. DEBUG_LOG(("m_opCount = %d - closing connection\n", m_opCount));
  965. CloseStatsConnection();
  966. m_opCount = 0;
  967. }
  968. }
  969. // end our timeslice
  970. Switch_Thread();
  971. }
  972. if (IsStatsConnected())
  973. CloseStatsConnection();
  974. } catch ( ... ) {
  975. DEBUG_CRASH(("Exception in storage thread!"));
  976. }
  977. }
  978. //-------------------------------------------------------------------------
  979. PSPlayerStats::PSPlayerStats( void )
  980. {
  981. reset();
  982. }
  983. void PSPlayerStats::reset( void )
  984. {
  985. id = 0;
  986. locale = 0;
  987. gamesAsRandom = 0;
  988. lastFPS = 0;
  989. lastGeneral = 0;
  990. gamesInRowWithLastGeneral = 0;
  991. builtNuke = 0;
  992. builtSCUD = 0;
  993. builtParticleCannon = 0;
  994. challengeMedals = 0;
  995. battleHonors = 0;
  996. winsInARow = 0;
  997. maxWinsInARow = 0;
  998. lossesInARow = 0;
  999. maxLossesInARow = 0;
  1000. disconsInARow = 0;
  1001. maxDisconsInARow = 0;
  1002. desyncsInARow = 0;
  1003. maxDesyncsInARow = 0;
  1004. lastLadderPort = 0;
  1005. //Added By Sadullah Nader
  1006. maxQMwinsInARow = 0;
  1007. QMwinsInARow = 0;
  1008. //
  1009. }
  1010. //-------------------------------------------------------------------------
  1011. #define CHECK(x) if (k == #x && generalMarker >= 0) { s.x[generalMarker] = atoi(v.c_str()); continue; }
  1012. PSPlayerStats GameSpyPSMessageQueueInterface::parsePlayerKVPairs( std::string kvPairs )
  1013. {
  1014. PSPlayerStats s;
  1015. kvPairs.append("\\");
  1016. Int offset = 0;
  1017. while (1)
  1018. {
  1019. Int firstMarker = kvPairs.find_first_of('\\', offset);
  1020. if (firstMarker < 0)
  1021. break;
  1022. Int secondMarker = kvPairs.find_first_of('\\', firstMarker + 1);
  1023. if (secondMarker < 0)
  1024. break;
  1025. Int thirdMarker = kvPairs.find_first_of('\\', secondMarker + 1);
  1026. if (thirdMarker < 0)
  1027. break;
  1028. Int generalMarker = kvPairs.find_last_not_of("0123456789", secondMarker - 1);
  1029. std::string k, v, g;
  1030. if (generalMarker == secondMarker - 1)
  1031. {
  1032. k = kvPairs.substr(firstMarker + 1, secondMarker - firstMarker - 1);
  1033. generalMarker = -1;
  1034. }
  1035. else
  1036. {
  1037. k = kvPairs.substr(firstMarker + 1, generalMarker - firstMarker);
  1038. g = kvPairs.substr(generalMarker + 1, secondMarker - generalMarker - 1);
  1039. generalMarker = atoi(g.c_str());
  1040. }
  1041. v = kvPairs.substr(secondMarker + 1, thirdMarker - secondMarker - 1);
  1042. //DEBUG_LOG(("%d [%s] [%s]\n", generalMarker, k.c_str(), v.c_str()));
  1043. offset = thirdMarker - 1;
  1044. CHECK(wins);
  1045. CHECK(losses);
  1046. CHECK(games);
  1047. CHECK(duration);
  1048. CHECK(unitsKilled);
  1049. CHECK(unitsLost);
  1050. CHECK(unitsBuilt);
  1051. CHECK(buildingsKilled);
  1052. CHECK(buildingsLost);
  1053. CHECK(buildingsBuilt);
  1054. CHECK(earnings);
  1055. CHECK(techCaptured);
  1056. CHECK(discons);
  1057. CHECK(desyncs);
  1058. CHECK(surrenders);
  1059. CHECK(gamesOf2p);
  1060. CHECK(gamesOf3p);
  1061. CHECK(gamesOf4p);
  1062. CHECK(gamesOf5p);
  1063. CHECK(gamesOf6p);
  1064. CHECK(gamesOf7p);
  1065. CHECK(gamesOf8p);
  1066. CHECK(customGames);
  1067. CHECK(QMGames);
  1068. if (k == "locale" && generalMarker < 0)
  1069. {
  1070. s.locale = atoi(v.c_str());
  1071. continue;
  1072. }
  1073. if (k == "random" && generalMarker < 0)
  1074. {
  1075. s.gamesAsRandom = atoi(v.c_str());
  1076. continue;
  1077. }
  1078. if (k == "options" && generalMarker < 0)
  1079. {
  1080. s.options = v;
  1081. continue;
  1082. }
  1083. if (k == "systemSpec" && generalMarker < 0)
  1084. {
  1085. s.systemSpec = v;
  1086. continue;
  1087. }
  1088. if (k == "fps" && generalMarker < 0)
  1089. {
  1090. s.lastFPS = atof(v.c_str());
  1091. continue;
  1092. }
  1093. if (k == "lastGeneral" && generalMarker < 0)
  1094. {
  1095. s.lastGeneral = atoi(v.c_str());
  1096. continue;
  1097. }
  1098. if (k == "genInRow" && generalMarker < 0)
  1099. {
  1100. s.gamesInRowWithLastGeneral = atoi(v.c_str());
  1101. continue;
  1102. }
  1103. if (k == "builtNuke" && generalMarker < 0)
  1104. {
  1105. s.builtNuke = atoi(v.c_str());
  1106. continue;
  1107. }
  1108. if (k == "builtSCUD" && generalMarker < 0)
  1109. {
  1110. s.builtSCUD = atoi(v.c_str());
  1111. continue;
  1112. }
  1113. if (k == "builtCannon" && generalMarker < 0)
  1114. {
  1115. s.builtParticleCannon = atoi(v.c_str());
  1116. continue;
  1117. }
  1118. if (k == "challenge" && generalMarker < 0)
  1119. {
  1120. s.challengeMedals = atoi(v.c_str());
  1121. continue;
  1122. }
  1123. if (k == "battle" && generalMarker < 0)
  1124. {
  1125. s.battleHonors = atoi(v.c_str());
  1126. continue;
  1127. }
  1128. if (k == "WinRow" && generalMarker < 0)
  1129. {
  1130. s.winsInARow = atoi(v.c_str());
  1131. continue;
  1132. }
  1133. if (k == "WinRowMax" && generalMarker < 0)
  1134. {
  1135. s.maxWinsInARow = atoi(v.c_str());
  1136. continue;
  1137. }
  1138. if (k == "LossRow" && generalMarker < 0)
  1139. {
  1140. s.lossesInARow = atoi(v.c_str());
  1141. continue;
  1142. }
  1143. if (k == "LossRowMax" && generalMarker < 0)
  1144. {
  1145. s.maxLossesInARow = atoi(v.c_str());
  1146. continue;
  1147. }
  1148. if (k == "DSRow" && generalMarker < 0)
  1149. {
  1150. s.desyncsInARow = atoi(v.c_str());
  1151. continue;
  1152. }
  1153. if (k == "DSRowMax" && generalMarker < 0)
  1154. {
  1155. s.maxDesyncsInARow = atoi(v.c_str());
  1156. continue;
  1157. }
  1158. if (k == "DCRow" && generalMarker < 0)
  1159. {
  1160. s.disconsInARow = atoi(v.c_str());
  1161. continue;
  1162. }
  1163. if (k == "DCRowMax" && generalMarker < 0)
  1164. {
  1165. s.maxDisconsInARow = atoi(v.c_str());
  1166. continue;
  1167. }
  1168. if (k == "ladderPort" && generalMarker < 0)
  1169. {
  1170. s.lastLadderPort = atoi(v.c_str());
  1171. continue;
  1172. }
  1173. if (k == "ladderHost" && generalMarker < 0)
  1174. {
  1175. s.lastLadderHost = v;
  1176. continue;
  1177. }
  1178. //DEBUG_ASSERTCRASH(generalMarker >= 0, ("Unknown KV Pair in persistent storage: [%s] = [%s]\n", k.c_str(), v.c_str()));
  1179. //DEBUG_ASSERTCRASH(generalMarker < 0, ("Unknown KV Pair in persistent storage for PlayerTemplate %d: [%s] = [%s]\n", generalMarker, k.c_str(), v.c_str()));
  1180. }
  1181. return s;
  1182. }
  1183. #define ITERATE_OVER(x) for (it = stats.x.begin(); it != stats.x.end(); ++it) \
  1184. { \
  1185. if (it->second > 0) \
  1186. { \
  1187. sprintf(kvbuf, "\\" #x "%d\\%d", it->first, it->second); \
  1188. s.append(kvbuf); \
  1189. } \
  1190. }
  1191. std::string GameSpyPSMessageQueueInterface::formatPlayerKVPairs( PSPlayerStats stats )
  1192. {
  1193. char kvbuf[256];
  1194. std::string s = "";
  1195. PerGeneralMap::iterator it;
  1196. ITERATE_OVER(wins);
  1197. ITERATE_OVER(losses);
  1198. ITERATE_OVER(games);
  1199. ITERATE_OVER(duration);
  1200. ITERATE_OVER(unitsKilled);
  1201. ITERATE_OVER(unitsLost);
  1202. ITERATE_OVER(unitsBuilt);
  1203. ITERATE_OVER(buildingsKilled);
  1204. ITERATE_OVER(buildingsLost);
  1205. ITERATE_OVER(buildingsBuilt);
  1206. ITERATE_OVER(earnings);
  1207. ITERATE_OVER(techCaptured);
  1208. //GS Report all disconnects, even if zero, because might have been
  1209. //previously reported as 1 by updateAdditionalGameSpyDisconnections
  1210. // ITERATE_OVER(discons);
  1211. for (Int ptIdx = 0; ptIdx < ThePlayerTemplateStore->getPlayerTemplateCount(); ++ptIdx)
  1212. {
  1213. // const PlayerTemplate* pTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(ptIdx);
  1214. // const GeneralPersona* pGeneral = TheChallengeGenerals->getGeneralByTemplateName(pTemplate->getName());
  1215. // BOOL isReported = pGeneral ? pGeneral->isStartingEnabled() : FALSE;
  1216. // if( !isReported )
  1217. // continue; //don't report unplayable templates (observer, boss, etc.)
  1218. sprintf(kvbuf, "\\discons%d\\%d", ptIdx, stats.discons[ptIdx]);
  1219. s.append(kvbuf);
  1220. }
  1221. ITERATE_OVER(desyncs);
  1222. ITERATE_OVER(surrenders);
  1223. ITERATE_OVER(gamesOf2p);
  1224. ITERATE_OVER(gamesOf3p);
  1225. ITERATE_OVER(gamesOf4p);
  1226. ITERATE_OVER(gamesOf5p);
  1227. ITERATE_OVER(gamesOf6p);
  1228. ITERATE_OVER(gamesOf7p);
  1229. ITERATE_OVER(gamesOf8p);
  1230. ITERATE_OVER(customGames);
  1231. ITERATE_OVER(QMGames);
  1232. if (stats.locale > 0)
  1233. {
  1234. sprintf(kvbuf, "\\locale\\%d", stats.locale);
  1235. s.append(kvbuf);
  1236. }
  1237. if (stats.gamesAsRandom > 0)
  1238. {
  1239. sprintf(kvbuf, "\\random\\%d", stats.gamesAsRandom);
  1240. s.append(kvbuf);
  1241. }
  1242. if (stats.options.length())
  1243. {
  1244. _snprintf(kvbuf, 256, "\\options\\%s", stats.options.c_str());
  1245. kvbuf[255] = 0;
  1246. s.append(kvbuf);
  1247. }
  1248. if (stats.systemSpec.length())
  1249. {
  1250. _snprintf(kvbuf, 256, "\\systemSpec\\%s", stats.systemSpec.c_str());
  1251. kvbuf[255] = 0;
  1252. s.append(kvbuf);
  1253. }
  1254. if (stats.lastFPS > 0.0f)
  1255. {
  1256. sprintf(kvbuf, "\\fps\\%g", stats.lastFPS);
  1257. s.append(kvbuf);
  1258. }
  1259. if (stats.lastGeneral >= 0)
  1260. {
  1261. sprintf(kvbuf, "\\lastGeneral\\%d", stats.lastGeneral);
  1262. s.append(kvbuf);
  1263. }
  1264. if (stats.gamesInRowWithLastGeneral >= 0)
  1265. {
  1266. sprintf(kvbuf, "\\genInRow\\%d", stats.gamesInRowWithLastGeneral);
  1267. s.append(kvbuf);
  1268. }
  1269. if (stats.builtParticleCannon >= 0)
  1270. {
  1271. sprintf(kvbuf, "\\builtCannon\\%d", stats.builtParticleCannon);
  1272. s.append(kvbuf);
  1273. }
  1274. if (stats.builtNuke >= 0)
  1275. {
  1276. sprintf(kvbuf, "\\builtNuke\\%d", stats.builtNuke);
  1277. s.append(kvbuf);
  1278. }
  1279. if (stats.builtSCUD >= 0)
  1280. {
  1281. sprintf(kvbuf, "\\builtSCUD\\%d", stats.builtSCUD);
  1282. s.append(kvbuf);
  1283. }
  1284. if (stats.challengeMedals > 0)
  1285. {
  1286. sprintf(kvbuf, "\\challenge\\%d", stats.challengeMedals);
  1287. s.append(kvbuf);
  1288. }
  1289. if (stats.battleHonors > 0)
  1290. {
  1291. sprintf(kvbuf, "\\battle\\%d", stats.battleHonors);
  1292. s.append(kvbuf);
  1293. }
  1294. //if (stats.winsInARow > 0)
  1295. {
  1296. sprintf(kvbuf, "\\WinRow\\%d", stats.winsInARow);
  1297. s.append(kvbuf);
  1298. }
  1299. if (stats.maxWinsInARow > 0)
  1300. {
  1301. sprintf(kvbuf, "\\WinRowMax\\%d", stats.maxWinsInARow);
  1302. s.append(kvbuf);
  1303. }
  1304. //if (stats.lossesInARow > 0)
  1305. {
  1306. sprintf(kvbuf, "\\LossRow\\%d", stats.lossesInARow);
  1307. s.append(kvbuf);
  1308. }
  1309. if (stats.maxLossesInARow > 0)
  1310. {
  1311. sprintf(kvbuf, "\\LossRowMax\\%d", stats.maxLossesInARow);
  1312. s.append(kvbuf);
  1313. }
  1314. //if (stats.disconsInARow > 0)
  1315. {
  1316. sprintf(kvbuf, "\\DCRow\\%d", stats.disconsInARow);
  1317. s.append(kvbuf);
  1318. }
  1319. if (stats.maxDisconsInARow > 0)
  1320. {
  1321. sprintf(kvbuf, "\\DCRowMax\\%d", stats.maxDisconsInARow);
  1322. s.append(kvbuf);
  1323. }
  1324. //if (stats.desyncsInARow > 0)
  1325. {
  1326. sprintf(kvbuf, "\\DSRow\\%d", stats.desyncsInARow);
  1327. s.append(kvbuf);
  1328. }
  1329. if (stats.maxDesyncsInARow > 0)
  1330. {
  1331. sprintf(kvbuf, "\\DSRowMax\\%d", stats.maxDesyncsInARow);
  1332. s.append(kvbuf);
  1333. }
  1334. if (stats.lastLadderPort > 0)
  1335. {
  1336. sprintf(kvbuf, "\\ladderPort\\%d", stats.lastLadderPort);
  1337. s.append(kvbuf);
  1338. }
  1339. if (stats.lastLadderHost.length())
  1340. {
  1341. _snprintf(kvbuf, 256, "\\ladderHost\\%s", stats.lastLadderHost.c_str());
  1342. kvbuf[255] = 0;
  1343. s.append(kvbuf);
  1344. }
  1345. DEBUG_LOG(("Formatted persistent values as '%s'\n", s.c_str()));
  1346. return s;
  1347. }
  1348. //-------------------------------------------------------------------------