123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852 |
- /*
- * LobbyServer.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 "LobbyServer.h"
- #include "LobbyDatabase.h"
- #include "../lib/json/JsonFormatException.h"
- #include "../lib/json/JsonNode.h"
- #include "../lib/json/JsonUtils.h"
- #include "../lib/texts/Languages.h"
- #include "../lib/texts/TextOperations.h"
- #include <boost/uuid/uuid_generators.hpp>
- #include <boost/uuid/uuid_io.hpp>
- bool LobbyServer::isAccountNameValid(const std::string & accountName) const
- {
- // Arbitrary limit on account name length.
- // Can be extended if there are no issues with UI space
- if(accountName.size() < 4)
- return false;
- if(accountName.size() > 20)
- return false;
- // For now permit only latin alphabet and numbers
- // Can be extended, but makes sure that such symbols will be present in all H3 fonts
- for(const auto & c : accountName)
- if(!std::isalnum(c))
- return false;
- return true;
- }
- std::string LobbyServer::sanitizeChatMessage(const std::string & inputString) const
- {
- static const std::string blacklist = "{}";
- std::string sanitized;
- for(const auto & ch : inputString)
- {
- // Remove all control characters
- if (ch >= '\0' && ch < ' ')
- continue;
- // Remove blacklisted characters such as brackets that are used for text formatting
- if (blacklist.find(ch) != std::string::npos)
- continue;
- sanitized += ch;
- }
- return boost::trim_copy(sanitized);
- }
- NetworkConnectionPtr LobbyServer::findAccount(const std::string & accountID) const
- {
- for(const auto & account : activeAccounts)
- if(account.second == accountID)
- return account.first;
- return nullptr;
- }
- NetworkConnectionPtr LobbyServer::findGameRoom(const std::string & gameRoomID) const
- {
- for(const auto & account : activeGameRooms)
- if(account.second == gameRoomID)
- return account.first;
- return nullptr;
- }
- void LobbyServer::sendMessage(const NetworkConnectionPtr & target, const JsonNode & json)
- {
- logGlobal->info("Sending message of type %s", json["type"].String());
- assert(JsonUtils::validate(json, "vcmi:lobbyProtocol/" + json["type"].String(), json["type"].String() + " pack"));
- target->sendPacket(json.toBytes());
- }
- void LobbyServer::sendAccountCreated(const NetworkConnectionPtr & target, const std::string & accountID, const std::string & accountCookie)
- {
- JsonNode reply;
- reply["type"].String() = "accountCreated";
- reply["accountID"].String() = accountID;
- reply["accountCookie"].String() = accountCookie;
- sendMessage(target, reply);
- }
- void LobbyServer::sendInviteReceived(const NetworkConnectionPtr & target, const std::string & accountID, const std::string & gameRoomID)
- {
- JsonNode reply;
- reply["type"].String() = "inviteReceived";
- reply["accountID"].String() = accountID;
- reply["gameRoomID"].String() = gameRoomID;
- sendMessage(target, reply);
- }
- void LobbyServer::sendOperationFailed(const NetworkConnectionPtr & target, const std::string & reason)
- {
- JsonNode reply;
- reply["type"].String() = "operationFailed";
- reply["reason"].String() = reason;
- sendMessage(target, reply);
- }
- void LobbyServer::sendClientLoginSuccess(const NetworkConnectionPtr & target, const std::string & accountCookie, const std::string & displayName)
- {
- JsonNode reply;
- reply["type"].String() = "clientLoginSuccess";
- reply["accountCookie"].String() = accountCookie;
- reply["displayName"].String() = displayName;
- sendMessage(target, reply);
- }
- void LobbyServer::sendServerLoginSuccess(const NetworkConnectionPtr & target, const std::string & accountCookie)
- {
- JsonNode reply;
- reply["type"].String() = "serverLoginSuccess";
- reply["accountCookie"].String() = accountCookie;
- sendMessage(target, reply);
- }
- void LobbyServer::sendFullChatHistory(const NetworkConnectionPtr & target, const std::string & channelType, const std::string & channelName, const std::string & channelNameForClient)
- {
- sendChatHistory(target, channelType, channelNameForClient, database->getFullMessageHistory(channelType, channelName));
- }
- void LobbyServer::sendRecentChatHistory(const NetworkConnectionPtr & target, const std::string & channelType, const std::string & channelName)
- {
- sendChatHistory(target, channelType, channelName, database->getRecentMessageHistory(channelType, channelName));
- }
- void LobbyServer::sendChatHistory(const NetworkConnectionPtr & target, const std::string & channelType, const std::string & channelName, const std::vector<LobbyChatMessage> & history)
- {
- JsonNode reply;
- reply["type"].String() = "chatHistory";
- reply["channelType"].String() = channelType;
- reply["channelName"].String() = channelName;
- reply["messages"].Vector(); // force creation of empty vector
- for(const auto & message : boost::adaptors::reverse(history))
- {
- JsonNode jsonEntry;
- jsonEntry["accountID"].String() = message.accountID;
- jsonEntry["displayName"].String() = message.displayName;
- jsonEntry["messageText"].String() = message.messageText;
- jsonEntry["ageSeconds"].Integer() = message.age.count();
- reply["messages"].Vector().push_back(jsonEntry);
- }
- sendMessage(target, reply);
- }
- void LobbyServer::broadcastActiveAccounts()
- {
- auto activeAccountsStats = database->getActiveAccounts();
- JsonNode reply;
- reply["type"].String() = "activeAccounts";
- reply["accounts"].Vector(); // force creation of empty vector
- for(const auto & account : activeAccountsStats)
- {
- JsonNode jsonEntry;
- jsonEntry["accountID"].String() = account.accountID;
- jsonEntry["displayName"].String() = account.displayName;
- jsonEntry["status"].String() = "In Lobby"; // TODO: in room status, in match status, offline status(?)
- reply["accounts"].Vector().push_back(jsonEntry);
- }
- for(const auto & connection : activeAccounts)
- sendMessage(connection.first, reply);
- }
- static JsonNode loadLobbyAccountToJson(const LobbyAccount & account)
- {
- JsonNode jsonEntry;
- jsonEntry["accountID"].String() = account.accountID;
- jsonEntry["displayName"].String() = account.displayName;
- return jsonEntry;
- }
- static JsonNode loadLobbyGameRoomToJson(const LobbyGameRoom & gameRoom)
- {
- static constexpr std::array LOBBY_ROOM_STATE_NAMES = {
- "idle",
- "public",
- "private",
- "busy",
- "cancelled",
- "closed"
- };
- JsonNode jsonEntry;
- jsonEntry["gameRoomID"].String() = gameRoom.roomID;
- jsonEntry["hostAccountID"].String() = gameRoom.hostAccountID;
- jsonEntry["hostAccountDisplayName"].String() = gameRoom.hostAccountDisplayName;
- jsonEntry["description"].String() = gameRoom.description;
- jsonEntry["version"].String() = gameRoom.version;
- jsonEntry["status"].String() = LOBBY_ROOM_STATE_NAMES[vstd::to_underlying(gameRoom.roomState)];
- jsonEntry["playerLimit"].Integer() = gameRoom.playerLimit;
- jsonEntry["ageSeconds"].Integer() = gameRoom.age.count();
- if (!gameRoom.modsJson.empty()) // not present in match history
- jsonEntry["mods"] = JsonNode(reinterpret_cast<const std::byte *>(gameRoom.modsJson.data()), gameRoom.modsJson.size(), "<lobby "+gameRoom.roomID+">");
- for(const auto & account : gameRoom.participants)
- jsonEntry["participants"].Vector().push_back(loadLobbyAccountToJson(account));
- for(const auto & account : gameRoom.invited)
- jsonEntry["invited"].Vector().push_back(loadLobbyAccountToJson(account));
- return jsonEntry;
- }
- void LobbyServer::sendMatchesHistory(const NetworkConnectionPtr & target)
- {
- std::string accountID = activeAccounts.at(target);
- auto matchesHistory = database->getAccountGameHistory(accountID);
- JsonNode reply;
- reply["type"].String() = "matchesHistory";
- reply["matchesHistory"].Vector(); // force creation of empty vector
- for(const auto & gameRoom : matchesHistory)
- reply["matchesHistory"].Vector().push_back(loadLobbyGameRoomToJson(gameRoom));
- sendMessage(target, reply);
- }
- JsonNode LobbyServer::prepareActiveGameRooms()
- {
- auto activeGameRoomStats = database->getActiveGameRooms();
- JsonNode reply;
- reply["type"].String() = "activeGameRooms";
- reply["gameRooms"].Vector(); // force creation of empty vector
- for(const auto & gameRoom : activeGameRoomStats)
- reply["gameRooms"].Vector().push_back(loadLobbyGameRoomToJson(gameRoom));
- return reply;
- }
- void LobbyServer::broadcastActiveGameRooms()
- {
- auto reply = prepareActiveGameRooms();
- for(const auto & connection : activeAccounts)
- sendMessage(connection.first, reply);
- }
- void LobbyServer::sendAccountJoinsRoom(const NetworkConnectionPtr & target, const std::string & accountID)
- {
- JsonNode reply;
- reply["type"].String() = "accountJoinsRoom";
- reply["accountID"].String() = accountID;
- sendMessage(target, reply);
- }
- void LobbyServer::sendJoinRoomSuccess(const NetworkConnectionPtr & target, const std::string & gameRoomID, bool proxyMode)
- {
- JsonNode reply;
- reply["type"].String() = "joinRoomSuccess";
- reply["gameRoomID"].String() = gameRoomID;
- reply["proxyMode"].Bool() = proxyMode;
- sendMessage(target, reply);
- }
- void LobbyServer::sendChatMessage(const NetworkConnectionPtr & target, const std::string & channelType, const std::string & channelName, const std::string & accountID, const std::string & displayName, const std::string & messageText)
- {
- JsonNode reply;
- reply["type"].String() = "chatMessage";
- reply["messageText"].String() = messageText;
- reply["accountID"].String() = accountID;
- reply["displayName"].String() = displayName;
- reply["channelType"].String() = channelType;
- reply["channelName"].String() = channelName;
- sendMessage(target, reply);
- }
- void LobbyServer::onNewConnection(const NetworkConnectionPtr & connection)
- {
- connection->setAsyncWritesEnabled(true);
- // no-op - waiting for incoming data
- }
- void LobbyServer::onDisconnected(const NetworkConnectionPtr & connection, const std::string & errorMessage)
- {
- if(activeAccounts.count(connection))
- {
- logGlobal->info("Account %s disconnecting. Accounts online: %d", activeAccounts.at(connection), activeAccounts.size() - 1);
- database->setAccountOnline(activeAccounts.at(connection), false);
- activeAccounts.erase(connection);
- }
- if(activeGameRooms.count(connection))
- {
- std::string gameRoomID = activeGameRooms.at(connection);
- logGlobal->info("Game room %s disconnecting. Rooms online: %d", gameRoomID, activeGameRooms.size() - 1);
- if (database->getGameRoomStatus(gameRoomID) == LobbyRoomState::BUSY)
- {
- database->setGameRoomStatus(gameRoomID, LobbyRoomState::CLOSED);
- for(const auto & accountConnection : activeAccounts)
- if (database->isPlayerInGameRoom(accountConnection.second, gameRoomID))
- sendMatchesHistory(accountConnection.first);
- }
- else
- database->setGameRoomStatus(gameRoomID, LobbyRoomState::CANCELLED);
- activeGameRooms.erase(connection);
- }
- if(activeProxies.count(connection))
- {
- const auto otherConnection = activeProxies.at(connection);
- if (otherConnection)
- otherConnection->close();
- activeProxies.erase(connection);
- activeProxies.erase(otherConnection);
- }
- broadcastActiveAccounts();
- broadcastActiveGameRooms();
- }
- JsonNode LobbyServer::parseAndValidateMessage(const std::vector<std::byte> & message) const
- {
- JsonParsingSettings parserSettings;
- parserSettings.mode = JsonParsingSettings::JsonFormatMode::JSON;
- parserSettings.maxDepth = 2;
- parserSettings.strict = true;
- JsonNode json;
- try
- {
- JsonNode jsonTemp(message.data(), message.size(), "<lobby message>");
- json = std::move(jsonTemp);
- }
- catch (const JsonFormatException & e)
- {
- logGlobal->info(std::string("Json parsing error encountered: ") + e.what());
- return JsonNode();
- }
- std::string messageType = json["type"].String();
- if (messageType.empty())
- {
- logGlobal->info("Json parsing error encountered: Message type not set!");
- return JsonNode();
- }
- std::string schemaName = "vcmi:lobbyProtocol/" + messageType;
- if (!JsonUtils::validate(json, schemaName, messageType + " pack"))
- {
- logGlobal->info("Json validation error encountered!");
- assert(0);
- return JsonNode();
- }
- return json;
- }
- void LobbyServer::onPacketReceived(const NetworkConnectionPtr & connection, const std::vector<std::byte> & message)
- {
- // proxy connection - no processing, only redirect
- if(activeProxies.count(connection))
- {
- auto lockedPtr = activeProxies.at(connection);
- if(lockedPtr)
- return lockedPtr->sendPacket(message);
- logGlobal->info("Received unexpected message for inactive proxy!");
- }
- JsonNode json = parseAndValidateMessage(message);
- std::string messageType = json["type"].String();
- // communication messages from vcmiclient
- if(activeAccounts.count(connection))
- {
- std::string accountName = activeAccounts.at(connection);
- logGlobal->info("%s: Received message of type %s", accountName, messageType);
- if(messageType == "sendChatMessage")
- return receiveSendChatMessage(connection, json);
- if(messageType == "requestChatHistory")
- return receiveRequestChatHistory(connection, json);
- if(messageType == "activateGameRoom")
- return receiveActivateGameRoom(connection, json);
- if(messageType == "joinGameRoom")
- return receiveJoinGameRoom(connection, json);
- if(messageType == "sendInvite")
- return receiveSendInvite(connection, json);
- logGlobal->warn("%s: Unknown message type: %s", accountName, messageType);
- return;
- }
- // communication messages from vcmiserver
- if(activeGameRooms.count(connection))
- {
- std::string roomName = activeGameRooms.at(connection);
- logGlobal->info("%s: Received message of type %s", roomName, messageType);
- if(messageType == "changeRoomDescription")
- return receiveChangeRoomDescription(connection, json);
- if(messageType == "gameStarted")
- return receiveGameStarted(connection, json);
- if(messageType == "leaveGameRoom")
- return receiveLeaveGameRoom(connection, json);
- logGlobal->warn("%s: Unknown message type: %s", roomName, messageType);
- return;
- }
- logGlobal->info("(unauthorised): Received message of type %s", messageType);
- // unauthorized connections - permit only login or register attempts
- if(messageType == "clientLogin")
- return receiveClientLogin(connection, json);
- if(messageType == "clientRegister")
- return receiveClientRegister(connection, json);
- if(messageType == "serverLogin")
- return receiveServerLogin(connection, json);
- if(messageType == "clientProxyLogin")
- return receiveClientProxyLogin(connection, json);
- if(messageType == "serverProxyLogin")
- return receiveServerProxyLogin(connection, json);
- connection->close();
- logGlobal->info("(unauthorised): Unknown message type %s", messageType);
- }
- void LobbyServer::receiveRequestChatHistory(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string accountID = activeAccounts[connection];
- std::string channelType = json["channelType"].String();
- std::string channelName = json["channelName"].String();
- if (channelType == "global")
- {
- sendRecentChatHistory(connection, channelType, channelName);
- }
- if (channelType == "match")
- {
- if (!database->isPlayerInGameRoom(accountID, channelName))
- return sendOperationFailed(connection, "Can not access room you are not part of!");
- sendFullChatHistory(connection, channelType, channelName, channelName);
- }
- if (channelType == "player")
- {
- if (!database->isAccountIDExists(channelName))
- return sendOperationFailed(connection, "Such player does not exists!");
- // room ID for private messages is actually <player 1 ID>_<player 2 ID>, with player ID's sorted alphabetically (to generate unique room ID)
- std::string roomID = std::min(accountID, channelName) + "_" + std::max(accountID, channelName);
- sendFullChatHistory(connection, channelType, roomID, channelName);
- }
- }
- void LobbyServer::receiveSendChatMessage(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string senderAccountID = activeAccounts[connection];
- std::string messageText = json["messageText"].String();
- std::string channelType = json["channelType"].String();
- std::string channelName = json["channelName"].String();
- std::string displayName = database->getAccountDisplayName(senderAccountID);
- if(!TextOperations::isValidUnicodeString(messageText))
- return sendOperationFailed(connection, "String contains invalid characters!");
- std::string messageTextClean = sanitizeChatMessage(messageText);
- if(messageTextClean.empty())
- return sendOperationFailed(connection, "No printable characters in sent message!");
- if (channelType == "global")
- {
- try
- {
- Languages::getLanguageOptions(channelName);
- }
- catch (const std::out_of_range &)
- {
- return sendOperationFailed(connection, "Unknown language!");
- }
- database->insertChatMessage(senderAccountID, channelType, channelName, messageText);
- for(const auto & otherConnection : activeAccounts)
- sendChatMessage(otherConnection.first, channelType, channelName, senderAccountID, displayName, messageText);
- }
- if (channelType == "match")
- {
- if (!database->isPlayerInGameRoom(senderAccountID, channelName))
- return sendOperationFailed(connection, "Can not access room you are not part of!");
- database->insertChatMessage(senderAccountID, channelType, channelName, messageText);
- LobbyRoomState roomStatus = database->getGameRoomStatus(channelName);
- // Broadcast chat message only if it being sent to already closed match
- // Othervice it will be handled by match server
- if (roomStatus == LobbyRoomState::CLOSED)
- {
- for(const auto & otherConnection : activeAccounts)
- {
- if (database->isPlayerInGameRoom(otherConnection.second, channelName))
- sendChatMessage(otherConnection.first, channelType, channelName, senderAccountID, displayName, messageText);
- }
- }
- }
- if (channelType == "player")
- {
- const std::string & receiverAccountID = channelName;
- std::string roomID = std::min(senderAccountID, receiverAccountID) + "_" + std::max(senderAccountID, receiverAccountID);
- if (!database->isAccountIDExists(receiverAccountID))
- return sendOperationFailed(connection, "Such player does not exists!");
- database->insertChatMessage(senderAccountID, channelType, roomID, messageText);
- sendChatMessage(connection, channelType, receiverAccountID, senderAccountID, displayName, messageText);
- if (senderAccountID != receiverAccountID)
- {
- for(const auto & otherConnection : activeAccounts)
- if (otherConnection.second == receiverAccountID)
- sendChatMessage(otherConnection.first, channelType, senderAccountID, senderAccountID, displayName, messageText);
- }
- }
- }
- void LobbyServer::receiveClientRegister(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string displayName = json["displayName"].String();
- std::string language = json["language"].String();
- if(!isAccountNameValid(displayName))
- return sendOperationFailed(connection, "Illegal account name");
- if(database->isAccountNameExists(displayName))
- return sendOperationFailed(connection, "Account name already in use");
- std::string accountCookie = boost::uuids::to_string(boost::uuids::random_generator()());
- std::string accountID = boost::uuids::to_string(boost::uuids::random_generator()());
- database->insertAccount(accountID, displayName);
- database->insertAccessCookie(accountID, accountCookie);
- sendAccountCreated(connection, accountID, accountCookie);
- }
- void LobbyServer::receiveClientLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string accountID = json["accountID"].String();
- std::string accountCookie = json["accountCookie"].String();
- std::string language = json["language"].String();
- std::string version = json["version"].String();
- const auto & languageRooms = json["languageRooms"].Vector();
- if(!database->isAccountIDExists(accountID))
- return sendOperationFailed(connection, "Account not found");
- auto clientCookieStatus = database->getAccountCookieStatus(accountID, accountCookie);
- if(clientCookieStatus == LobbyCookieStatus::INVALID)
- return sendOperationFailed(connection, "Authentication failure");
- database->updateAccountLoginTime(accountID);
- database->setAccountOnline(accountID, true);
- std::string displayName = database->getAccountDisplayName(accountID);
- activeAccounts[connection] = accountID;
- logGlobal->info("%s: Logged in as %s", accountID, displayName);
- sendClientLoginSuccess(connection, accountCookie, displayName);
- if (!languageRooms.empty())
- {
- for (const auto & entry : languageRooms)
- sendRecentChatHistory(connection, "global", entry.String());
- }
- else
- {
- sendRecentChatHistory(connection, "global", "english");
- if (language != "english")
- sendRecentChatHistory(connection, "global", language);
- }
- // send active game rooms list to new account
- // and update account list to everybody else including new account
- broadcastActiveAccounts();
- sendMessage(connection, prepareActiveGameRooms());
- sendMatchesHistory(connection);
- }
- void LobbyServer::receiveServerLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = json["gameRoomID"].String();
- std::string accountID = json["accountID"].String();
- std::string accountCookie = json["accountCookie"].String();
- std::string version = json["version"].String();
- auto clientCookieStatus = database->getAccountCookieStatus(accountID, accountCookie);
- if(clientCookieStatus == LobbyCookieStatus::INVALID)
- {
- sendOperationFailed(connection, "Invalid credentials");
- }
- else
- {
- std::string modListString = json["mods"].isNull() ? "[]" : json["mods"].toCompactString();
- database->insertGameRoom(gameRoomID, accountID, version, modListString);
- activeGameRooms[connection] = gameRoomID;
- sendServerLoginSuccess(connection, accountCookie);
- broadcastActiveGameRooms();
- }
- }
- void LobbyServer::receiveClientProxyLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = json["gameRoomID"].String();
- std::string accountID = json["accountID"].String();
- std::string accountCookie = json["accountCookie"].String();
- auto clientCookieStatus = database->getAccountCookieStatus(accountID, accountCookie);
- if(clientCookieStatus != LobbyCookieStatus::INVALID)
- {
- for(auto & proxyEntry : awaitingProxies)
- {
- if(proxyEntry.accountID != accountID)
- continue;
- if(proxyEntry.roomID != gameRoomID)
- continue;
- proxyEntry.accountConnection = connection;
- auto gameRoomConnection = proxyEntry.roomConnection.lock();
- if(gameRoomConnection)
- {
- activeProxies[gameRoomConnection] = connection;
- activeProxies[connection] = gameRoomConnection;
- }
- return;
- }
- }
- sendOperationFailed(connection, "Invalid credentials");
- connection->close();
- }
- void LobbyServer::receiveServerProxyLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = json["gameRoomID"].String();
- std::string guestAccountID = json["guestAccountID"].String();
- std::string accountCookie = json["accountCookie"].String();
- // FIXME: find host account ID and validate his cookie
- //auto clientCookieStatus = database->getAccountCookieStatus(hostAccountID, accountCookie, accountCookieLifetime);
- //if(clientCookieStatus != LobbyCookieStatus::INVALID)
- {
- NetworkConnectionPtr targetAccount = findAccount(guestAccountID);
- if(targetAccount == nullptr)
- {
- sendOperationFailed(connection, "Invalid credentials");
- return; // unknown / disconnected account
- }
- sendJoinRoomSuccess(targetAccount, gameRoomID, true);
- AwaitingProxyState proxy;
- proxy.accountID = guestAccountID;
- proxy.roomID = gameRoomID;
- proxy.roomConnection = connection;
- awaitingProxies.push_back(proxy);
- return;
- }
- //connection->close();
- }
- void LobbyServer::receiveActivateGameRoom(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string hostAccountID = json["hostAccountID"].String();
- std::string accountID = activeAccounts[connection];
- int playerLimit = json["playerLimit"].Integer();
- if(database->isPlayerInGameRoom(accountID))
- return sendOperationFailed(connection, "Player already in the room!");
- std::string gameRoomID = database->getIdleGameRoom(hostAccountID);
- if(gameRoomID.empty())
- return sendOperationFailed(connection, "Failed to find idle server to join!");
- std::string roomType = json["roomType"].String();
- if(roomType != "public" && roomType != "private")
- return sendOperationFailed(connection, "Invalid room type!");
- if(roomType == "public")
- database->setGameRoomStatus(gameRoomID, LobbyRoomState::PUBLIC);
- if(roomType == "private")
- database->setGameRoomStatus(gameRoomID, LobbyRoomState::PRIVATE);
- database->updateRoomPlayerLimit(gameRoomID, playerLimit);
- database->insertPlayerIntoGameRoom(accountID, gameRoomID);
- broadcastActiveGameRooms();
- sendJoinRoomSuccess(connection, gameRoomID, false);
- }
- void LobbyServer::receiveJoinGameRoom(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = json["gameRoomID"].String();
- std::string accountID = activeAccounts[connection];
- if(database->isPlayerInGameRoom(accountID))
- return sendOperationFailed(connection, "Player already in the room!");
- NetworkConnectionPtr targetRoom = findGameRoom(gameRoomID);
- if(targetRoom == nullptr)
- return sendOperationFailed(connection, "Failed to find game room to join!");
- auto roomStatus = database->getGameRoomStatus(gameRoomID);
- if(roomStatus != LobbyRoomState::PRIVATE && roomStatus != LobbyRoomState::PUBLIC)
- return sendOperationFailed(connection, "Room does not accepts new players!");
- if(roomStatus == LobbyRoomState::PRIVATE)
- {
- if(database->getAccountInviteStatus(accountID, gameRoomID) != LobbyInviteStatus::INVITED)
- return sendOperationFailed(connection, "You are not permitted to join private room without invite!");
- }
- if(database->getGameRoomFreeSlots(gameRoomID) == 0)
- return sendOperationFailed(connection, "Room is already full!");
- database->insertPlayerIntoGameRoom(accountID, gameRoomID);
- sendAccountJoinsRoom(targetRoom, accountID);
- //No reply to client - will be sent once match server establishes proxy connection with lobby
- broadcastActiveGameRooms();
- }
- void LobbyServer::receiveChangeRoomDescription(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = activeGameRooms[connection];
- std::string description = json["description"].String();
- database->updateRoomDescription(gameRoomID, description);
- broadcastActiveGameRooms();
- }
- void LobbyServer::receiveGameStarted(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string gameRoomID = activeGameRooms[connection];
- database->setGameRoomStatus(gameRoomID, LobbyRoomState::BUSY);
- broadcastActiveGameRooms();
- }
- void LobbyServer::receiveLeaveGameRoom(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string accountID = json["accountID"].String();
- std::string gameRoomID = activeGameRooms[connection];
- if(!database->isPlayerInGameRoom(accountID, gameRoomID))
- return sendOperationFailed(connection, "You are not in the room!");
- database->deletePlayerFromGameRoom(accountID, gameRoomID);
- broadcastActiveGameRooms();
- }
- void LobbyServer::receiveSendInvite(const NetworkConnectionPtr & connection, const JsonNode & json)
- {
- std::string senderName = activeAccounts[connection];
- std::string accountID = json["accountID"].String();
- std::string gameRoomID = database->getAccountGameRoom(senderName);
- auto targetAccountConnection = findAccount(accountID);
- if(!targetAccountConnection)
- return sendOperationFailed(connection, "Player is offline or does not exists!");
- if(!database->isPlayerInGameRoom(senderName))
- return sendOperationFailed(connection, "You are not in the room!");
- if(database->isPlayerInGameRoom(accountID))
- return sendOperationFailed(connection, "This player is already in a room!");
- if(database->getAccountInviteStatus(accountID, gameRoomID) != LobbyInviteStatus::NOT_INVITED)
- return sendOperationFailed(connection, "This player is already invited!");
- database->insertGameRoomInvite(accountID, gameRoomID);
- sendInviteReceived(targetAccountConnection, senderName, gameRoomID);
- broadcastActiveGameRooms();
- }
- LobbyServer::~LobbyServer() = default;
- LobbyServer::LobbyServer(const boost::filesystem::path & databasePath)
- : database(std::make_unique<LobbyDatabase>(databasePath))
- , networkHandler(INetworkHandler::createHandler())
- , networkServer(networkHandler->createServerTCP(*this))
- {
- }
- void LobbyServer::start(uint16_t port)
- {
- networkServer->start(port);
- }
- void LobbyServer::run()
- {
- networkHandler->run();
- }
|