GameSpyPersistentStorage.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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: GameSpyPersistentStorage.cpp //////////////////////////////////////////////////////
  24. // GameSpy Persistent Storage callbacks, utils, etc
  25. // Author: Matthew D. Campbell, March 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "GameSpy/gstats/gpersist.h"
  28. #include "GameClient/Shell.h"
  29. #include "GameClient/MessageBox.h"
  30. #include "GameNetwork/GameSpy.h"
  31. #include "GameNetwork/GameSpyGP.h"
  32. #include "GameNetwork/GameSpyPersistentStorage.h"
  33. #include "GameNetwork/GameSpyThread.h"
  34. static Bool isProfileAuthorized = false;
  35. static Bool gameSpyInitPersistentStorageConnection( void );
  36. static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance);
  37. static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance);
  38. class GameSpyPlayerInfo : public GameSpyPlayerInfoInterface
  39. {
  40. public:
  41. GameSpyPlayerInfo() { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; m_shouldDisconnect = false; }
  42. virtual ~GameSpyPlayerInfo() { reset(); }
  43. virtual void init( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
  44. virtual void reset( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
  45. virtual void update( void );
  46. virtual AsciiString getLocale( void ) { return m_locale; }
  47. virtual Int getWins( void ) { return m_wins; }
  48. virtual Int getLosses( void ) { return m_losses; }
  49. virtual void setLocale( AsciiString locale, Bool setOnServer );
  50. virtual void setWins( Int wins, Bool setOnServer );
  51. virtual void setLosses( Int losses, Bool setOnServer );
  52. virtual void readFromServer( void );
  53. virtual void threadReadFromServer( void );
  54. virtual void threadSetLocale( AsciiString val );
  55. virtual void threadSetWins ( AsciiString val );
  56. virtual void threadSetLosses( AsciiString val );
  57. void queueDisconnect( void ) { MutexClass::LockClass m(TheGameSpyMutex); if (IsStatsConnected()) m_shouldDisconnect = true; else m_shouldDisconnect = false; }
  58. private:
  59. void setValue( AsciiString key, AsciiString val, Bool setOnServer );
  60. AsciiString m_locale;
  61. Int m_wins;
  62. Int m_losses;
  63. Int m_operationCount;
  64. Bool m_shouldDisconnect;
  65. };
  66. void GameSpyPlayerInfo::update( void )
  67. {
  68. if (IsStatsConnected())
  69. {
  70. if (m_shouldDisconnect)
  71. {
  72. DEBUG_LOG(("Persistent Storage close\n"));
  73. CloseStatsConnection();
  74. }
  75. else
  76. {
  77. PersistThink();
  78. }
  79. }
  80. }
  81. void GameSpyPlayerInfo::readFromServer( void )
  82. {
  83. TheGameSpyThread->queueReadPersistentStatsFromServer();
  84. }
  85. void GameSpyPlayerInfo::threadReadFromServer( void )
  86. {
  87. MutexClass::LockClass m(TheGameSpyMutex);
  88. if (gameSpyInitPersistentStorageConnection())
  89. {
  90. // get persistent info
  91. m_operationCount++;
  92. DEBUG_LOG(("GameSpyPlayerInfo::readFromServer() operation count = %d\n", m_operationCount));
  93. GetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, "\\locale\\wins\\losses", getPersistentDataCallback, &m_operationCount);
  94. }
  95. else
  96. {
  97. //TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
  98. //TheShell->pop();
  99. //TheShell->push("Menus/WOLWelcomeMenu.wnd");
  100. }
  101. }
  102. void GameSpyPlayerInfo::setLocale( AsciiString locale, Bool setOnServer )
  103. {
  104. m_locale = locale;
  105. if (!TheGameSpyChat->getProfileID() || !setOnServer)
  106. return;
  107. setValue("locale", m_locale, setOnServer);
  108. }
  109. void GameSpyPlayerInfo::setWins( Int wins, Bool setOnServer )
  110. {
  111. m_wins = wins;
  112. if (!TheGameSpyChat->getProfileID() || !setOnServer)
  113. return;
  114. AsciiString winStr;
  115. winStr.format("%d", wins);
  116. setValue("wins", winStr, setOnServer);
  117. }
  118. void GameSpyPlayerInfo::setLosses( Int losses, Bool setOnServer )
  119. {
  120. m_losses = losses;
  121. if (!TheGameSpyChat->getProfileID() || !setOnServer)
  122. return;
  123. AsciiString lossesStr;
  124. lossesStr.format("%d", losses);
  125. setValue("losses", lossesStr, setOnServer);
  126. }
  127. void GameSpyPlayerInfo::setValue( AsciiString key, AsciiString val, Bool setOnServer )
  128. {
  129. if (!setOnServer)
  130. return;
  131. if (key == "locale")
  132. TheGameSpyThread->queueUpdateLocale(val);
  133. else if (key == "wins")
  134. TheGameSpyThread->queueUpdateWins(val);
  135. else if (key == "losses")
  136. TheGameSpyThread->queueUpdateLosses(val);
  137. }
  138. void GameSpyPlayerInfo::threadSetLocale( AsciiString val )
  139. {
  140. MutexClass::LockClass m(TheGameSpyMutex);
  141. if (!gameSpyInitPersistentStorageConnection())
  142. return;
  143. // set locale info
  144. AsciiString key = "locale";
  145. AsciiString str;
  146. str.format("\\%s\\%s", key.str(), val.str());
  147. char *writable = strdup(str.str());
  148. m_operationCount++;
  149. DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
  150. SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
  151. free(writable);
  152. }
  153. void GameSpyPlayerInfo::threadSetWins( AsciiString val )
  154. {
  155. MutexClass::LockClass m(TheGameSpyMutex);
  156. if (!gameSpyInitPersistentStorageConnection())
  157. return;
  158. // set win info
  159. AsciiString key = "wins";
  160. AsciiString str;
  161. str.format("\\%s\\%s", key.str(), val.str());
  162. char *writable = strdup(str.str());
  163. m_operationCount++;
  164. DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
  165. SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
  166. free(writable);
  167. }
  168. void GameSpyPlayerInfo::threadSetLosses( AsciiString val )
  169. {
  170. MutexClass::LockClass m(TheGameSpyMutex);
  171. if (!gameSpyInitPersistentStorageConnection())
  172. return;
  173. // set loss info
  174. AsciiString key = "losses";
  175. AsciiString str;
  176. str.format("\\%s\\%s", key.str(), val.str());
  177. char *writable = strdup(str.str());
  178. m_operationCount++;
  179. DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
  180. SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
  181. free(writable);
  182. }
  183. GameSpyPlayerInfoInterface *TheGameSpyPlayerInfo = NULL;
  184. GameSpyPlayerInfoInterface *createGameSpyPlayerInfo( void )
  185. {
  186. return NEW GameSpyPlayerInfo;
  187. }
  188. static void persAuthCallback(int localid, int profileid, int authenticated, char *errmsg, void *instance)
  189. {
  190. DEBUG_LOG(("Auth callback: localid: %d profileid: %d auth: %d err: %s\n",localid, profileid, authenticated, errmsg));
  191. isProfileAuthorized = (authenticated != 0);
  192. }
  193. static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance)
  194. {
  195. DEBUG_LOG(("Data get callback: localid: %d profileid: %d success: %d len: %d data: %s\n",localid, profileid, success, len, data));
  196. if (!TheGameSpyPlayerInfo)
  197. {
  198. //TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
  199. //TheShell->pop();
  200. //TheShell->push("Menus/WOLWelcomeMenu.wnd");
  201. return;
  202. }
  203. AsciiString str = data;
  204. AsciiString key, val;
  205. while (!str.isEmpty())
  206. {
  207. str.nextToken(&key, "\\");
  208. str.nextToken(&val, "\\");
  209. if (!key.isEmpty() && !val.isEmpty())
  210. {
  211. if (!key.compareNoCase("locale"))
  212. {
  213. TheGameSpyPlayerInfo->setLocale(val, false);
  214. }
  215. else if (!key.compareNoCase("wins"))
  216. {
  217. TheGameSpyPlayerInfo->setWins(atoi(val.str()), false);
  218. }
  219. else if (!key.compareNoCase("losses"))
  220. {
  221. TheGameSpyPlayerInfo->setLosses(atoi(val.str()), false);
  222. }
  223. }
  224. }
  225. // decrement count of active operations
  226. Int *opCount = (Int *)instance;
  227. (*opCount) --;
  228. DEBUG_LOG(("getPersistentDataCallback() operation count = %d\n", (*opCount)));
  229. if (!*opCount)
  230. {
  231. DEBUG_LOG(("getPersistentDataCallback() queue disconnect\n"));
  232. ((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
  233. }
  234. const char *keys[3] = { "locale", "wins", "losses" };
  235. char valueStrings[3][20];
  236. char *values[3] = { valueStrings[0], valueStrings[1], valueStrings[2] };
  237. _snprintf(values[0], 20, "%s", TheGameSpyPlayerInfo->getLocale().str());
  238. _snprintf(values[1], 20, "%d", TheGameSpyPlayerInfo->getWins());
  239. _snprintf(values[2], 20, "%d", TheGameSpyPlayerInfo->getLosses());
  240. peerSetGlobalKeys(TheGameSpyChat->getPeer(), 3, (const char **)keys, (const char **)values);
  241. peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), GroupRoom, 3, keys, PEERTrue);
  242. peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), StagingRoom, 3, keys, PEERTrue);
  243. // choose next screen
  244. if (TheGameSpyPlayerInfo->getLocale().isEmpty())
  245. {
  246. TheGameSpyThread->setShowLocaleSelect(true);
  247. }
  248. }
  249. static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance)
  250. {
  251. DEBUG_LOG(("Data save callback: localid: %d profileid: %d success: %d\n", localid, profileid, success));
  252. Int *opCount = (Int *)instance;
  253. (*opCount) --;
  254. DEBUG_LOG(("setPersistentDataCallback() operation count = %d\n", (*opCount)));
  255. if (!*opCount)
  256. {
  257. DEBUG_LOG(("setPersistentDataCallback() queue disconnect\n"));
  258. ((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
  259. }
  260. }
  261. static Bool gameSpyInitPersistentStorageConnection( void )
  262. {
  263. if (IsStatsConnected())
  264. return true;
  265. isProfileAuthorized = false;
  266. Int result;
  267. /*********
  268. First step, set our game authentication info
  269. We could do:
  270. strcpy(gcd_gamename,"gmtest");
  271. strcpy(gcd_secret_key,"HA6zkS");
  272. ...but this is more secure:
  273. **********/
  274. gcd_gamename[0]='g';gcd_gamename[1]='m';gcd_gamename[2]='t';gcd_gamename[3]='e';
  275. gcd_gamename[4]='s';gcd_gamename[5]='t';gcd_gamename[6]='\0';
  276. gcd_secret_key[0]='H';gcd_secret_key[1]='A';gcd_secret_key[2]='6';gcd_secret_key[3]='z';
  277. gcd_secret_key[4]='k';gcd_secret_key[5]='S';gcd_secret_key[6]='\0';
  278. /*********
  279. Next, open the stats connection. This may block for
  280. a 1-2 seconds, so it should be done before the actual game starts.
  281. **********/
  282. result = InitStatsConnection(0);
  283. if (result != GE_NOERROR)
  284. {
  285. DEBUG_LOG(("InitStatsConnection returned %d\n",result));
  286. return isProfileAuthorized;
  287. }
  288. if (TheGameSpyChat->getProfileID())
  289. {
  290. char validate[33];
  291. /***********
  292. We'll go ahead and start the authentication, using a Presence & Messaging SDK
  293. profileid / password. To generate the new validation token, we'll need to pass
  294. in the password for the profile we are authenticating.
  295. Again, if this is done in a client/server setting, with the Persistent Storage
  296. access being done on the server, and the P&M SDK is used on the client, the
  297. server will need to send the challenge (GetChallenge(NULL)) to the client, the
  298. client will create the validation token using GenerateAuth, and send it
  299. back to the server for use in PreAuthenticatePlayerPM
  300. ***********/
  301. char *munkeeHack = strdup(TheGameSpyChat->getPassword().str()); // GenerateAuth takes a char*, not a const char* :P
  302. GenerateAuth(GetChallenge(NULL), munkeeHack, validate);
  303. free (munkeeHack);
  304. /************
  305. After we get the validation token, we pass it and the profileid of the user
  306. we are authenticating into PreAuthenticatePlayerPM.
  307. We pass the same authentication callback as for the first user, but a different
  308. localid this time.
  309. ************/
  310. PreAuthenticatePlayerPM(0, TheGameSpyChat->getProfileID(), validate, persAuthCallback, NULL);
  311. }
  312. else
  313. {
  314. return isProfileAuthorized;
  315. }
  316. UnsignedInt timeoutTime = timeGetTime() + 5000;
  317. while (!isProfileAuthorized && timeGetTime() < timeoutTime && IsStatsConnected())
  318. {
  319. PersistThink();
  320. msleep(10);
  321. }
  322. DEBUG_LOG(("Persistent Storage connect: %d\n", isProfileAuthorized));
  323. return isProfileAuthorized;
  324. }