GlobalLobbyClient.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /*
  2. * GlobalLobbyClient.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "GlobalLobbyClient.h"
  12. #include "GlobalLobbyInviteWindow.h"
  13. #include "GlobalLobbyLoginWindow.h"
  14. #include "GlobalLobbyObserver.h"
  15. #include "GlobalLobbyWindow.h"
  16. #include "../CGameInfo.h"
  17. #include "../CServerHandler.h"
  18. #include "../gui/CGuiHandler.h"
  19. #include "../gui/WindowHandler.h"
  20. #include "../mainmenu/CMainMenu.h"
  21. #include "../media/ISoundPlayer.h"
  22. #include "../windows/InfoWindows.h"
  23. #include "../../lib/CConfigHandler.h"
  24. #include "../../lib/json/JsonUtils.h"
  25. #include "../../lib/texts/CGeneralTextHandler.h"
  26. #include "../../lib/texts/MetaString.h"
  27. #include "../../lib/texts/TextOperations.h"
  28. GlobalLobbyClient::GlobalLobbyClient()
  29. {
  30. activeChannels.emplace_back("english");
  31. if (CGI->generaltexth->getPreferredLanguage() != "english")
  32. activeChannels.emplace_back(CGI->generaltexth->getPreferredLanguage());
  33. }
  34. GlobalLobbyClient::~GlobalLobbyClient() = default;
  35. void GlobalLobbyClient::onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<std::byte> & message)
  36. {
  37. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  38. JsonNode json(message.data(), message.size(), "<lobby network packet>");
  39. if(json["type"].String() == "accountCreated")
  40. return receiveAccountCreated(json);
  41. if(json["type"].String() == "operationFailed")
  42. return receiveOperationFailed(json);
  43. if(json["type"].String() == "clientLoginSuccess")
  44. return receiveClientLoginSuccess(json);
  45. if(json["type"].String() == "chatHistory")
  46. return receiveChatHistory(json);
  47. if(json["type"].String() == "chatMessage")
  48. return receiveChatMessage(json);
  49. if(json["type"].String() == "activeAccounts")
  50. return receiveActiveAccounts(json);
  51. if(json["type"].String() == "activeGameRooms")
  52. return receiveActiveGameRooms(json);
  53. if(json["type"].String() == "joinRoomSuccess")
  54. return receiveJoinRoomSuccess(json);
  55. if(json["type"].String() == "inviteReceived")
  56. return receiveInviteReceived(json);
  57. if(json["type"].String() == "matchesHistory")
  58. return receiveMatchesHistory(json);
  59. logGlobal->error("Received unexpected message from lobby server: %s", json["type"].String());
  60. }
  61. void GlobalLobbyClient::receiveAccountCreated(const JsonNode & json)
  62. {
  63. auto loginWindowPtr = loginWindow.lock();
  64. if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
  65. throw std::runtime_error("lobby connection finished without active login window!");
  66. {
  67. setAccountID(json["accountID"].String());
  68. setAccountDisplayName(json["displayName"].String());
  69. setAccountCookie(json["accountCookie"].String());
  70. }
  71. sendClientLogin();
  72. }
  73. void GlobalLobbyClient::receiveOperationFailed(const JsonNode & json)
  74. {
  75. auto loginWindowPtr = loginWindow.lock();
  76. if(loginWindowPtr)
  77. loginWindowPtr->onConnectionFailed(json["reason"].String());
  78. logGlobal->warn("Operation failed! Reason: %s", json["reason"].String());
  79. // TODO: handle errors in lobby menu
  80. }
  81. void GlobalLobbyClient::receiveClientLoginSuccess(const JsonNode & json)
  82. {
  83. accountLoggedIn = true;
  84. setAccountDisplayName(json["displayName"].String());
  85. setAccountCookie(json["accountCookie"].String());
  86. auto loginWindowPtr = loginWindow.lock();
  87. if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
  88. throw std::runtime_error("lobby connection finished without active login window!");
  89. loginWindowPtr->onLoginSuccess();
  90. }
  91. void GlobalLobbyClient::receiveChatHistory(const JsonNode & json)
  92. {
  93. std::string channelType = json["channelType"].String();
  94. std::string channelName = json["channelName"].String();
  95. std::string channelKey = channelType + '_' + channelName;
  96. // create empty entry, potentially replacing any pre-existing data
  97. chatHistory[channelKey] = {};
  98. auto lobbyWindowPtr = lobbyWindow.lock();
  99. for(const auto & entry : json["messages"].Vector())
  100. {
  101. GlobalLobbyChannelMessage message;
  102. message.accountID = entry["accountID"].String();
  103. message.displayName = entry["displayName"].String();
  104. message.messageText = entry["messageText"].String();
  105. std::chrono::seconds ageSeconds (entry["ageSeconds"].Integer());
  106. message.timeFormatted = TextOperations::getCurrentFormattedTimeLocal(-ageSeconds);
  107. chatHistory[channelKey].push_back(message);
  108. if(lobbyWindowPtr && lobbyWindowPtr->isChannelOpen(channelType, channelName))
  109. lobbyWindowPtr->onGameChatMessage(message.displayName, message.messageText, message.timeFormatted, channelType, channelName);
  110. }
  111. if(lobbyWindowPtr && lobbyWindowPtr->isChannelOpen(channelType, channelName))
  112. lobbyWindowPtr->refreshChatText();
  113. }
  114. void GlobalLobbyClient::receiveChatMessage(const JsonNode & json)
  115. {
  116. GlobalLobbyChannelMessage message;
  117. message.accountID = json["accountID"].String();
  118. message.displayName = json["displayName"].String();
  119. message.messageText = json["messageText"].String();
  120. message.timeFormatted = TextOperations::getCurrentFormattedTimeLocal();
  121. std::string channelType = json["channelType"].String();
  122. std::string channelName = json["channelName"].String();
  123. std::string channelKey = channelType + '_' + channelName;
  124. chatHistory[channelKey].push_back(message);
  125. auto lobbyWindowPtr = lobbyWindow.lock();
  126. if(lobbyWindowPtr)
  127. {
  128. lobbyWindowPtr->onGameChatMessage(message.displayName, message.messageText, message.timeFormatted, channelType, channelName);
  129. lobbyWindowPtr->refreshChatText();
  130. if(channelType == "player" || lobbyWindowPtr->isChannelOpen(channelType, channelName))
  131. CCS->soundh->playSound(AudioPath::builtin("CHAT"));
  132. }
  133. }
  134. void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json)
  135. {
  136. activeAccounts.clear();
  137. for(const auto & jsonEntry : json["accounts"].Vector())
  138. {
  139. GlobalLobbyAccount account;
  140. account.accountID = jsonEntry["accountID"].String();
  141. account.displayName = jsonEntry["displayName"].String();
  142. account.status = jsonEntry["status"].String();
  143. activeAccounts.push_back(account);
  144. }
  145. auto lobbyWindowPtr = lobbyWindow.lock();
  146. if(lobbyWindowPtr)
  147. lobbyWindowPtr->onActiveAccounts(activeAccounts);
  148. for (auto const & window : GH.windows().findWindows<GlobalLobbyObserver>())
  149. window->onActiveAccounts(activeAccounts);
  150. }
  151. void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
  152. {
  153. activeRooms.clear();
  154. for(const auto & jsonEntry : json["gameRooms"].Vector())
  155. {
  156. GlobalLobbyRoom room;
  157. room.gameRoomID = jsonEntry["gameRoomID"].String();
  158. room.hostAccountID = jsonEntry["hostAccountID"].String();
  159. room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
  160. room.description = jsonEntry["description"].String();
  161. room.statusID = jsonEntry["status"].String();
  162. room.gameVersion = jsonEntry["version"].String();
  163. room.modList = ModVerificationInfo::jsonDeserializeList(jsonEntry["mods"]);
  164. std::chrono::seconds ageSeconds (jsonEntry["ageSeconds"].Integer());
  165. room.startDateFormatted = TextOperations::getCurrentFormattedDateTimeLocal(-ageSeconds);
  166. for(const auto & jsonParticipant : jsonEntry["participants"].Vector())
  167. {
  168. GlobalLobbyAccount account;
  169. account.accountID = jsonParticipant["accountID"].String();
  170. account.displayName = jsonParticipant["displayName"].String();
  171. room.participants.push_back(account);
  172. }
  173. for(const auto & jsonParticipant : jsonEntry["invited"].Vector())
  174. {
  175. GlobalLobbyAccount account;
  176. account.accountID = jsonParticipant["accountID"].String();
  177. account.displayName = jsonParticipant["displayName"].String();
  178. room.invited.push_back(account);
  179. }
  180. room.playerLimit = jsonEntry["playerLimit"].Integer();
  181. activeRooms.push_back(room);
  182. }
  183. auto lobbyWindowPtr = lobbyWindow.lock();
  184. if(lobbyWindowPtr)
  185. lobbyWindowPtr->onActiveGameRooms(activeRooms);
  186. for (auto const & window : GH.windows().findWindows<GlobalLobbyObserver>())
  187. window->onActiveGameRooms(activeRooms);
  188. }
  189. void GlobalLobbyClient::receiveMatchesHistory(const JsonNode & json)
  190. {
  191. matchesHistory.clear();
  192. for(const auto & jsonEntry : json["matchesHistory"].Vector())
  193. {
  194. GlobalLobbyRoom room;
  195. room.gameRoomID = jsonEntry["gameRoomID"].String();
  196. room.hostAccountID = jsonEntry["hostAccountID"].String();
  197. room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String();
  198. room.description = jsonEntry["description"].String();
  199. room.statusID = jsonEntry["status"].String();
  200. std::chrono::seconds ageSeconds (jsonEntry["ageSeconds"].Integer());
  201. room.startDateFormatted = TextOperations::getCurrentFormattedDateTimeLocal(-ageSeconds);
  202. for(const auto & jsonParticipant : jsonEntry["participants"].Vector())
  203. {
  204. GlobalLobbyAccount account;
  205. account.accountID = jsonParticipant["accountID"].String();
  206. account.displayName = jsonParticipant["displayName"].String();
  207. room.participants.push_back(account);
  208. }
  209. room.playerLimit = jsonEntry["playerLimit"].Integer();
  210. matchesHistory.push_back(room);
  211. }
  212. auto lobbyWindowPtr = lobbyWindow.lock();
  213. if(lobbyWindowPtr)
  214. lobbyWindowPtr->onMatchesHistory(matchesHistory);
  215. }
  216. void GlobalLobbyClient::receiveInviteReceived(const JsonNode & json)
  217. {
  218. auto lobbyWindowPtr = lobbyWindow.lock();
  219. std::string gameRoomID = json["gameRoomID"].String();
  220. std::string accountID = json["accountID"].String();
  221. activeInvites.insert(gameRoomID);
  222. if(lobbyWindowPtr)
  223. {
  224. std::string message = MetaString::createFromTextID("vcmi.lobby.invite.notification").toString();
  225. std::string time = TextOperations::getCurrentFormattedTimeLocal();
  226. lobbyWindowPtr->onGameChatMessage("System", message, time, "player", accountID);
  227. lobbyWindowPtr->onInviteReceived(gameRoomID);
  228. lobbyWindowPtr->refreshChatText();
  229. }
  230. CCS->soundh->playSound(AudioPath::builtin("CHAT"));
  231. }
  232. void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json)
  233. {
  234. if (json["proxyMode"].Bool())
  235. {
  236. CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOBBY_GUEST, { CSH->getGlobalLobby().getAccountDisplayName() });
  237. CSH->loadMode = ELoadMode::MULTI;
  238. std::string hostname = getServerHost();
  239. uint16_t port = getServerPort();
  240. CSH->connectToServer(hostname, port);
  241. }
  242. // NOTE: must be set after CSH->resetStateForLobby call
  243. currentGameRoomUUID = json["gameRoomID"].String();
  244. }
  245. void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr<INetworkConnection> & connection)
  246. {
  247. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  248. networkConnection = connection;
  249. auto loginWindowPtr = loginWindow.lock();
  250. if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
  251. throw std::runtime_error("lobby connection established without active login window!");
  252. loginWindowPtr->onConnectionSuccess();
  253. }
  254. void GlobalLobbyClient::sendClientRegister(const std::string & accountName)
  255. {
  256. JsonNode toSend;
  257. toSend["type"].String() = "clientRegister";
  258. toSend["displayName"].String() = accountName;
  259. toSend["language"].String() = CGI->generaltexth->getPreferredLanguage();
  260. toSend["version"].String() = VCMI_VERSION_STRING;
  261. sendMessage(toSend);
  262. }
  263. void GlobalLobbyClient::sendClientLogin()
  264. {
  265. JsonNode toSend;
  266. toSend["type"].String() = "clientLogin";
  267. toSend["accountID"].String() = getAccountID();
  268. toSend["accountCookie"].String() = getAccountCookie();
  269. toSend["language"].String() = CGI->generaltexth->getPreferredLanguage();
  270. toSend["version"].String() = VCMI_VERSION_STRING;
  271. sendMessage(toSend);
  272. }
  273. void GlobalLobbyClient::onConnectionFailed(const std::string & errorMessage)
  274. {
  275. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  276. auto loginWindowPtr = loginWindow.lock();
  277. if(!loginWindowPtr || !GH.windows().topWindow<GlobalLobbyLoginWindow>())
  278. throw std::runtime_error("lobby connection failed without active login window!");
  279. logGlobal->warn("Connection to game lobby failed! Reason: %s", errorMessage);
  280. loginWindowPtr->onConnectionFailed(errorMessage);
  281. }
  282. void GlobalLobbyClient::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
  283. {
  284. boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
  285. assert(connection == networkConnection);
  286. networkConnection.reset();
  287. accountLoggedIn = false;
  288. while (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
  289. {
  290. // if global lobby is open, pop all dialogs on top of it as well as lobby itself
  291. GH.windows().popWindows(1);
  292. }
  293. CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {});
  294. }
  295. void GlobalLobbyClient::sendMessage(const JsonNode & data)
  296. {
  297. assert(JsonUtils::validate(data, "vcmi:lobbyProtocol/" + data["type"].String(), data["type"].String() + " pack"));
  298. networkConnection->sendPacket(data.toBytes());
  299. }
  300. void GlobalLobbyClient::sendOpenRoom(const std::string & mode, int playerLimit)
  301. {
  302. JsonNode toSend;
  303. toSend["type"].String() = "activateGameRoom";
  304. toSend["hostAccountID"].String() = getAccountID();
  305. toSend["roomType"].String() = mode;
  306. toSend["playerLimit"].Integer() = playerLimit;
  307. sendMessage(toSend);
  308. }
  309. void GlobalLobbyClient::connect()
  310. {
  311. std::string hostname = getServerHost();
  312. uint16_t port = getServerPort();
  313. CSH->getNetworkHandler().connectToRemote(*this, hostname, port);
  314. }
  315. bool GlobalLobbyClient::isLoggedIn() const
  316. {
  317. return networkConnection != nullptr && accountLoggedIn;
  318. }
  319. bool GlobalLobbyClient::isConnected() const
  320. {
  321. return networkConnection != nullptr;
  322. }
  323. std::shared_ptr<GlobalLobbyLoginWindow> GlobalLobbyClient::createLoginWindow()
  324. {
  325. auto loginWindowPtr = loginWindow.lock();
  326. if(loginWindowPtr)
  327. return loginWindowPtr;
  328. auto loginWindowNew = std::make_shared<GlobalLobbyLoginWindow>();
  329. loginWindow = loginWindowNew;
  330. return loginWindowNew;
  331. }
  332. std::shared_ptr<GlobalLobbyWindow> GlobalLobbyClient::createLobbyWindow()
  333. {
  334. auto lobbyWindowPtr = lobbyWindow.lock();
  335. if(lobbyWindowPtr)
  336. return lobbyWindowPtr;
  337. lobbyWindowPtr = std::make_shared<GlobalLobbyWindow>();
  338. lobbyWindow = lobbyWindowPtr;
  339. lobbyWindowLock = lobbyWindowPtr;
  340. return lobbyWindowPtr;
  341. }
  342. const std::vector<GlobalLobbyAccount> & GlobalLobbyClient::getActiveAccounts() const
  343. {
  344. return activeAccounts;
  345. }
  346. const std::vector<GlobalLobbyRoom> & GlobalLobbyClient::getActiveRooms() const
  347. {
  348. return activeRooms;
  349. }
  350. const std::vector<std::string> & GlobalLobbyClient::getActiveChannels() const
  351. {
  352. return activeChannels;
  353. }
  354. const std::vector<GlobalLobbyRoom> & GlobalLobbyClient::getMatchesHistory() const
  355. {
  356. return matchesHistory;
  357. }
  358. const GlobalLobbyRoom & GlobalLobbyClient::getActiveRoomByName(const std::string & roomUUID) const
  359. {
  360. for (auto const & room : activeRooms)
  361. {
  362. if (room.gameRoomID == roomUUID)
  363. return room;
  364. }
  365. throw std::out_of_range("Failed to find room with UUID of " + roomUUID);
  366. }
  367. const std::vector<GlobalLobbyChannelMessage> & GlobalLobbyClient::getChannelHistory(const std::string & channelType, const std::string & channelName) const
  368. {
  369. static const std::vector<GlobalLobbyChannelMessage> emptyVector;
  370. std::string keyToTest = channelType + '_' + channelName;
  371. if (chatHistory.count(keyToTest) == 0)
  372. {
  373. if (channelType != "global")
  374. {
  375. JsonNode toSend;
  376. toSend["type"].String() = "requestChatHistory";
  377. toSend["channelType"].String() = channelType;
  378. toSend["channelName"].String() = channelName;
  379. CSH->getGlobalLobby().sendMessage(toSend);
  380. }
  381. return emptyVector;
  382. }
  383. else
  384. return chatHistory.at(keyToTest);
  385. }
  386. void GlobalLobbyClient::activateInterface()
  387. {
  388. if (GH.windows().topWindow<GlobalLobbyWindow>() != nullptr)
  389. {
  390. GH.windows().popWindows(1);
  391. return;
  392. }
  393. if (!GH.windows().findWindows<GlobalLobbyWindow>().empty())
  394. return;
  395. if (!GH.windows().findWindows<GlobalLobbyLoginWindow>().empty())
  396. return;
  397. if (isLoggedIn())
  398. GH.windows().pushWindow(createLobbyWindow());
  399. else
  400. GH.windows().pushWindow(createLoginWindow());
  401. GH.windows().topWindow<CIntObject>()->center();
  402. }
  403. void GlobalLobbyClient::activateRoomInviteInterface()
  404. {
  405. GH.windows().createAndPushWindow<GlobalLobbyInviteWindow>();
  406. }
  407. void GlobalLobbyClient::setAccountID(const std::string & accountID)
  408. {
  409. Settings configID = persistentStorage.write["lobby"][getServerHost()]["accountID"];
  410. configID->String() = accountID;
  411. }
  412. void GlobalLobbyClient::setAccountCookie(const std::string & accountCookie)
  413. {
  414. Settings configCookie = persistentStorage.write["lobby"][getServerHost()]["accountCookie"];
  415. configCookie->String() = accountCookie;
  416. }
  417. void GlobalLobbyClient::setAccountDisplayName(const std::string & accountDisplayName)
  418. {
  419. Settings configName = persistentStorage.write["lobby"][getServerHost()]["displayName"];
  420. configName->String() = accountDisplayName;
  421. }
  422. const std::string & GlobalLobbyClient::getAccountID() const
  423. {
  424. return persistentStorage["lobby"][getServerHost()]["accountID"].String();
  425. }
  426. const std::string & GlobalLobbyClient::getAccountCookie() const
  427. {
  428. return persistentStorage["lobby"][getServerHost()]["accountCookie"].String();
  429. }
  430. const std::string & GlobalLobbyClient::getAccountDisplayName() const
  431. {
  432. return persistentStorage["lobby"][getServerHost()]["displayName"].String();
  433. }
  434. const std::string & GlobalLobbyClient::getServerHost() const
  435. {
  436. return settings["lobby"]["hostname"].String();
  437. }
  438. uint16_t GlobalLobbyClient::getServerPort() const
  439. {
  440. return settings["lobby"]["port"].Integer();
  441. }
  442. void GlobalLobbyClient::sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection)
  443. {
  444. JsonNode toSend;
  445. toSend["type"].String() = "clientProxyLogin";
  446. toSend["accountID"].String() = getAccountID();
  447. toSend["accountCookie"].String() = getAccountCookie();
  448. toSend["gameRoomID"].String() = currentGameRoomUUID;
  449. assert(JsonUtils::validate(toSend, "vcmi:lobbyProtocol/" + toSend["type"].String(), toSend["type"].String() + " pack"));
  450. netConnection->sendPacket(toSend.toBytes());
  451. }
  452. void GlobalLobbyClient::resetMatchState()
  453. {
  454. currentGameRoomUUID.clear();
  455. }
  456. const std::string & GlobalLobbyClient::getCurrentGameRoomID() const
  457. {
  458. return currentGameRoomUUID;
  459. }
  460. void GlobalLobbyClient::sendMatchChatMessage(const std::string & messageText)
  461. {
  462. if (!isLoggedIn())
  463. return; // we are not playing with lobby
  464. if (currentGameRoomUUID.empty())
  465. return; // we are not playing through lobby
  466. JsonNode toSend;
  467. toSend["type"].String() = "sendChatMessage";
  468. toSend["channelType"].String() = "match";
  469. toSend["channelName"].String() = currentGameRoomUUID;
  470. toSend["messageText"].String() = messageText;
  471. assert(TextOperations::isValidUnicodeString(messageText));
  472. CSH->getGlobalLobby().sendMessage(toSend);
  473. }
  474. bool GlobalLobbyClient::isInvitedToRoom(const std::string & gameRoomID)
  475. {
  476. if (activeInvites.count(gameRoomID) > 0)
  477. return true;
  478. const auto & gameRoom = CSH->getGlobalLobby().getActiveRoomByName(gameRoomID);
  479. for (auto const & invited : gameRoom.invited)
  480. {
  481. if (invited.accountID == getAccountID())
  482. return true;
  483. }
  484. return false;
  485. }