1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Implemented connecting to server via proxy

This commit is contained in:
Ivan Savenko 2024-01-29 22:05:11 +02:00
parent bed05eb52d
commit c5c46a7c9a
15 changed files with 160 additions and 61 deletions

View File

@ -139,6 +139,7 @@ CServerHandler::CServerHandler()
, state(EClientState::NONE)
, campaignStateToSend(nullptr)
, screenType(ESelectionScreen::unknown)
, serverMode(EServerMode::NONE)
, loadMode(ELoadMode::NONE)
, client(nullptr)
, campaignServerRestartLock(false)
@ -156,10 +157,11 @@ void CServerHandler::threadRunNetwork()
logGlobal->info("Ending network thread");
}
void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen, const std::vector<std::string> & names)
void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen, EServerMode newServerMode, const std::vector<std::string> & names)
{
hostClientId = -1;
state = EClientState::NONE;
serverMode = newServerMode;
mapToStart = nullptr;
th = std::make_unique<CStopWatch>();
c.reset();
@ -297,11 +299,18 @@ void CServerHandler::onTimer()
networkHandler->connectToRemote(*this, getLocalHostname(), getLocalPort());
}
void CServerHandler::onConnectionEstablished(const std::shared_ptr<INetworkConnection> & netConnection)
void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netConnection)
{
networkConnection = netConnection;
logNetwork->info("Connection established");
if (serverMode == EServerMode::LOBBY_GUEST)
{
// say hello to lobby to switch connection to proxy mode
getGlobalLobby().sendProxyConnectionLogin(netConnection);
}
c = std::make_shared<CConnection>(netConnection);
nextClient = std::make_unique<CClient>();
c->uuid = uuid;
@ -791,12 +800,12 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
auto mapInfo = std::make_shared<CMapInfo>();
if(save)
{
resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, {});
resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, EServerMode::LOCAL, {});
mapInfo->saveInit(ResourcePath(filename, EResType::SAVEGAME));
}
else
{
resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, {});
resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOCAL, {});
mapInfo->mapInit(filename);
}
if(settings["session"]["donotstartserver"].Bool())

View File

@ -57,6 +57,14 @@ enum class EClientState : ui8
CONNECTION_FAILED // We could not connect to server
};
enum class EServerMode : uint8_t
{
NONE = 0,
LOCAL, // no global lobby
LOBBY_HOST, // We are hosting global server available via global lobby
LOBBY_GUEST // Connecting to a remote server via proxy provided by global lobby
};
class IServerAPI
{
protected:
@ -106,10 +114,10 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
void onServerFinished();
void sendLobbyPack(const CPackForLobby & pack) const override;
void onPacketReceived(const std::shared_ptr<INetworkConnection> &, const std::vector<uint8_t> & message) override;
void onPacketReceived(const NetworkConnectionPtr &, const std::vector<uint8_t> & message) override;
void onConnectionFailed(const std::string & errorMessage) override;
void onConnectionEstablished(const std::shared_ptr<INetworkConnection> &) override;
void onDisconnected(const std::shared_ptr<INetworkConnection> &) override;
void onConnectionEstablished(const NetworkConnectionPtr &) override;
void onDisconnected(const NetworkConnectionPtr &) override;
void onTimer() override;
void applyPackOnLobbyScreen(CPackForLobby & pack);
@ -132,6 +140,7 @@ public:
std::shared_ptr<CampaignState> campaignStateToSend;
ESelectionScreen screenType; // To create lobby UI only after server is setup
EServerMode serverMode;
ELoadMode loadMode; // For saves filtering in SelectionTab
////////////////////
@ -146,7 +155,7 @@ public:
CServerHandler();
~CServerHandler();
void resetStateForLobby(EStartMode mode, ESelectionScreen screen, const std::vector<std::string> & names);
void resetStateForLobby(EStartMode mode, ESelectionScreen screen, EServerMode serverMode, const std::vector<std::string> & names);
void startLocalServerAndConnect(bool connectToLobby);
void connectToServer(const std::string & addr, const ui16 port);

View File

@ -53,6 +53,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
if (!GH.windows().findWindows<GlobalLobbyServerSetup>().empty())
{
assert(handler.serverMode == EServerMode::LOBBY_HOST);
// announce opened game room
// TODO: find better approach?
int roomType = settings["lobby"]["roomType"].Integer();

View File

@ -18,6 +18,7 @@
#include "../gui/WindowHandler.h"
#include "../windows/InfoWindows.h"
#include "../CServerHandler.h"
#include "../mainmenu/CMainMenu.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/MetaString.h"
@ -190,7 +191,18 @@ void GlobalLobbyClient::receiveActiveGameRooms(const JsonNode & json)
void GlobalLobbyClient::receiveJoinRoomSuccess(const JsonNode & json)
{
// TODO: store "gameRoomID" field and use it for future queries
Settings configRoom = settings.write["lobby"]["roomID"];
configRoom->String() = json["gameRoomID"].String();
if (json["proxyMode"].Bool())
{
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOBBY_GUEST, {});
CSH->loadMode = ELoadMode::MULTI;
std::string hostname = settings["lobby"]["hostname"].String();
int16_t port = settings["lobby"]["port"].Integer();
CSH->connectToServer(hostname, port);
}
}
void GlobalLobbyClient::onConnectionEstablished(const std::shared_ptr<INetworkConnection> & connection)
@ -334,3 +346,22 @@ void GlobalLobbyClient::activateInterface()
else
GH.windows().pushWindow(createLoginWindow());
}
void GlobalLobbyClient::sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection)
{
JsonNode toSend;
toSend["type"].String() = "clientProxyLogin";
toSend["accountID"] = settings["lobby"]["accountID"];
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
toSend["gameRoomID"] = settings["lobby"]["roomID"];
std::string payloadString = toSend.toJson(true);
// FIXME: find better approach
uint8_t * payloadBegin = reinterpret_cast<uint8_t *>(payloadString.data());
uint8_t * payloadEnd = payloadBegin + payloadString.size();
std::vector<uint8_t> payloadBuffer(payloadBegin, payloadEnd);
netConnection->sendPacket(payloadBuffer);
}

View File

@ -63,6 +63,8 @@ public:
void sendOpenPublicRoom();
void sendOpenPrivateRoom();
void sendProxyConnectionLogin(const NetworkConnectionPtr & netConnection);
void connect();
bool isConnected();
};

View File

@ -125,9 +125,9 @@ void GlobalLobbyServerSetup::onGameModeChanged(int value)
void GlobalLobbyServerSetup::onCreate()
{
if(toggleGameMode->getSelected() == 0)
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, {});
CSH->resetStateForLobby(EStartMode::NEW_GAME, ESelectionScreen::newGame, EServerMode::LOBBY_HOST, {});
else
CSH->resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, {});
CSH->resetStateForLobby(EStartMode::LOAD_GAME, ESelectionScreen::loadGame, EServerMode::LOBBY_HOST, {});
CSH->loadMode = ELoadMode::MULTI;
CSH->startLocalServerAndConnect(true);

View File

@ -361,7 +361,7 @@ void CMainMenu::update()
void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> & names, ELoadMode loadMode)
{
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, names);
CSH->resetStateForLobby(screenType == ESelectionScreen::newGame ? EStartMode::NEW_GAME : EStartMode::LOAD_GAME, screenType, EServerMode::LOCAL, names);
CSH->loadMode = loadMode;
GH.windows().createAndPushWindow<CSimpleJoinScreen>(host);
@ -376,7 +376,7 @@ void CMainMenu::openCampaignLobby(const std::string & campaignFileName, std::str
void CMainMenu::openCampaignLobby(std::shared_ptr<CampaignState> campaign)
{
CSH->resetStateForLobby(EStartMode::CAMPAIGN, ESelectionScreen::campaignList, {});
CSH->resetStateForLobby(EStartMode::CAMPAIGN, ESelectionScreen::campaignList, EServerMode::LOCAL, {});
CSH->campaignStateToSend = campaign;
GH.windows().createAndPushWindow<CSimpleJoinScreen>();
}

View File

@ -553,28 +553,24 @@
"type" : "object",
"additionalProperties" : false,
"default" : {},
"required" : [ "mapPreview", "accountID", "accountCookie", "displayName", "hostname", "port", "roomPlayerLimit", "roomType", "roomMode" ],
"required" : [ "mapPreview", "accountID", "accountCookie", "displayName", "hostname", "port", "roomPlayerLimit", "roomType", "roomMode", "roomID" ],
"properties" : {
"mapPreview" : {
"type" : "boolean",
"default" : true
},
"accountID" : {
"type" : "string",
"default" : ""
},
"accountCookie" : {
"type" : "string",
"default" : ""
},
"displayName" : {
"type" : "string",
"default" : ""
},
"hostname" : {
"type" : "string",
"default" : "127.0.0.1"
@ -583,21 +579,22 @@
"type" : "number",
"default" : 30303
},
"roomPlayerLimit" : {
"type" : "number",
"default" : 2
},
"roomType" : {
"type" : "number",
"default" : 0
},
"roomMode" : {
"type" : "number",
"default" : 0
},
"roomID" : {
"type" : "string",
"default" : ""
}
}
},
"gameTweaks" : {

View File

@ -53,15 +53,20 @@ void NetworkServer::sendPacket(const std::shared_ptr<INetworkConnection> & conne
void NetworkServer::closeConnection(const std::shared_ptr<INetworkConnection> & connection)
{
logNetwork->info("Closing connection!");
assert(connections.count(connection));
connections.erase(connection);
}
void NetworkServer::onDisconnected(const std::shared_ptr<INetworkConnection> & connection)
{
logNetwork->info("Connection lost!");
assert(connections.count(connection));
connections.erase(connection);
listener.onDisconnected(connection);
if (connections.count(connection)) // how? Connection was explicitly closed before?
{
connections.erase(connection);
listener.onDisconnected(connection);
}
}
void NetworkServer::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<uint8_t> & message)

View File

@ -155,6 +155,12 @@ void LobbyDatabase::prepareStatements()
LIMIT 1
)";
static const std::string getGameRoomStatusText = R"(
SELECT status
FROM gameRooms
WHERE roomID = ?
)";
static const std::string getAccountGameRoomText = R"(
SELECT grp.roomID
FROM gameRoomPlayers grp
@ -209,14 +215,16 @@ void LobbyDatabase::prepareStatements()
static const std::string isPlayerInGameRoomText = R"(
SELECT COUNT(accountID)
FROM gameRoomPlayers
WHERE accountID = ? AND roomID = ?
FROM gameRoomPlayers grp
LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
WHERE accountID = ? AND grp.roomID = ? AND status IN (1, 2)
)";
static const std::string isPlayerInAnyGameRoomText = R"(
SELECT COUNT(accountID)
FROM gameRoomPlayers
WHERE accountID = ?
FROM gameRoomPlayers grp
LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
WHERE accountID = ? AND status IN (1, 2)
)";
static const std::string isAccountIDExistsText = R"(
@ -247,6 +255,7 @@ void LobbyDatabase::prepareStatements()
getRecentMessageHistoryStatement = database->prepare(getRecentMessageHistoryText);
getIdleGameRoomStatement = database->prepare(getIdleGameRoomText);
getGameRoomStatusStatement = database->prepare(getGameRoomStatusText);
getAccountGameRoomStatement = database->prepare(getAccountGameRoomText);
getActiveAccountsStatement = database->prepare(getActiveAccountsText);
getActiveGameRoomsStatement = database->prepare(getActiveGameRoomsText);
@ -406,7 +415,16 @@ LobbyInviteStatus LobbyDatabase::getAccountInviteStatus(const std::string & acco
LobbyRoomState LobbyDatabase::getGameRoomStatus(const std::string & roomID)
{
return {};
int result = -1;
getGameRoomStatusStatement->setBinds(roomID);
if(getGameRoomStatusStatement->execute())
getGameRoomStatusStatement->getColumns(result);
getGameRoomStatusStatement->reset();
if (result != -1)
return static_cast<LobbyRoomState>(result);
return LobbyRoomState::CLOSED;
}
uint32_t LobbyDatabase::getGameRoomFreeSlots(const std::string & roomID)

View File

@ -37,6 +37,7 @@ class LobbyDatabase
SQLiteStatementPtr getRecentMessageHistoryStatement;
SQLiteStatementPtr getIdleGameRoomStatement;
SQLiteStatementPtr getGameRoomStatusStatement;
SQLiteStatementPtr getActiveGameRoomsStatement;
SQLiteStatementPtr getActiveAccountsStatement;
SQLiteStatementPtr getAccountGameRoomStatement;

View File

@ -148,7 +148,7 @@ void LobbyServer::broadcastActiveAccounts()
sendMessage(connection.first, reply);
}
void LobbyServer::broadcastActiveGameRooms()
JsonNode LobbyServer::prepareActiveGameRooms()
{
auto activeGameRoomStats = database->getActiveGameRooms();
JsonNode reply;
@ -167,6 +167,13 @@ void LobbyServer::broadcastActiveGameRooms()
reply["gameRooms"].Vector().push_back(jsonEntry);
}
return reply;
}
void LobbyServer::broadcastActiveGameRooms()
{
auto reply = prepareActiveGameRooms();
for(const auto & connection : activeAccounts)
sendMessage(connection.first, reply);
}
@ -179,11 +186,12 @@ void LobbyServer::sendAccountJoinsRoom(const NetworkConnectionPtr & target, cons
sendMessage(target, reply);
}
void LobbyServer::sendJoinRoomSuccess(const NetworkConnectionPtr & target, const std::string & gameRoomID)
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);
}
@ -230,8 +238,9 @@ void LobbyServer::onPacketReceived(const NetworkConnectionPtr & connection, cons
{
auto lockedPtr = activeProxies.at(connection).lock();
if(lockedPtr)
lockedPtr->sendPacket(message);
return;
return lockedPtr->sendPacket(message);
throw std::runtime_error("Received unexpected message for inactive proxy!");
}
JsonNode json(message.data(), message.size());
@ -257,7 +266,7 @@ void LobbyServer::onPacketReceived(const NetworkConnectionPtr & connection, cons
if(json["type"].String() == "declineInvite")
return receiveDeclineInvite(connection, json);
return;
throw std::runtime_error("Received unexpected message of type " + json["type"].String());
}
// communication messages from vcmiserver
@ -266,7 +275,7 @@ void LobbyServer::onPacketReceived(const NetworkConnectionPtr & connection, cons
if(json["type"].String() == "leaveGameRoom")
return receiveLeaveGameRoom(connection, json);
return;
throw std::runtime_error("Received unexpected message of type " + json["type"].String());
}
// unauthorized connections - permit only login or register attempts
@ -287,6 +296,8 @@ void LobbyServer::onPacketReceived(const NetworkConnectionPtr & connection, cons
// TODO: add logging of suspicious connections.
networkServer->closeConnection(connection);
throw std::runtime_error("Received unexpected message of type " + json["type"].String());
}
void LobbyServer::receiveSendChatMessage(const NetworkConnectionPtr & connection, const JsonNode & json)
@ -358,6 +369,7 @@ void LobbyServer::receiveClientLogin(const NetworkConnectionPtr & connection, co
// send active accounts list to new account
// and update acount list to everybody else
broadcastActiveAccounts();
sendMessage(connection, prepareActiveGameRooms());
}
void LobbyServer::receiveServerLogin(const NetworkConnectionPtr & connection, const JsonNode & json)
@ -420,18 +432,19 @@ void LobbyServer::receiveServerProxyLogin(const NetworkConnectionPtr & connectio
{
std::string gameRoomID = json["gameRoomID"].String();
std::string guestAccountID = json["guestAccountID"].String();
std::string hostCookie = json["hostCookie"].String();
std::string accountCookie = json["accountCookie"].String();
auto clientCookieStatus = database->getGameRoomCookieStatus(gameRoomID, hostCookie, accountCookieLifetime);
// FIXME: find host account ID and validate his cookie
//auto clientCookieStatus = database->getAccountCookieStatus(hostAccountID, accountCookie, accountCookieLifetime);
if(clientCookieStatus != LobbyCookieStatus::INVALID)
//if(clientCookieStatus != LobbyCookieStatus::INVALID)
{
NetworkConnectionPtr targetAccount = findAccount(guestAccountID);
if(targetAccount == nullptr)
return; // unknown / disconnected account
sendJoinRoomSuccess(targetAccount, gameRoomID);
sendJoinRoomSuccess(targetAccount, gameRoomID, true);
AwaitingProxyState proxy;
proxy.accountID = guestAccountID;
@ -441,7 +454,7 @@ void LobbyServer::receiveServerProxyLogin(const NetworkConnectionPtr & connectio
return;
}
networkServer->closeConnection(connection);
//networkServer->closeConnection(connection);
}
void LobbyServer::receiveOpenGameRoom(const NetworkConnectionPtr & connection, const JsonNode & json)
@ -467,7 +480,7 @@ void LobbyServer::receiveOpenGameRoom(const NetworkConnectionPtr & connection, c
database->insertPlayerIntoGameRoom(accountID, gameRoomID);
broadcastActiveGameRooms();
sendJoinRoomSuccess(connection, gameRoomID);
sendJoinRoomSuccess(connection, gameRoomID, false);
}
void LobbyServer::receiveJoinGameRoom(const NetworkConnectionPtr & connection, const JsonNode & json)

View File

@ -70,13 +70,15 @@ class LobbyServer : public INetworkServerListener
void broadcastActiveAccounts();
void broadcastActiveGameRooms();
JsonNode prepareActiveGameRooms();
void sendChatMessage(const NetworkConnectionPtr & target, const std::string & roomMode, const std::string & roomName, const std::string & accountID, const std::string & displayName, const std::string & messageText);
void sendAccountCreated(const NetworkConnectionPtr & target, const std::string & accountID, const std::string & accountCookie);
void sendLoginFailed(const NetworkConnectionPtr & target, const std::string & reason);
void sendLoginSuccess(const NetworkConnectionPtr & target, const std::string & accountCookie, const std::string & displayName);
void sendChatHistory(const NetworkConnectionPtr & target, const std::vector<LobbyChatMessage> &);
void sendAccountJoinsRoom(const NetworkConnectionPtr & target, const std::string & accountID);
void sendJoinRoomSuccess(const NetworkConnectionPtr & target, const std::string & gameRoomID);
void sendJoinRoomSuccess(const NetworkConnectionPtr & target, const std::string & gameRoomID, bool proxyMode);
void sendInviteReceived(const NetworkConnectionPtr & target, const std::string & accountID, const std::string & gameRoomID);
void receiveClientRegister(const NetworkConnectionPtr & connection, const JsonNode & json);

View File

@ -34,29 +34,39 @@ void GlobalLobbyProcessor::onDisconnected(const std::shared_ptr<INetworkConnecti
void GlobalLobbyProcessor::onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<uint8_t> & message)
{
JsonNode json(message.data(), message.size());
if (connection == controlConnection)
{
JsonNode json(message.data(), message.size());
if(json["type"].String() == "loginFailed")
return receiveLoginFailed(json);
if(json["type"].String() == "loginFailed")
return receiveLoginFailed(json);
if(json["type"].String() == "loginSuccess")
return receiveLoginSuccess(json);
if(json["type"].String() == "loginSuccess")
return receiveLoginSuccess(json);
if(json["type"].String() == "accountJoinsRoom")
return receiveAccountJoinsRoom(json);
if(json["type"].String() == "accountJoinsRoom")
return receiveAccountJoinsRoom(json);
throw std::runtime_error("Received unexpected message from lobby server: " + json["type"].String());
throw std::runtime_error("Received unexpected message from lobby server: " + json["type"].String());
}
else
{
// received game message via proxy connection
owner.onPacketReceived(connection, message);
}
}
void GlobalLobbyProcessor::receiveLoginFailed(const JsonNode & json)
{
logGlobal->info("Lobby: Failed to login into a lobby server!");
throw std::runtime_error("Failed to login into a lobby server!");
}
void GlobalLobbyProcessor::receiveLoginSuccess(const JsonNode & json)
{
// no-op, wait just for any new commands from lobby
logGlobal->info("Succesfully connected to lobby server");
logGlobal->info("Lobby: Succesfully connected to lobby server");
owner.startAcceptingIncomingConnections();
}
@ -64,6 +74,7 @@ void GlobalLobbyProcessor::receiveAccountJoinsRoom(const JsonNode & json)
{
std::string accountID = json["accountID"].String();
logGlobal->info("Lobby: Account %s will join our room!", accountID);
assert(proxyConnections.count(accountID) == 0);
proxyConnections[accountID] = nullptr;
@ -87,29 +98,29 @@ void GlobalLobbyProcessor::onConnectionEstablished(const std::shared_ptr<INetwor
toSend["gameRoomID"].String() = owner.uuid;
toSend["accountID"] = settings["lobby"]["accountID"];
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
sendMessage(toSend);
sendMessage(connection, toSend);
}
else
{
// Proxy connection for a player
std::string accountID;
std::string guestAccountID;
for (auto const & proxies : proxyConnections)
if (proxies.second == nullptr)
accountID = proxies.first;
guestAccountID = proxies.first;
JsonNode toSend;
toSend["type"].String() = "serverProxyLogin";
toSend["gameRoomID"].String() = owner.uuid;
toSend["accountID"].String() = accountID;
toSend["guestAccountID"].String() = guestAccountID;
toSend["accountCookie"] = settings["lobby"]["accountCookie"];
sendMessage(toSend);
sendMessage(connection, toSend);
proxyConnections[accountID] = connection;
proxyConnections[guestAccountID] = connection;
owner.onNewConnection(connection);
}
}
void GlobalLobbyProcessor::sendMessage(const JsonNode & data)
void GlobalLobbyProcessor::sendMessage(const NetworkConnectionPtr & target, const JsonNode & data)
{
std::string payloadString = data.toJson(true);
@ -119,5 +130,5 @@ void GlobalLobbyProcessor::sendMessage(const JsonNode & data)
std::vector<uint8_t> payloadBuffer(payloadBegin, payloadEnd);
controlConnection->sendPacket(payloadBuffer);
target->sendPacket(payloadBuffer);
}

View File

@ -21,15 +21,15 @@ class GlobalLobbyProcessor : public INetworkClientListener
{
CVCMIServer & owner;
std::shared_ptr<INetworkConnection> controlConnection;
std::map<std::string, std::shared_ptr<INetworkConnection>> proxyConnections;
NetworkConnectionPtr controlConnection;
std::map<std::string, NetworkConnectionPtr> proxyConnections;
void onDisconnected(const std::shared_ptr<INetworkConnection> & connection) override;
void onPacketReceived(const std::shared_ptr<INetworkConnection> & connection, const std::vector<uint8_t> & message) override;
void onConnectionFailed(const std::string & errorMessage) override;
void onConnectionEstablished(const std::shared_ptr<INetworkConnection> &) override;
void sendMessage(const JsonNode & data);
void sendMessage(const NetworkConnectionPtr & target, const JsonNode & data);
void receiveLoginFailed(const JsonNode & json);
void receiveLoginSuccess(const JsonNode & json);