/* * GlobalLobbyClient.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "GlobalLobbyClient.h" #include "GlobalLobbyLoginWindow.h" #include "GlobalLobbyWindow.h" #include "../gui/CGuiHandler.h" #include "../gui/WindowHandler.h" #include "../windows/InfoWindows.h" #include "../CServerHandler.h" #include "../../lib/CConfigHandler.h" #include "../../lib/MetaString.h" #include "../../lib/TextOperations.h" GlobalLobbyClient::GlobalLobbyClient() = default; GlobalLobbyClient::~GlobalLobbyClient() = default; static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0) { // FIXME: better/unified way to format date auto timeNowChrono = std::chrono::system_clock::now(); timeNowChrono += std::chrono::seconds(timeOffsetSeconds); return TextOperations::getFormattedTimeLocal(std::chrono::system_clock::to_time_t(timeNowChrono)); } void GlobalLobbyClient::onPacketReceived(const std::shared_ptr &, const std::vector & message) { boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); JsonNode json(message.data(), message.size()); if(json["type"].String() == "accountCreated") return receiveAccountCreated(json); if(json["type"].String() == "loginFailed") return receiveLoginFailed(json); if(json["type"].String() == "loginSuccess") return receiveLoginSuccess(json); if(json["type"].String() == "chatHistory") return receiveChatHistory(json); if(json["type"].String() == "chatMessage") return receiveChatMessage(json); if(json["type"].String() == "activeAccounts") return receiveActiveAccounts(json); if(json["type"].String() == "activeGameRooms") return receiveActiveGameRooms(json); if(json["type"].String() == "joinRoomSuccess") return receiveJoinRoomSuccess(json); throw std::runtime_error("Received unexpected message from lobby server: " + json["type"].String()); } void GlobalLobbyClient::receiveAccountCreated(const JsonNode & json) { auto loginWindowPtr = loginWindow.lock(); if(!loginWindowPtr || !GH.windows().topWindow()) throw std::runtime_error("lobby connection finished without active login window!"); { Settings configID = settings.write["lobby"]["accountID"]; configID->String() = json["accountID"].String(); Settings configName = settings.write["lobby"]["displayName"]; configName->String() = json["displayName"].String(); Settings configCookie = settings.write["lobby"]["accountCookie"]; configCookie->String() = json["accountCookie"].String(); } sendClientLogin(); } void GlobalLobbyClient::receiveLoginFailed(const JsonNode & json) { auto loginWindowPtr = loginWindow.lock(); if(!loginWindowPtr || !GH.windows().topWindow()) throw std::runtime_error("lobby connection finished without active login window!"); loginWindowPtr->onConnectionFailed(json["reason"].String()); } void GlobalLobbyClient::receiveLoginSuccess(const JsonNode & json) { { Settings configCookie = settings.write["lobby"]["accountCookie"]; configCookie->String() = json["accountCookie"].String(); Settings configName = settings.write["lobby"]["displayName"]; configName->String() = json["displayName"].String(); } auto loginWindowPtr = loginWindow.lock(); if(!loginWindowPtr || !GH.windows().topWindow()) throw std::runtime_error("lobby connection finished without active login window!"); loginWindowPtr->onConnectionSuccess(); } void GlobalLobbyClient::receiveChatHistory(const JsonNode & json) { for(const auto & entry : json["messages"].Vector()) { std::string accountID = entry["accountID"].String(); std::string displayName = entry["displayName"].String(); std::string messageText = entry["messageText"].String(); int ageSeconds = entry["ageSeconds"].Integer(); std::string timeFormatted = getCurrentTimeFormatted(-ageSeconds); auto lobbyWindowPtr = lobbyWindow.lock(); if(lobbyWindowPtr) lobbyWindowPtr->onGameChatMessage(displayName, messageText, timeFormatted); } } void GlobalLobbyClient::receiveChatMessage(const JsonNode & json) { std::string accountID = json["accountID"].String(); std::string displayName = json["displayName"].String(); std::string messageText = json["messageText"].String(); std::string timeFormatted = getCurrentTimeFormatted(); auto lobbyWindowPtr = lobbyWindow.lock(); if(lobbyWindowPtr) lobbyWindowPtr->onGameChatMessage(displayName, messageText, timeFormatted); } void GlobalLobbyClient::receiveActiveAccounts(const JsonNode & json) { activeAccounts.clear(); for (auto const & jsonEntry : json["accounts"].Vector()) { GlobalLobbyAccount account; account.accountID = jsonEntry["accountID"].String(); account.displayName = jsonEntry["displayName"].String(); account.status = jsonEntry["status"].String(); activeAccounts.push_back(account); } auto lobbyWindowPtr = lobbyWindow.lock(); if(lobbyWindowPtr) lobbyWindowPtr->onActiveAccounts(activeAccounts); } void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json) { activeRooms.clear(); for (auto const & jsonEntry : json["gameRooms"].Vector()) { GlobalLobbyRoom room; room.gameRoomID = jsonEntry["gameRoomID"].String(); room.hostAccountID = jsonEntry["hostAccountID"].String(); room.hostAccountDisplayName = jsonEntry["hostAccountDisplayName"].String(); room.description = jsonEntry["description"].String(); // room.status = jsonEntry["status"].String(); room.playersCount = jsonEntry["playersCount"].Integer(); room.playersLimit = jsonEntry["playersLimit"].Integer(); activeRooms.push_back(room); } auto lobbyWindowPtr = lobbyWindow.lock(); if(lobbyWindowPtr) lobbyWindowPtr->onActiveRooms(activeRooms); } void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json) { // TODO: store "gameRoomID" field and use it for future queries } void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr & connection) { networkConnection = connection; JsonNode toSend; std::string accountID = settings["lobby"]["accountID"].String(); if(accountID.empty()) sendClientRegister(); else sendClientLogin(); } void GlobalLobbyClient::sendClientRegister() { JsonNode toSend; toSend["type"].String() = "clientRegister"; toSend["displayName"] = settings["lobby"]["displayName"]; sendMessage(toSend); } void GlobalLobbyClient::sendClientLogin() { JsonNode toSend; toSend["type"].String() = "clientLogin"; toSend["accountID"] = settings["lobby"]["accountID"]; toSend["accountCookie"] = settings["lobby"]["accountCookie"]; sendMessage(toSend); } void GlobalLobbyClient::onConnectionFailed(const std::string & errorMessage) { auto loginWindowPtr = loginWindow.lock(); if(!loginWindowPtr || !GH.windows().topWindow()) throw std::runtime_error("lobby connection failed without active login window!"); logGlobal->warn("Connection to game lobby failed! Reason: %s", errorMessage); loginWindowPtr->onConnectionFailed(errorMessage); } void GlobalLobbyClient::onDisconnected(const std::shared_ptr & connection) { assert(connection == networkConnection); networkConnection.reset(); GH.windows().popWindows(1); CInfoWindow::showInfoDialog("Connection to game lobby was lost!", {}); } void GlobalLobbyClient::sendMessage(const JsonNode & data) { std::string payloadString = data.toJson(true); // FIXME: find better approach uint8_t * payloadBegin = reinterpret_cast(payloadString.data()); uint8_t * payloadEnd = payloadBegin + payloadString.size(); std::vector payloadBuffer(payloadBegin, payloadEnd); networkConnection->sendPacket(payloadBuffer); } void GlobalLobbyClient::sendOpenPublicRoom() { JsonNode toSend; toSend["type"].String() = "openGameRoom"; toSend["hostAccountID"] = settings["lobby"]["accountID"]; toSend["roomType"].String() = "public"; sendMessage(toSend); } void GlobalLobbyClient::sendOpenPrivateRoom() { JsonNode toSend; toSend["type"].String() = "openGameRoom"; toSend["hostAccountID"] = settings["lobby"]["accountID"]; toSend["roomType"].String() = "private"; sendMessage(toSend); } void GlobalLobbyClient::connect() { std::string hostname = settings["lobby"]["hostname"].String(); int16_t port = settings["lobby"]["port"].Integer(); CSH->networkHandler->connectToRemote(*this, hostname, port); } bool GlobalLobbyClient::isConnected() { return networkConnection != nullptr; } std::shared_ptr GlobalLobbyClient::createLoginWindow() { auto loginWindowPtr = loginWindow.lock(); if(loginWindowPtr) return loginWindowPtr; auto loginWindowNew = std::make_shared(); loginWindow = loginWindowNew; return loginWindowNew; } std::shared_ptr GlobalLobbyClient::createLobbyWindow() { auto lobbyWindowPtr = lobbyWindow.lock(); if(lobbyWindowPtr) return lobbyWindowPtr; lobbyWindowPtr = std::make_shared(); lobbyWindow = lobbyWindowPtr; lobbyWindowLock = lobbyWindowPtr; return lobbyWindowPtr; } const std::vector & GlobalLobbyClient::getActiveAccounts() const { return activeAccounts; } const std::vector & GlobalLobbyClient::getActiveRooms() const { return activeRooms; } void GlobalLobbyClient::activateInterface() { if (!GH.windows().findWindows().empty()) return; if (!GH.windows().findWindows().empty()) return; if (isConnected()) GH.windows().pushWindow(createLobbyWindow()); else GH.windows().pushWindow(createLoginWindow()); }