MainMenuUtils.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  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. ///////////////////////////////////////////////////////////////////////////////////////
  24. // FILE: MainMenuUtils.cpp
  25. // Author: Matthew D. Campbell, Sept 2002
  26. // Description: GameSpy version check, patch download, etc utils
  27. ///////////////////////////////////////////////////////////////////////////////////////
  28. // INCLUDES ///////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include <fcntl.h>
  31. //#include "Common/Registry.h"
  32. #include "Common/UserPreferences.h"
  33. #include "Common/Version.h"
  34. #include "GameClient/GameText.h"
  35. #include "GameClient/MessageBox.h"
  36. #include "GameClient/Shell.h"
  37. #include "GameLogic/ScriptEngine.h"
  38. #include "GameClient/ShellHooks.h"
  39. #include "GameSpy/ghttp/ghttp.h"
  40. #include "GameNetwork/DownloadManager.h"
  41. #include "GameNetwork/GameSpy/BuddyThread.h"
  42. #include "GameNetwork/GameSpy/MainMenuUtils.h"
  43. #include "GameNetwork/GameSpy/PeerDefs.h"
  44. #include "GameNetwork/GameSpy/PeerThread.h"
  45. #include "WWDownload/Registry.h"
  46. #include "WWDownload/URLBuilder.h"
  47. #ifdef _INTERNAL
  48. // for occasional debugging...
  49. //#pragma optimize("", off)
  50. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  51. #endif
  52. ///////////////////////////////////////////////////////////////////////////////////////
  53. static Bool checkingForPatchBeforeGameSpy = FALSE;
  54. static Int checksLeftBeforeOnline = 0;
  55. static Int timeThroughOnline = 0; // used to avoid having old callbacks cause problems
  56. static Bool mustDownloadPatch = FALSE;
  57. static Bool cantConnectBeforeOnline = FALSE;
  58. static std::list<QueuedDownload> queuedDownloads;
  59. static char *MOTDBuffer = NULL;
  60. static char *configBuffer = NULL;
  61. GameWindow *onlineCancelWindow = NULL;
  62. static Bool s_asyncDNSThreadDone = TRUE;
  63. static Bool s_asyncDNSThreadSucceeded = FALSE;
  64. static Bool s_asyncDNSLookupInProgress = FALSE;
  65. static HANDLE s_asyncDNSThreadHandle = NULL;
  66. enum {
  67. LOOKUP_INPROGRESS,
  68. LOOKUP_FAILED,
  69. LOOKUP_SUCCEEDED,
  70. };
  71. ///////////////////////////////////////////////////////////////////////////////////////
  72. static void startOnline( void );
  73. static void reallyStartPatchCheck( void );
  74. ///////////////////////////////////////////////////////////////////////////////////////
  75. // someone has hit a button allowing downloads to start
  76. void StartDownloadingPatches( void )
  77. {
  78. if (queuedDownloads.empty())
  79. {
  80. HandleCanceledDownload();
  81. return;
  82. }
  83. WindowLayout *layout;
  84. layout = TheWindowManager->winCreateLayout( AsciiString( "Menus/DownloadMenu.wnd" ) );
  85. layout->runInit();
  86. layout->hide( FALSE );
  87. layout->bringForward();
  88. HandleCanceledDownload(FALSE);
  89. DEBUG_ASSERTCRASH(TheDownloadManager, ("No download manager!"));
  90. if (TheDownloadManager)
  91. {
  92. std::list<QueuedDownload>::iterator it = queuedDownloads.begin();
  93. while (it != queuedDownloads.end())
  94. {
  95. QueuedDownload q = *it;
  96. TheDownloadManager->queueFileForDownload(q.server, q.userName, q.password,
  97. q.file, q.localFile, q.regKey, q.tryResume);
  98. queuedDownloads.pop_front();
  99. it = queuedDownloads.begin();
  100. }
  101. TheDownloadManager->downloadNextQueuedFile();
  102. }
  103. }
  104. ///////////////////////////////////////////////////////////////////////////////////////
  105. // user agrees to patch before going online
  106. static void patchBeforeOnlineCallback( void )
  107. {
  108. StartDownloadingPatches();
  109. }
  110. // user doesn't want to patch before going online
  111. static void noPatchBeforeOnlineCallback( void )
  112. {
  113. queuedDownloads.clear();
  114. if (mustDownloadPatch || cantConnectBeforeOnline)
  115. {
  116. // go back to normal
  117. HandleCanceledDownload();
  118. }
  119. else
  120. {
  121. // clear out unneeded downloads and go on
  122. startOnline();
  123. }
  124. }
  125. ///////////////////////////////////////////////////////////////////////////////////////
  126. static Bool hasWriteAccess()
  127. {
  128. const char* filename = "PatchAccessTest.txt";
  129. remove(filename);
  130. int handle = _open( filename, _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE);
  131. if (handle == -1)
  132. {
  133. return false;
  134. }
  135. _close(handle);
  136. remove(filename);
  137. unsigned int val;
  138. if (!GetUnsignedIntFromRegistry("", "Version", val))
  139. {
  140. return false;
  141. }
  142. if (!SetUnsignedIntInRegistry("", "Version", val))
  143. {
  144. return false;
  145. }
  146. return true;
  147. }
  148. ///////////////////////////////////////////////////////////////////////////////////////
  149. static void startOnline( void )
  150. {
  151. checkingForPatchBeforeGameSpy = FALSE;
  152. DEBUG_ASSERTCRASH(checksLeftBeforeOnline==0, ("starting online with pending callbacks"));
  153. if (onlineCancelWindow)
  154. {
  155. TheWindowManager->winDestroy(onlineCancelWindow);
  156. onlineCancelWindow = NULL;
  157. }
  158. if (cantConnectBeforeOnline)
  159. {
  160. MessageBoxOk(TheGameText->fetch("GUI:CannotConnectToServservTitle"),
  161. TheGameText->fetch("GUI:CannotConnectToServserv"),
  162. noPatchBeforeOnlineCallback);
  163. return;
  164. }
  165. if (queuedDownloads.size())
  166. {
  167. if (!hasWriteAccess())
  168. {
  169. MessageBoxOk(TheGameText->fetch("GUI:Error"),
  170. TheGameText->fetch("GUI:MustHaveAdminRights"),
  171. noPatchBeforeOnlineCallback);
  172. }
  173. else if (mustDownloadPatch)
  174. {
  175. MessageBoxOkCancel(TheGameText->fetch("GUI:PatchAvailable"),
  176. TheGameText->fetch("GUI:MustPatchForOnline"),
  177. patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
  178. }
  179. else
  180. {
  181. MessageBoxYesNo(TheGameText->fetch("GUI:PatchAvailable"),
  182. TheGameText->fetch("GUI:CanPatchForOnline"),
  183. patchBeforeOnlineCallback, noPatchBeforeOnlineCallback);
  184. }
  185. return;
  186. }
  187. TheScriptEngine->signalUIInteract(TheShellHookNames[SHELL_SCRIPT_HOOK_MAIN_MENU_ONLINE_SELECTED]);
  188. DEBUG_ASSERTCRASH( !TheGameSpyBuddyMessageQueue, ("TheGameSpyBuddyMessageQueue exists!") );
  189. DEBUG_ASSERTCRASH( !TheGameSpyPeerMessageQueue, ("TheGameSpyPeerMessageQueue exists!") );
  190. DEBUG_ASSERTCRASH( !TheGameSpyInfo, ("TheGameSpyInfo exists!") );
  191. SetUpGameSpy(MOTDBuffer, configBuffer);
  192. if (MOTDBuffer)
  193. {
  194. delete[] MOTDBuffer;
  195. MOTDBuffer = NULL;
  196. }
  197. if (configBuffer)
  198. {
  199. delete[] configBuffer;
  200. configBuffer = NULL;
  201. }
  202. #ifdef ALLOW_NON_PROFILED_LOGIN
  203. UserPreferences pref;
  204. pref.load("GameSpyLogin.ini");
  205. UserPreferences::const_iterator it = pref.find("useProfiles");
  206. if (it != pref.end() && it->second.compareNoCase("yes") == 0)
  207. #endif ALLOW_NON_PROFILED_LOGIN
  208. TheShell->push( AsciiString("Menus/GameSpyLoginProfile.wnd") );
  209. #ifdef ALLOW_NON_PROFILED_LOGIN
  210. else
  211. TheShell->push( AsciiString("Menus/GameSpyLoginQuick.wnd") );
  212. #endif ALLOW_NON_PROFILED_LOGIN
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////////////
  215. static void queuePatch(Bool mandatory, AsciiString downloadURL)
  216. {
  217. QueuedDownload q;
  218. Bool success = TRUE;
  219. AsciiString connectionType;
  220. success &= downloadURL.nextToken(&connectionType, ":");
  221. AsciiString server;
  222. success &= downloadURL.nextToken(&server, ":/");
  223. AsciiString user;
  224. success &= downloadURL.nextToken(&user, ":@");
  225. AsciiString pass;
  226. success &= downloadURL.nextToken(&pass, "@/");
  227. AsciiString filePath;
  228. success &= downloadURL.nextToken(&filePath, "");
  229. if (!success && user.isNotEmpty())
  230. {
  231. // no user/pass combo - move the file into it's proper place
  232. filePath = user;
  233. user = ""; // LFeenanEA - Credentials removed as per Security requirements
  234. pass = "";
  235. success = TRUE;
  236. }
  237. AsciiString fileStr = filePath;
  238. const char *s = filePath.reverseFind('/');
  239. if (s)
  240. fileStr = s+1;
  241. AsciiString fileName = "patches\\";
  242. fileName.concat(fileStr);
  243. DEBUG_LOG(("download URL split: %d [%s] [%s] [%s] [%s] [%s] [%s]\n",
  244. success, connectionType.str(), server.str(), user.str(), pass.str(),
  245. filePath.str(), fileName.str()));
  246. if (!success)
  247. return;
  248. q.file = filePath;
  249. q.localFile = fileName;
  250. q.password = pass;
  251. q.regKey = "";
  252. q.server = server;
  253. q.tryResume = TRUE;
  254. q.userName = user;
  255. std::list<QueuedDownload>::iterator it = queuedDownloads.begin();
  256. while (it != queuedDownloads.end())
  257. {
  258. if (it->localFile == q.localFile)
  259. return; // don't add it if it exists already (because we can check multiple times)
  260. ++it;
  261. }
  262. queuedDownloads.push_back(q);
  263. }
  264. ///////////////////////////////////////////////////////////////////////////////////////
  265. static GHTTPBool motdCallback( GHTTPRequest request, GHTTPResult result,
  266. char * buffer, int bufferLen, void * param )
  267. {
  268. Int run = (Int)param;
  269. if (run != timeThroughOnline)
  270. {
  271. DEBUG_CRASH(("Old callback being called!"));
  272. return GHTTPTrue;
  273. }
  274. if (MOTDBuffer)
  275. {
  276. delete[] MOTDBuffer;
  277. MOTDBuffer = NULL;
  278. }
  279. MOTDBuffer = NEW char[bufferLen];
  280. memcpy(MOTDBuffer, buffer, bufferLen);
  281. MOTDBuffer[bufferLen-1] = 0;
  282. --checksLeftBeforeOnline;
  283. DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
  284. if (onlineCancelWindow && !checksLeftBeforeOnline)
  285. {
  286. TheWindowManager->winDestroy(onlineCancelWindow);
  287. onlineCancelWindow = NULL;
  288. }
  289. DEBUG_LOG(("------- Got MOTD before going online -------\n"));
  290. DEBUG_LOG(("%s\n", (MOTDBuffer)?MOTDBuffer:""));
  291. DEBUG_LOG(("--------------------------------------------\n"));
  292. if (!checksLeftBeforeOnline)
  293. startOnline();
  294. return GHTTPTrue;
  295. }
  296. ///////////////////////////////////////////////////////////////////////////////////////
  297. static GHTTPBool configCallback( GHTTPRequest request, GHTTPResult result,
  298. char * buffer, int bufferLen, void * param )
  299. {
  300. Int run = (Int)param;
  301. if (run != timeThroughOnline)
  302. {
  303. DEBUG_CRASH(("Old callback being called!"));
  304. return GHTTPTrue;
  305. }
  306. if (configBuffer)
  307. {
  308. delete[] configBuffer;
  309. configBuffer = NULL;
  310. }
  311. if (result != GHTTPSuccess || bufferLen < 100)
  312. {
  313. if (!checkingForPatchBeforeGameSpy)
  314. return GHTTPTrue;
  315. --checksLeftBeforeOnline;
  316. if (onlineCancelWindow && !checksLeftBeforeOnline)
  317. {
  318. TheWindowManager->winDestroy(onlineCancelWindow);
  319. onlineCancelWindow = NULL;
  320. }
  321. cantConnectBeforeOnline = TRUE;
  322. if (!checksLeftBeforeOnline)
  323. {
  324. startOnline();
  325. }
  326. return GHTTPTrue;
  327. }
  328. configBuffer = NEW char[bufferLen];
  329. memcpy(configBuffer, buffer, bufferLen);
  330. configBuffer[bufferLen-1] = 0;
  331. AsciiString fname;
  332. fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
  333. FILE *fp = fopen(fname.str(), "wb");
  334. if (fp)
  335. {
  336. fwrite(configBuffer, bufferLen, 1, fp);
  337. fclose(fp);
  338. }
  339. --checksLeftBeforeOnline;
  340. DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
  341. if (onlineCancelWindow && !checksLeftBeforeOnline)
  342. {
  343. TheWindowManager->winDestroy(onlineCancelWindow);
  344. onlineCancelWindow = NULL;
  345. }
  346. DEBUG_LOG(("Got Config before going online\n"));
  347. if (!checksLeftBeforeOnline)
  348. startOnline();
  349. return GHTTPTrue;
  350. }
  351. ///////////////////////////////////////////////////////////////////////////////////////
  352. static GHTTPBool configHeadCallback( GHTTPRequest request, GHTTPResult result,
  353. char * buffer, int bufferLen, void * param )
  354. {
  355. Int run = (Int)param;
  356. if (run != timeThroughOnline)
  357. {
  358. DEBUG_CRASH(("Old callback being called!"));
  359. return GHTTPTrue;
  360. }
  361. DEBUG_LOG(("HTTP head resp: res=%d, len=%d, buf=[%s]\n", result, bufferLen, buffer));
  362. if (result == GHTTPSuccess)
  363. {
  364. DEBUG_LOG(("Headers are [%s]\n", ghttpGetHeaders( request )));
  365. AsciiString headers(ghttpGetHeaders( request ));
  366. AsciiString line;
  367. while (headers.nextToken(&line, "\n\r"))
  368. {
  369. AsciiString key, val;
  370. line.nextToken(&key, ": ");
  371. line.nextToken(&val, ": \r\n");
  372. if (key.compare("Content-Length") == 0 && val.isNotEmpty())
  373. {
  374. Int serverLen = atoi(val.str());
  375. Int fileLen = 0;
  376. AsciiString fname;
  377. fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
  378. FILE *fp = fopen(fname.str(), "rb");
  379. if (fp)
  380. {
  381. fseek(fp, 0, SEEK_END);
  382. fileLen = ftell(fp);
  383. fclose(fp);
  384. }
  385. if (serverLen == fileLen)
  386. {
  387. // we don't need to download the MOTD again
  388. --checksLeftBeforeOnline;
  389. DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
  390. if (onlineCancelWindow && !checksLeftBeforeOnline)
  391. {
  392. TheWindowManager->winDestroy(onlineCancelWindow);
  393. onlineCancelWindow = NULL;
  394. }
  395. if (configBuffer)
  396. {
  397. delete[] configBuffer;
  398. configBuffer = NULL;
  399. }
  400. AsciiString fname;
  401. fname.format("%sGeneralsOnline\\Config.txt", TheGlobalData->getPath_UserData().str());
  402. FILE *fp = fopen(fname.str(), "rb");
  403. if (fp)
  404. {
  405. configBuffer = NEW char[fileLen];
  406. fread(configBuffer, fileLen, 1, fp);
  407. configBuffer[fileLen-1] = 0;
  408. fclose(fp);
  409. DEBUG_LOG(("Got Config before going online\n"));
  410. if (!checksLeftBeforeOnline)
  411. startOnline();
  412. return GHTTPTrue;
  413. }
  414. }
  415. }
  416. }
  417. }
  418. // we need to download the MOTD again
  419. std::string gameURL, mapURL;
  420. std::string configURL, motdURL;
  421. FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
  422. ghttpGet( configURL.c_str(), GHTTPFalse, configCallback, param );
  423. return GHTTPTrue;
  424. }
  425. ///////////////////////////////////////////////////////////////////////////////////////
  426. static GHTTPBool gamePatchCheckCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
  427. {
  428. Int run = (Int)param;
  429. if (run != timeThroughOnline)
  430. {
  431. DEBUG_CRASH(("Old callback being called!"));
  432. return GHTTPTrue;
  433. }
  434. --checksLeftBeforeOnline;
  435. DEBUG_ASSERTCRASH(checksLeftBeforeOnline>=0, ("Too many callbacks"));
  436. DEBUG_LOG(("Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
  437. if (result != GHTTPSuccess)
  438. {
  439. if (!checkingForPatchBeforeGameSpy)
  440. return GHTTPTrue;
  441. cantConnectBeforeOnline = TRUE;
  442. if (!checksLeftBeforeOnline)
  443. {
  444. startOnline();
  445. }
  446. return GHTTPTrue;
  447. }
  448. AsciiString message = buffer;
  449. AsciiString line;
  450. while (message.nextToken(&line, "\r\n"))
  451. {
  452. AsciiString type, req, url;
  453. Bool ok = TRUE;
  454. ok &= line.nextToken(&type, " ");
  455. ok &= line.nextToken(&req, " ");
  456. ok &= line.nextToken(&url, " ");
  457. if (ok && type == "patch")
  458. {
  459. DEBUG_LOG(("Saw a patch: %d/[%s]\n", atoi(req.str()), url.str()));
  460. queuePatch( atoi(req.str()), url );
  461. if (atoi(req.str()))
  462. {
  463. mustDownloadPatch = TRUE;
  464. }
  465. }
  466. else if (ok && type == "server")
  467. {
  468. }
  469. }
  470. if (!checksLeftBeforeOnline)
  471. {
  472. startOnline();
  473. }
  474. return GHTTPTrue;
  475. }
  476. ///////////////////////////////////////////////////////////////////////////////////////
  477. void CancelPatchCheckCallbackAndReopenDropdown( void )
  478. {
  479. HandleCanceledDownload();
  480. CancelPatchCheckCallback();
  481. }
  482. void CancelPatchCheckCallback( void )
  483. {
  484. s_asyncDNSLookupInProgress = FALSE;
  485. HandleCanceledDownload(FALSE); // don't dropdown
  486. checkingForPatchBeforeGameSpy = FALSE;
  487. checksLeftBeforeOnline = 0;
  488. if (onlineCancelWindow)
  489. {
  490. TheWindowManager->winDestroy(onlineCancelWindow);
  491. onlineCancelWindow = NULL;
  492. }
  493. queuedDownloads.clear();
  494. if (MOTDBuffer)
  495. {
  496. delete[] MOTDBuffer;
  497. MOTDBuffer = NULL;
  498. }
  499. if (configBuffer)
  500. {
  501. delete[] configBuffer;
  502. configBuffer = NULL;
  503. }
  504. }
  505. ///////////////////////////////////////////////////////////////////////////////////////
  506. static GHTTPBool overallStatsCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
  507. {
  508. DEBUG_LOG(("overallStatsCallback() - Result=%d, len=%d\n", result, bufferLen));
  509. if (result != GHTTPSuccess)
  510. {
  511. return GHTTPTrue;
  512. }
  513. OverallStats USA, China, GLA;
  514. AsciiString message = buffer;
  515. Int state = STATS_MAX; // STATS_MAX == none
  516. AsciiString line;
  517. OverallStats *stats = NULL;
  518. while (message.nextToken(&line, "\n"))
  519. {
  520. line.trim();
  521. line.toLower();
  522. if (strstr(line.str(), "today"))
  523. {
  524. state = STATS_TODAY;
  525. }
  526. else if (strstr(line.str(), "yesterday"))
  527. {
  528. state = STATS_YESTERDAY;
  529. }
  530. else if (strstr(line.str(), "all time"))
  531. {
  532. state = STATS_ALLTIME;
  533. }
  534. else if (strstr(line.str(), "last week"))
  535. {
  536. state = STATS_LASTWEEK;
  537. }
  538. else if (state != STATS_MAX && strstr(line.str(), "usa"))
  539. {
  540. stats = &USA;
  541. }
  542. else if (state != STATS_MAX && strstr(line.str(), "china"))
  543. {
  544. stats = &China;
  545. }
  546. else if (state != STATS_MAX && strstr(line.str(), "gla"))
  547. {
  548. stats = &GLA;
  549. }
  550. if (stats)
  551. {
  552. AsciiString totalLine, winsLine, lossesLine;
  553. message.nextToken(&totalLine, "\n");
  554. message.nextToken(&winsLine, "\n");
  555. message.nextToken(&lossesLine, "\n");
  556. while (totalLine.isNotEmpty() && !isdigit(totalLine.getCharAt(0)))
  557. {
  558. totalLine = totalLine.str()+1;
  559. }
  560. while (winsLine.isNotEmpty() && !isdigit(winsLine.getCharAt(0)))
  561. {
  562. winsLine = winsLine.str()+1;
  563. }
  564. while (lossesLine.isNotEmpty() && !isdigit(lossesLine.getCharAt(0)))
  565. {
  566. lossesLine = lossesLine.str()+1;
  567. }
  568. if (totalLine.isNotEmpty() && winsLine.isNotEmpty() && lossesLine.isNotEmpty())
  569. {
  570. stats->wins[state] = atoi(winsLine.str());
  571. stats->losses[state] = atoi(lossesLine.str());
  572. }
  573. stats = NULL;
  574. }
  575. }
  576. HandleOverallStats(USA, China, GLA);
  577. return GHTTPTrue;
  578. }
  579. ///////////////////////////////////////////////////////////////////////////////////////
  580. static GHTTPBool numPlayersOnlineCallback( GHTTPRequest request, GHTTPResult result, char * buffer, int bufferLen, void * param )
  581. {
  582. DEBUG_LOG(("numPlayersOnlineCallback() - Result=%d, buffer=[%s], len=%d\n", result, buffer, bufferLen));
  583. if (result != GHTTPSuccess)
  584. {
  585. return GHTTPTrue;
  586. }
  587. AsciiString message = buffer;
  588. message.trim();
  589. const char *s = message.reverseFind('\\');
  590. if (!s)
  591. {
  592. return GHTTPTrue;
  593. }
  594. if (*s == '\\')
  595. ++s;
  596. DEBUG_LOG(("Message was '%s', trimmed to '%s'=%d\n", buffer, s, atoi(s)));
  597. HandleNumPlayersOnline(atoi(s));
  598. return GHTTPTrue;
  599. }
  600. ///////////////////////////////////////////////////////////////////////////////////////
  601. void CheckOverallStats( void )
  602. {
  603. ghttpGet("http://gamestats.gamespy.com/ccgenerals/display.html",
  604. GHTTPFalse, overallStatsCallback, NULL);
  605. }
  606. ///////////////////////////////////////////////////////////////////////////////////////
  607. void CheckNumPlayersOnline( void )
  608. {
  609. ghttpGet("http://launch.gamespyarcade.com/software/launch/arcadecount2.dll?svcname=ccgenerals",
  610. GHTTPFalse, numPlayersOnlineCallback, NULL);
  611. }
  612. ///////////////////////////////////////////////////////////////////////////////////////
  613. DWORD WINAPI asyncGethostbynameThreadFunc( void * szName )
  614. {
  615. HOSTENT *he = gethostbyname( (const char *)szName );
  616. if (he)
  617. {
  618. s_asyncDNSThreadSucceeded = TRUE;
  619. }
  620. else
  621. {
  622. s_asyncDNSThreadSucceeded = FALSE;
  623. }
  624. s_asyncDNSThreadDone = TRUE;
  625. return 0;
  626. }
  627. ///////////////////////////////////////////////////////////////////////////////////////
  628. int asyncGethostbyname(char * szName)
  629. {
  630. static int stat = 0;
  631. static unsigned long threadid;
  632. if( stat == 0 )
  633. {
  634. /* Kick off gethostname thread */
  635. s_asyncDNSThreadDone = FALSE;
  636. s_asyncDNSThreadHandle = CreateThread( NULL, 0, asyncGethostbynameThreadFunc, szName, 0, &threadid );
  637. if( s_asyncDNSThreadHandle == NULL )
  638. {
  639. return( LOOKUP_FAILED );
  640. }
  641. stat = 1;
  642. }
  643. if( stat == 1 )
  644. {
  645. if( s_asyncDNSThreadDone )
  646. {
  647. /* Thread finished */
  648. stat = 0;
  649. s_asyncDNSLookupInProgress = FALSE;
  650. s_asyncDNSThreadHandle = NULL;
  651. return( (s_asyncDNSThreadSucceeded)?LOOKUP_SUCCEEDED:LOOKUP_FAILED );
  652. }
  653. }
  654. return( LOOKUP_INPROGRESS );
  655. }
  656. ///////////////////////////////////////////////////////////////////////////////////////
  657. // GameSpy's HTTP SDK has had at least 1 crash bug, so we're going to just bail and
  658. // never try again if they crash us. We won't be able to get back online again (we'll
  659. // time out) but at least we'll live.
  660. static Bool isHttpOk = TRUE;
  661. void HTTPThinkWrapper( void )
  662. {
  663. if (s_asyncDNSLookupInProgress)
  664. {
  665. Int ret = asyncGethostbyname("servserv.generals.ea.com");
  666. switch(ret)
  667. {
  668. case LOOKUP_FAILED:
  669. cantConnectBeforeOnline = TRUE;
  670. startOnline();
  671. break;
  672. case LOOKUP_SUCCEEDED:
  673. reallyStartPatchCheck();
  674. break;
  675. }
  676. }
  677. if (isHttpOk)
  678. {
  679. try
  680. {
  681. ghttpThink();
  682. }
  683. catch (...)
  684. {
  685. isHttpOk = FALSE; // we can't abort the login, since we might be done with the
  686. // required checks and are fetching extras. If it is a required
  687. // check, we'll time out normally.
  688. }
  689. }
  690. }
  691. ///////////////////////////////////////////////////////////////////////////////////////
  692. void StopAsyncDNSCheck( void )
  693. {
  694. if (s_asyncDNSThreadHandle)
  695. {
  696. #ifdef DEBUG_CRASHING
  697. Int res =
  698. #endif
  699. TerminateThread(s_asyncDNSThreadHandle,0);
  700. DEBUG_ASSERTCRASH(res, ("Could not terminate the Async DNS Lookup thread!")); // Thread still not killed!
  701. }
  702. s_asyncDNSThreadHandle = NULL;
  703. s_asyncDNSLookupInProgress = FALSE;
  704. }
  705. ///////////////////////////////////////////////////////////////////////////////////////
  706. void StartPatchCheck( void )
  707. {
  708. checkingForPatchBeforeGameSpy = TRUE;
  709. cantConnectBeforeOnline = FALSE;
  710. timeThroughOnline++;
  711. checksLeftBeforeOnline = 0;
  712. onlineCancelWindow = MessageBoxCancel(TheGameText->fetch("GUI:CheckingForPatches"),
  713. TheGameText->fetch("GUI:CheckingForPatches"), CancelPatchCheckCallbackAndReopenDropdown);
  714. s_asyncDNSLookupInProgress = TRUE;
  715. Int ret = asyncGethostbyname("servserv.generals.ea.com");
  716. switch(ret)
  717. {
  718. case LOOKUP_FAILED:
  719. cantConnectBeforeOnline = TRUE;
  720. startOnline();
  721. break;
  722. case LOOKUP_SUCCEEDED:
  723. reallyStartPatchCheck();
  724. break;
  725. }
  726. }
  727. ///////////////////////////////////////////////////////////////////////////////////////
  728. static void reallyStartPatchCheck( void )
  729. {
  730. checksLeftBeforeOnline = 4;
  731. std::string gameURL, mapURL;
  732. std::string configURL, motdURL;
  733. FormatURLFromRegistry(gameURL, mapURL, configURL, motdURL);
  734. std::string proxy;
  735. if (GetStringFromRegistry("", "Proxy", proxy))
  736. {
  737. if (!proxy.empty())
  738. {
  739. ghttpSetProxy(proxy.c_str());
  740. }
  741. }
  742. // check for a patch first
  743. DEBUG_LOG(("Game patch check: [%s]\n", gameURL.c_str()));
  744. DEBUG_LOG(("Map patch check: [%s]\n", mapURL.c_str()));
  745. DEBUG_LOG(("Config: [%s]\n", configURL.c_str()));
  746. DEBUG_LOG(("MOTD: [%s]\n", motdURL.c_str()));
  747. ghttpGet(gameURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
  748. ghttpGet(mapURL.c_str(), GHTTPFalse, gamePatchCheckCallback, (void *)timeThroughOnline);
  749. ghttpHead(configURL.c_str(), GHTTPFalse, configHeadCallback, (void *)timeThroughOnline);
  750. ghttpGet(motdURL.c_str(), GHTTPFalse, motdCallback, (void *)timeThroughOnline);
  751. // check total game stats
  752. CheckOverallStats();
  753. // check the users online
  754. CheckNumPlayersOnline();
  755. }
  756. ///////////////////////////////////////////////////////////////////////////////////////