PersistentStorageThread.cpp 39 KB

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