PeerDefs.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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. // FILE: PeerDefs.cpp //////////////////////////////////////////////////////
  19. // Generals GameSpy Peer (chat) definitions
  20. // Author: Matthew D. Campbell, June 2002
  21. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  22. #include <set>
  23. #include "Common/GameState.h"
  24. #include "Common/RandomValue.h"
  25. #include "Common/IgnorePreferences.h"
  26. #include "Common/CustomMatchPreferences.h"
  27. #include "Common/GameSpyMiscPreferences.h"
  28. #include "Common/Recorder.h"
  29. #include "Common/Player.h"
  30. #include "Common/PlayerList.h"
  31. #include "Common/PlayerTemplate.h"
  32. #include "GameClient/MapUtil.h"
  33. #include "GameClient/ShellHooks.h"
  34. #include "GameClient/GameText.h"
  35. #include "GameNetwork/GameSpy/LadderDefs.h"
  36. #include "GameNetwork/GameSpy/PeerDefsImplementation.h"
  37. #include "GameNetwork/GameSpy/BuddyThread.h"
  38. #include "GameNetwork/GameSpy/PeerThread.h"
  39. #include "GameNetwork/GameSpy/PingThread.h"
  40. #include "GameNetwork/GameSpy/PersistentStorageThread.h"
  41. #include "GameNetwork/GameSpy/GSConfig.h"
  42. #include "GameNetwork/GameSpyOverlay.h"
  43. #include "GameNetwork/RankPointValue.h"
  44. #include "GameLogic/GameLogic.h"
  45. #ifdef _INTERNAL
  46. // for occasional debugging...
  47. //#pragma optimize("", off)
  48. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  49. #endif
  50. GameSpyInfoInterface *TheGameSpyInfo = NULL;
  51. GameSpyStagingRoom *TheGameSpyGame = NULL;
  52. void deleteNotificationBox( void );
  53. bool AsciiComparator::operator()(AsciiString s1, AsciiString s2) const
  54. {
  55. return stricmp(s1.str(), s2.str()) < 0;
  56. }
  57. GameSpyInfo::GameSpyInfo()
  58. {
  59. reset();
  60. TheGameSpyGame = &m_localStagingRoom;
  61. m_isDisconAfterGameStart = FALSE;
  62. }
  63. GameSpyInfo::~GameSpyInfo()
  64. {
  65. TheGameSpyGame = NULL;
  66. reset();
  67. }
  68. void GameSpyInfo::reset( void )
  69. {
  70. m_sawFullGameList = FALSE;
  71. m_isDisconAfterGameStart = FALSE;
  72. m_currentGroupRoomID = 0;
  73. clearGroupRoomList();
  74. clearStagingRoomList();
  75. m_localStagingRoomID = 0;
  76. m_buddyRequestMap.clear();
  77. m_buddyMap.clear();
  78. m_buddyMessages.clear();
  79. m_joinedStagingRoom = 0;
  80. m_isHosting = false;
  81. m_localStagingRoomID = 0;
  82. m_localStagingRoom.reset();
  83. m_gotGroupRoomList = false;
  84. m_localName = "";
  85. m_localProfileID = 0;
  86. m_maxMessagesPerUpdate = 100;
  87. // Added By Sadullah Nader
  88. // Initialization missing and needed
  89. m_disallowAsainText = FALSE;
  90. m_disallowNonAsianText = FALSE;
  91. m_disconReason = 0;
  92. m_localBaseName.clear();
  93. m_localEmail.clear();
  94. m_localPasswd.clear();
  95. m_pingString.clear();
  96. m_rawConfig.clear();
  97. m_rawMotd.clear();
  98. //
  99. m_internalIP = m_externalIP = 0;
  100. m_savedIgnoreMap.clear();
  101. m_preorderPlayers.clear();
  102. m_cachedLocalPlayerStats.reset();
  103. m_additionalDisconnects = -1;
  104. }
  105. Bool GameSpyInfo::didPlayerPreorder( Int profileID ) const
  106. {
  107. std::set<Int>::const_iterator it = m_preorderPlayers.find(profileID);
  108. return (it != m_preorderPlayers.end());
  109. }
  110. void GameSpyInfo::markPlayerAsPreorder( Int profileID )
  111. {
  112. m_preorderPlayers.insert(profileID);
  113. }
  114. void GameSpyInfo::setLocalIPs(UnsignedInt internalIP, UnsignedInt externalIP)
  115. {
  116. m_internalIP = internalIP;
  117. m_externalIP = externalIP;
  118. }
  119. void GameSpyInfo::readAdditionalDisconnects( void )
  120. {
  121. m_additionalDisconnects = GetAdditionalDisconnectsFromUserFile(m_localProfileID);
  122. DEBUG_LOG(("GameSpyInfo::readAdditionalDisconnects() found %d disconnects.\n", m_additionalDisconnects));
  123. }
  124. Int GameSpyInfo::getAdditionalDisconnects( void )
  125. {
  126. DEBUG_LOG(("GameSpyInfo::getAdditionalDisconnects() would have returned %d. Returning 0 instead.\n", m_additionalDisconnects));
  127. return 0;
  128. }
  129. void GameSpyInfo::clearAdditionalDisconnects( void )
  130. {
  131. m_additionalDisconnects = 0;
  132. }
  133. GameSpyInfoInterface* GameSpyInfoInterface::createNewGameSpyInfoInterface( void )
  134. {
  135. return NEW GameSpyInfo;
  136. }
  137. Bool GameSpyInfo::amIHost( void )
  138. {
  139. return m_isHosting;
  140. }
  141. GameSpyStagingRoom* GameSpyInfo::getCurrentStagingRoom( void )
  142. {
  143. if (m_isHosting || m_joinedStagingRoom)
  144. return &m_localStagingRoom;
  145. StagingRoomMap::iterator it = m_stagingRooms.find(m_joinedStagingRoom);
  146. if (it != m_stagingRooms.end())
  147. return it->second;
  148. return NULL;
  149. }
  150. void GameSpyInfo::setGameOptions( void )
  151. {
  152. if (!m_isHosting)
  153. return;
  154. // set options for game lists, and UTM players in-game
  155. PeerRequest req;
  156. req.peerRequestType = PeerRequest::PEERREQUEST_SETGAMEOPTIONS;
  157. req.options = GameInfoToAsciiString(&m_localStagingRoom).str();
  158. Int i;
  159. AsciiString mapName = TheGameState->realMapPathToPortableMapPath(m_localStagingRoom.getMap());
  160. AsciiString newMapName;
  161. for (i=0; i<mapName.getLength(); ++i)
  162. {
  163. char c = mapName.getCharAt(i);
  164. if (c != '\\')
  165. newMapName.concat(c);
  166. else
  167. newMapName.concat('/');
  168. }
  169. req.gameOptsMapName = newMapName.str();
  170. //const MapMetaData *md = TheMapCache->findMap(mapName);
  171. //if (!md)
  172. //return; // there really isn't any need to send info like this...
  173. req.gameOptions.numPlayers = 0;
  174. req.gameOptions.numObservers = 0;
  175. Int numOpenSlots = 0;
  176. AsciiString playerInfo = "";
  177. for (i=0; i<MAX_SLOTS; ++i)
  178. {
  179. Int wins = 0, losses = 0, profileID = 0;
  180. GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
  181. req.gameOptsPlayerNames[i] = "";
  182. if (!slot->isOccupied())
  183. {
  184. if (slot->isOpen())
  185. ++numOpenSlots;
  186. }
  187. else
  188. {
  189. AsciiString playerName;
  190. if (slot->isHuman())
  191. {
  192. playerName.translate(slot->getName());
  193. req.gameOptsPlayerNames[i] = playerName.str();
  194. PlayerInfoMap::iterator it = m_playerInfoMap.find(playerName);
  195. if (it != m_playerInfoMap.end())
  196. {
  197. wins = it->second.m_wins;
  198. losses = it->second.m_losses;
  199. profileID = it->second.m_profileID;
  200. }
  201. req.gameOptions.wins[req.gameOptions.numObservers+req.gameOptions.numPlayers] = wins;
  202. req.gameOptions.losses[req.gameOptions.numObservers+req.gameOptions.numPlayers] = losses;
  203. req.gameOptions.profileID[req.gameOptions.numObservers+req.gameOptions.numPlayers] = profileID;
  204. req.gameOptions.faction[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getPlayerTemplate();
  205. req.gameOptions.color[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getColor();
  206. if (slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
  207. {
  208. ++req.gameOptions.numObservers;
  209. }
  210. else
  211. {
  212. ++req.gameOptions.numPlayers;
  213. }
  214. }
  215. else if (slot->isAI())
  216. {
  217. // add in AI players
  218. switch (slot->getState())
  219. {
  220. case SLOT_EASY_AI:
  221. playerName = "CE";
  222. break;
  223. case SLOT_MED_AI:
  224. playerName = "CM";
  225. break;
  226. case SLOT_BRUTAL_AI:
  227. playerName = "CH";
  228. break;
  229. }
  230. req.gameOptsPlayerNames[i] = playerName.str(); // name is unused - we go off of the profileID
  231. req.gameOptions.wins[req.gameOptions.numObservers+req.gameOptions.numPlayers] = 0;
  232. req.gameOptions.losses[req.gameOptions.numObservers+req.gameOptions.numPlayers] = 0;
  233. req.gameOptions.profileID[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getState();
  234. req.gameOptions.faction[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getPlayerTemplate();
  235. req.gameOptions.color[req.gameOptions.numObservers+req.gameOptions.numPlayers] = slot->getColor();
  236. ++req.gameOptions.numPlayers;
  237. }
  238. }
  239. }
  240. req.gameOptions.maxPlayers = numOpenSlots + req.gameOptions.numPlayers + req.gameOptions.numObservers;
  241. TheGameSpyPeerMessageQueue->addRequest(req);
  242. req.peerRequestType = PeerRequest::PEERREQUEST_UTMROOM;
  243. req.UTM.isStagingRoom = TRUE;
  244. req.id = "Pings/";
  245. AsciiString pings;
  246. for (i=0; i<MAX_SLOTS; ++i)
  247. {
  248. if (i!=0)
  249. pings.concat(",");
  250. GameSpyGameSlot *slot = TheGameSpyGame->getGameSpySlot(i);
  251. if (slot && slot->isHuman())
  252. {
  253. pings.concat(slot->getPingString());
  254. }
  255. else
  256. {
  257. pings.concat("0");
  258. }
  259. }
  260. req.options = pings.str();
  261. TheGameSpyPeerMessageQueue->addRequest(req);
  262. }
  263. Bool GameSpyInfo::isBuddy( Int id )
  264. {
  265. return m_buddyMap.find(id) != m_buddyMap.end();
  266. }
  267. void GameSpyInfo::addGroupRoom( GameSpyGroupRoom room )
  268. {
  269. if (room.m_groupID == 0)
  270. {
  271. m_gotGroupRoomList = TRUE;
  272. GroupRoomMap::iterator iter;
  273. // figure out how many good strings we've got
  274. std::vector<UnicodeString> names;
  275. Int numRooms = 0;
  276. for (iter = getGroupRoomList()->begin(); iter != getGroupRoomList()->end(); ++iter)
  277. {
  278. GameSpyGroupRoom room = iter->second;
  279. if (room.m_groupID != TheGameSpyConfig->getQMChannel())
  280. {
  281. ++numRooms;
  282. AsciiString groupLabel;
  283. groupLabel.format("GUI:%s", room.m_name.str());
  284. Bool exists = FALSE;
  285. UnicodeString groupName = TheGameText->fetch(groupLabel, &exists);
  286. if (exists)
  287. {
  288. names.push_back(groupName);
  289. }
  290. }
  291. }
  292. if (!names.empty() && names.size() != numRooms)
  293. {
  294. // didn't get all names. fix up
  295. Int nameIndex = 0;
  296. Int timesThrough = 1; // start with USA Lobby 1
  297. for (iter = TheGameSpyInfo->getGroupRoomList()->begin(); iter != TheGameSpyInfo->getGroupRoomList()->end(); ++iter)
  298. {
  299. GameSpyGroupRoom room = iter->second;
  300. if (room.m_groupID != TheGameSpyConfig->getQMChannel())
  301. {
  302. room.m_translatedName.format(L"%ls %d", names[nameIndex].str(), timesThrough);
  303. nameIndex = (nameIndex+1)%names.size();
  304. m_groupRooms[room.m_groupID] = room;
  305. if (!nameIndex)
  306. {
  307. // we've looped through the name list already. increment the timesThrough counter
  308. ++timesThrough;
  309. }
  310. }
  311. }
  312. }
  313. }
  314. else
  315. {
  316. DEBUG_LOG(("Adding group room %d (%s)\n", room.m_groupID, room.m_name.str()));
  317. AsciiString groupLabel;
  318. groupLabel.format("GUI:%s", room.m_name.str());
  319. room.m_translatedName = TheGameText->fetch(groupLabel);
  320. m_groupRooms[room.m_groupID] = room;
  321. if ( !stricmp("quickmatch", room.m_name.str()) )
  322. {
  323. DEBUG_LOG(("Group room %d (%s) is the QuickMatch room\n", room.m_groupID, room.m_name.str()));
  324. TheGameSpyConfig->setQMChannel(room.m_groupID);
  325. }
  326. }
  327. }
  328. void GameSpyInfo::joinGroupRoom( Int groupID )
  329. {
  330. if (groupID > 0)
  331. {
  332. PeerRequest req;
  333. req.peerRequestType = PeerRequest::PEERREQUEST_JOINGROUPROOM;
  334. req.groupRoom.id = groupID;
  335. TheGameSpyPeerMessageQueue->addRequest(req);
  336. m_playerInfoMap.clear();
  337. }
  338. }
  339. void GameSpyInfo::leaveGroupRoom( void )
  340. {
  341. PeerRequest req;
  342. req.peerRequestType = PeerRequest::PEERREQUEST_LEAVEGROUPROOM;
  343. TheGameSpyPeerMessageQueue->addRequest(req);
  344. setCurrentGroupRoom(0);
  345. m_playerInfoMap.clear();
  346. }
  347. void GameSpyInfo::joinBestGroupRoom( void )
  348. {
  349. if (m_currentGroupRoomID)
  350. {
  351. DEBUG_LOG(("Bailing from GameSpyInfo::joinBestGroupRoom() - we were already in a room\n"));
  352. m_currentGroupRoomID = 0;
  353. return;
  354. }
  355. if (m_groupRooms.size())
  356. {
  357. int minID = -1;
  358. int minPlayers = 1000;
  359. GroupRoomMap::iterator iter = m_groupRooms.begin();
  360. while (iter != m_groupRooms.end())
  361. {
  362. GameSpyGroupRoom room = iter->second;
  363. DEBUG_LOG(("Group room %d: %s (%d, %d, %d, %d)\n", room.m_groupID, room.m_name.str(), room.m_numWaiting, room.m_maxWaiting,
  364. room.m_numGames, room.m_numPlaying));
  365. if (TheGameSpyConfig->getQMChannel() != room.m_groupID && minPlayers > 25 && room.m_numWaiting < minPlayers)
  366. {
  367. minID = room.m_groupID;
  368. minPlayers = room.m_numWaiting;
  369. }
  370. ++iter;
  371. }
  372. if (minID > 0)
  373. {
  374. PeerRequest req;
  375. req.peerRequestType = PeerRequest::PEERREQUEST_JOINGROUPROOM;
  376. req.groupRoom.id = minID;
  377. TheGameSpyPeerMessageQueue->addRequest(req);
  378. m_playerInfoMap.clear();
  379. }
  380. else
  381. {
  382. GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:GSGroupRoomJoinFail"), NULL);
  383. }
  384. }
  385. else
  386. {
  387. GSMessageBoxOk(TheGameText->fetch("GUI:Error"), TheGameText->fetch("GUI:GSGroupRoomJoinFail"), NULL);
  388. }
  389. }
  390. void GameSpyInfo::updatePlayerInfo( PlayerInfo pi, AsciiString oldNick )
  391. {
  392. if (!oldNick.isEmpty())
  393. playerLeftGroupRoom(oldNick);
  394. m_playerInfoMap[pi.m_name] = pi;
  395. if (pi.m_preorder != 0)
  396. markPlayerAsPreorder(pi.m_profileID);
  397. }
  398. void GameSpyInfo::playerLeftGroupRoom( AsciiString nick )
  399. {
  400. PlayerInfoMap::iterator it = m_playerInfoMap.find(nick);
  401. if (it != m_playerInfoMap.end())
  402. {
  403. m_playerInfoMap.erase(it);
  404. }
  405. }
  406. void GameSpyInfo::clearStagingRoomList( void )
  407. {
  408. Int numRoomsRemoved = 0;
  409. m_sawFullGameList = FALSE;
  410. m_stagingRoomsDirty = FALSE;
  411. StagingRoomMap::iterator it = m_stagingRooms.begin();
  412. while (it != m_stagingRooms.end())
  413. {
  414. ++numRoomsRemoved;
  415. delete it->second;
  416. m_stagingRooms.erase(it);
  417. it = m_stagingRooms.begin();
  418. }
  419. if (numRoomsRemoved > 0)
  420. {
  421. //m_stagingRoomsDirty = true; // only consider ourselves dirty if we actually removed some games.
  422. }
  423. }
  424. void GameSpyInfo::addStagingRoom( GameSpyStagingRoom room )
  425. {
  426. removeStagingRoom(room);
  427. GameSpyStagingRoom *newRoom = NEW GameSpyStagingRoom;
  428. *newRoom = room;
  429. newRoom->cleanUpSlotPointers();
  430. m_stagingRooms[room.getID()] = newRoom;
  431. m_stagingRoomsDirty = m_sawFullGameList;
  432. }
  433. void GameSpyInfo::updateStagingRoom( GameSpyStagingRoom room )
  434. {
  435. addStagingRoom(room);
  436. }
  437. void GameSpyInfo::removeStagingRoom( GameSpyStagingRoom room )
  438. {
  439. StagingRoomMap::iterator it = m_stagingRooms.find(room.getID());
  440. if (it != m_stagingRooms.end())
  441. {
  442. delete it->second;
  443. m_stagingRooms.erase(it);
  444. m_stagingRoomsDirty = m_sawFullGameList;
  445. }
  446. }
  447. Bool GameSpyInfo::hasStagingRoomListChanged( void )
  448. {
  449. Bool val = m_stagingRoomsDirty;
  450. m_stagingRoomsDirty = false;
  451. return val;
  452. }
  453. GameSpyStagingRoom* GameSpyInfo::findStagingRoomByID( Int id )
  454. {
  455. StagingRoomMap::iterator it = m_stagingRooms.find(id);
  456. if (it != m_stagingRooms.end())
  457. return it->second;
  458. return NULL;
  459. }
  460. void GameSpyInfo::leaveStagingRoom( void )
  461. {
  462. m_localStagingRoomID = 0;
  463. PeerRequest req;
  464. req.peerRequestType = PeerRequest::PEERREQUEST_LEAVESTAGINGROOM;
  465. TheGameSpyPeerMessageQueue->addRequest(req);
  466. m_playerInfoMap.clear();
  467. m_joinedStagingRoom = FALSE;
  468. m_isHosting = FALSE;
  469. }
  470. void GameSpyInfo::markAsStagingRoomHost( void )
  471. {
  472. m_localStagingRoomID = 0;
  473. m_joinedStagingRoom = FALSE; m_isHosting = TRUE;
  474. m_localStagingRoom.reset();
  475. m_localStagingRoom.enterGame();
  476. m_localStagingRoom.setSeed(GetTickCount());
  477. GameSlot newSlot;
  478. UnicodeString uName;
  479. uName.translate(m_localName);
  480. newSlot.setState(SLOT_PLAYER, uName);
  481. m_localStagingRoom.setLocalIP(m_externalIP);
  482. newSlot.setIP(m_externalIP);
  483. m_localStagingRoom.setSlot(0,newSlot);
  484. m_localStagingRoom.setLocalName(m_localName);
  485. TheMapCache->updateCache();
  486. m_localStagingRoom.setMap(getDefaultMap(TRUE));
  487. m_localStagingRoom.adjustSlotsForMap(); // close slots that the map can't hold. BGC
  488. }
  489. void GameSpyInfo::markAsStagingRoomJoiner( Int game )
  490. {
  491. m_localStagingRoomID = game;
  492. m_joinedStagingRoom = TRUE; m_isHosting = FALSE;
  493. m_localStagingRoom.reset();
  494. m_localStagingRoom.enterGame();
  495. StagingRoomMap::iterator it = m_stagingRooms.find(game);
  496. if (it != m_stagingRooms.end())
  497. {
  498. GameSpyStagingRoom *info = it->second;
  499. info->cleanUpSlotPointers();
  500. AsciiString options = GameInfoToAsciiString(info);
  501. #ifdef DEBUG_CRASHING
  502. Bool res =
  503. #endif
  504. ParseAsciiStringToGameInfo(&m_localStagingRoom, options);
  505. DEBUG_ASSERTCRASH(res, ("Could not parse game info \"%s\"", options.str()));
  506. m_localStagingRoom.setInGame();
  507. m_localStagingRoom.setLocalName(m_localName);
  508. m_localStagingRoom.setExeCRC(info->getExeCRC());
  509. m_localStagingRoom.setIniCRC(info->getIniCRC());
  510. m_localStagingRoom.setAllowObservers(info->getAllowObservers());
  511. m_localStagingRoom.setHasPassword(info->getHasPassword());
  512. m_localStagingRoom.setGameName(info->getGameName());
  513. DEBUG_LOG(("Joining game: host is %ls\n", m_localStagingRoom.getConstSlot(0)->getName().str()));
  514. }
  515. }
  516. void GameSpyInfo::setMOTD( const AsciiString& motd )
  517. {
  518. m_rawMotd = motd;
  519. }
  520. const AsciiString& GameSpyInfo::getMOTD( void )
  521. {
  522. return m_rawMotd;
  523. }
  524. void GameSpyInfo::setConfig( const AsciiString& config )
  525. {
  526. m_rawConfig = config;
  527. }
  528. const AsciiString& GameSpyInfo::getConfig( void )
  529. {
  530. return m_rawConfig;
  531. }
  532. // --------------------------------------------------------------
  533. void SetUpGameSpy( const char *motdBuffer, const char *configBuffer )
  534. {
  535. if (!motdBuffer)
  536. motdBuffer = "";
  537. if (!configBuffer)
  538. configBuffer = "";
  539. TearDownGameSpy();
  540. AsciiString dir = TheGlobalData->getPath_UserData();
  541. CreateDirectory(dir.str(), NULL);
  542. dir.format("%sGeneralsOnline", TheGlobalData->getPath_UserData().str());
  543. CreateDirectory(dir.str(), NULL);
  544. dir.format("%sGeneralsOnline\\Ladders", TheGlobalData->getPath_UserData().str());
  545. CreateDirectory(dir.str(), NULL);
  546. TheGameSpyBuddyMessageQueue = GameSpyBuddyMessageQueueInterface::createNewMessageQueue();
  547. TheGameSpyBuddyMessageQueue->startThread();
  548. TheGameSpyPeerMessageQueue = GameSpyPeerMessageQueueInterface::createNewMessageQueue();
  549. TheGameSpyPeerMessageQueue->startThread();
  550. TheGameSpyPSMessageQueue = GameSpyPSMessageQueueInterface::createNewMessageQueue();
  551. TheGameSpyPSMessageQueue->startThread();
  552. /*
  553. TheGameSpyGame = NEW GameSpyStagingRoom;
  554. */
  555. TheGameSpyInfo = GameSpyInfoInterface::createNewGameSpyInfoInterface();
  556. TheGameSpyInfo->setMOTD(motdBuffer);
  557. TheGameSpyInfo->setConfig(configBuffer);
  558. CustomMatchPreferences pref;
  559. TheGameSpyInfo->setDisallowAsianText(pref.getDisallowAsianText());
  560. TheGameSpyInfo->setDisallowNonAsianText( pref.getDisallowNonAsianText());
  561. TheGameSpyConfig = GameSpyConfigInterface::create(configBuffer);
  562. TheLadderList = NEW LadderList;
  563. ThePinger = PingerInterface::createNewPingerInterface();
  564. ThePinger->startThreads();
  565. TheRankPointValues = NEW RankPoints;
  566. }
  567. void TearDownGameSpy( void )
  568. {
  569. // save off cached stats
  570. if (TheGameSpyInfo && TheGameSpyInfo->getLocalProfileID())
  571. {
  572. // /* This was done on the score screen, so there is no need to do it now.
  573. // *
  574. PSPlayerStats localPSStats = TheGameSpyPSMessageQueue->findPlayerStatsByID(TheGameSpyInfo->getLocalProfileID());
  575. if (localPSStats.id != 0)
  576. {
  577. GameSpyMiscPreferences mPref;
  578. mPref.setCachedStats(GameSpyPSMessageQueueInterface::formatPlayerKVPairs(localPSStats).c_str());
  579. mPref.write();
  580. }
  581. // */
  582. }
  583. // End our threads before we kill off the singletons they reference. No crashy-crash for you!
  584. if (TheGameSpyPSMessageQueue)
  585. TheGameSpyPSMessageQueue->endThread();
  586. if (TheGameSpyBuddyMessageQueue)
  587. TheGameSpyBuddyMessageQueue->endThread();
  588. if (TheGameSpyPeerMessageQueue)
  589. TheGameSpyPeerMessageQueue->endThread();
  590. if (ThePinger)
  591. ThePinger->endThreads();
  592. if(TheRankPointValues)
  593. {
  594. delete TheRankPointValues;
  595. TheRankPointValues = NULL;
  596. }
  597. if (TheGameSpyPSMessageQueue)
  598. {
  599. delete TheGameSpyPSMessageQueue;
  600. TheGameSpyPSMessageQueue = NULL;
  601. }
  602. if (TheGameSpyBuddyMessageQueue)
  603. {
  604. delete TheGameSpyBuddyMessageQueue;
  605. TheGameSpyBuddyMessageQueue = NULL;
  606. }
  607. if (TheGameSpyPeerMessageQueue)
  608. {
  609. delete TheGameSpyPeerMessageQueue;
  610. TheGameSpyPeerMessageQueue = NULL;
  611. }
  612. if (TheGameSpyInfo)
  613. {
  614. if (TheGameSpyInfo->getInternalIP())
  615. {
  616. // we've logged in before. mark us as logging out.
  617. SignalUIInteraction(SHELL_SCRIPT_HOOK_GENERALS_ONLINE_LOGOUT);
  618. }
  619. delete TheGameSpyInfo;
  620. TheGameSpyInfo = NULL;
  621. }
  622. if (ThePinger)
  623. {
  624. delete ThePinger;
  625. ThePinger = NULL;
  626. }
  627. if (TheLadderList)
  628. {
  629. delete TheLadderList;
  630. TheLadderList = NULL;
  631. }
  632. if (TheGameSpyConfig)
  633. {
  634. delete TheGameSpyConfig;
  635. TheGameSpyConfig = NULL;
  636. }
  637. // make sure the notification box doesn't exist
  638. deleteNotificationBox();
  639. }
  640. void GameSpyInfo::addToIgnoreList( AsciiString nick )
  641. {
  642. m_ignoreList.insert(nick);
  643. }
  644. void GameSpyInfo::removeFromIgnoreList( AsciiString nick )
  645. {
  646. m_ignoreList.erase(nick);
  647. }
  648. Bool GameSpyInfo::isIgnored( AsciiString nick )
  649. {
  650. return m_ignoreList.find(nick) != m_ignoreList.end();
  651. }
  652. IgnoreList GameSpyInfo::returnIgnoreList( void )
  653. {
  654. return m_ignoreList;
  655. }
  656. void GameSpyInfo::addToSavedIgnoreList( Int profileID, AsciiString nick)
  657. {
  658. m_savedIgnoreMap[profileID] = nick;
  659. IgnorePreferences pref;
  660. pref.setIgnore(nick, profileID, true);
  661. pref.write();
  662. }
  663. void GameSpyInfo::removeFromSavedIgnoreList( Int profileID )
  664. {
  665. m_savedIgnoreMap.erase(profileID);
  666. IgnorePreferences pref;
  667. pref.setIgnore(AsciiString::TheEmptyString, profileID, false);
  668. pref.write();
  669. }
  670. Bool GameSpyInfo::isSavedIgnored( Int profileID )
  671. {
  672. return m_savedIgnoreMap.find(profileID) != m_savedIgnoreMap.end();
  673. }
  674. SavedIgnoreMap GameSpyInfo::returnSavedIgnoreList( void )
  675. {
  676. return m_savedIgnoreMap;
  677. }
  678. static Int grabHexInt(const char *s)
  679. {
  680. char tmp[5] = "0xff";
  681. tmp[2] = s[0];
  682. tmp[3] = s[1];
  683. Int b = strtol(tmp, NULL, 16);
  684. return b;
  685. }
  686. Int GameSpyInfo::getPingValue( const AsciiString& otherPing )
  687. {
  688. if (m_pingString.getLength() != otherPing.getLength())
  689. {
  690. return TheGameSpyConfig->getPingTimeoutInMs();
  691. }
  692. if (m_pingString.getLength() % 2 != 0)
  693. {
  694. return TheGameSpyConfig->getPingTimeoutInMs();
  695. }
  696. Int best = 255+255;
  697. const char *myStr = m_pingString.str();
  698. const char *otherStr = otherPing.str();
  699. while (*myStr)
  700. {
  701. Int myVal = grabHexInt(myStr);
  702. Int otherVal = grabHexInt(otherStr);
  703. Int val = myVal + otherVal;
  704. best = (val < best) ? val : best;
  705. myStr += 2;
  706. otherStr += 2;
  707. }
  708. return best * TheGameSpyConfig->getPingTimeoutInMs() / (255+255);
  709. }
  710. Bool PlayerInfo::isIgnored( void )
  711. {
  712. return (m_profileID)?TheGameSpyInfo->isSavedIgnored(m_profileID):TheGameSpyInfo->isIgnored(m_name);
  713. }
  714. void GameSpyInfo::loadSavedIgnoreList( void )
  715. {
  716. m_savedIgnoreMap.clear();
  717. IgnorePreferences prefs;
  718. m_savedIgnoreMap = prefs.getIgnores();
  719. }
  720. void GameSpyInfo::setDisallowAsianText( Bool val )
  721. {
  722. m_disallowAsainText = val;
  723. }
  724. void GameSpyInfo::setDisallowNonAsianText( Bool val )
  725. {
  726. m_disallowNonAsianText = val;
  727. }
  728. Bool GameSpyInfo::getDisallowAsianText( void )
  729. {
  730. return m_disallowAsainText;
  731. }
  732. Bool GameSpyInfo::getDisallowNonAsianText(void )
  733. {
  734. return m_disallowNonAsianText;
  735. }
  736. void GameSpyInfo::setMaxMessagesPerUpdate( Int num )
  737. {
  738. m_maxMessagesPerUpdate = num;
  739. }
  740. Int GameSpyInfo::getMaxMessagesPerUpdate( void )
  741. {
  742. return m_maxMessagesPerUpdate;
  743. }
  744. /**This function is used to force an update of player's gamespy stats with an additional
  745. disconnection. This is used upon starting a new game so that if user disconnects prior
  746. to finishing game, the disconnection stays on the server. If he completes the game, we
  747. remove this extra disconnection inside of populatePlayerInfo() on the ScoreScreen. This
  748. seems like the only secure way to handle this issue since users can abort the process
  749. before we can detect/log disconnections.*/
  750. void GameSpyInfo::updateAdditionalGameSpyDisconnections(Int count)
  751. {
  752. if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame())
  753. {
  754. Int localID = TheGameSpyInfo->getLocalProfileID();
  755. PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(localID);
  756. Player *player=ThePlayerList->getLocalPlayer();
  757. Int ptIdx;
  758. const PlayerTemplate *myTemplate = player->getPlayerTemplate();
  759. DEBUG_LOG(("myTemplate = %X(%s)\n", myTemplate, myTemplate->getName().str()));
  760. for (ptIdx = 0; ptIdx < ThePlayerTemplateStore->getPlayerTemplateCount(); ++ptIdx)
  761. {
  762. const PlayerTemplate *nthTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(ptIdx);
  763. DEBUG_LOG(("nthTemplate = %X(%s)\n", nthTemplate, nthTemplate->getName().str()));
  764. if (nthTemplate == myTemplate)
  765. {
  766. break;
  767. }
  768. }
  769. Bool anyAI = FALSE;
  770. for (Int i=0; i<MAX_SLOTS; ++i)
  771. {
  772. const GameSlot *slot = TheGameInfo->getConstSlot(i);
  773. if (slot->isAI())
  774. {
  775. anyAI = TRUE;
  776. }
  777. }
  778. //Check for cases where we're not tracking stats.
  779. if (anyAI || stats.id == 0 || myTemplate->isObserver() || player->getPlayerType() != PLAYER_HUMAN || player->isPlayerObserver())
  780. return;
  781. Int disCons=stats.discons[ptIdx];
  782. disCons += count;
  783. if (disCons < 0)
  784. { DEBUG_LOG(("updateAdditionalGameSpyDisconnections() - disconnection count below zero\n"));
  785. return; //something is wrong here
  786. }
  787. stats.discons[ptIdx] = disCons; //add an additional disconnection to their stats.
  788. //Add an additional disconnection to player stats.
  789. PSRequest req;
  790. req.requestType = PSRequest::PSREQUEST_UPDATEPLAYERSTATS;
  791. req.email = TheGameSpyInfo->getLocalEmail().str();
  792. req.nick = TheGameSpyInfo->getLocalBaseName().str();
  793. req.password = TheGameSpyInfo->getLocalPassword().str();
  794. req.player = stats;
  795. req.addDesync = FALSE;
  796. req.addDiscon = FALSE;
  797. req.lastHouse = ptIdx;
  798. TheGameSpyPSMessageQueue->addRequest(req);
  799. TheGameSpyPSMessageQueue->trackPlayerStats(stats);
  800. // force an update of our shtuff
  801. PSResponse newResp;
  802. newResp.responseType = PSResponse::PSRESPONSE_PLAYERSTATS;
  803. newResp.player = stats;
  804. TheGameSpyPSMessageQueue->addResponse(newResp);
  805. // cache our stuff for easy reading next time
  806. GameSpyMiscPreferences mPref;
  807. mPref.setCachedStats(GameSpyPSMessageQueueInterface::formatPlayerKVPairs(stats).c_str());
  808. mPref.write();
  809. }
  810. }