diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index a26d4aac7..a725d2ed5 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -45,17 +45,17 @@ #include "../lib/mapping/CMapInfo.h" #include "../lib/mapObjects/MiscObjects.h" #include "../lib/modding/ModIncompatibility.h" +#include "../lib/network/NetworkClient.h" #include "../lib/rmg/CMapGenOptions.h" +#include "../lib/serializer/Connection.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/registerTypes/RegisterTypesLobbyPacks.h" -#include "../lib/serializer/Connection.h" #include "../lib/serializer/CMemorySerializer.h" #include "../lib/UnlockGuard.h" #include #include #include -#include #include "../lib/serializer/Cast.h" #include "LobbyClientNetPackVisitors.h" @@ -131,8 +131,16 @@ public: static const std::string NAME_AFFIX = "client"; static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name +CServerHandler::~CServerHandler() = default; + CServerHandler::CServerHandler() - : state(EClientState::NONE), mx(std::make_shared()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false) + : state(EClientState::NONE) + , mx(std::make_shared()) + , networkClient(std::make_unique(*this)) + , client(nullptr) + , loadMode(0) + , campaignStateToSend(nullptr) + , campaignServerRestartLock(false) { uuid = boost::uuids::to_string(boost::uuids::random_generator()()); //read from file to restore last session @@ -161,7 +169,7 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std:: myNames.push_back(settings["general"]["playerName"].String()); } -void CServerHandler::startLocalServerAndConnect() +void CServerHandler::startLocalServerAndConnect(const std::function & onConnected) { if(threadRunLocalServer) threadRunLocalServer->join(); @@ -169,17 +177,13 @@ void CServerHandler::startLocalServerAndConnect() th->update(); auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.existingProcess"); - try + + if (!checkNetworkPortIsFree(localhostAddress, getDefaultPort())) { - CConnection testConnection(localhostAddress, getDefaultPort(), NAME, uuid); logNetwork->error("Port is busy, check if another instance of vcmiserver is working"); CInfoWindow::showInfoDialog(errorMsg, {}); return; } - catch(std::runtime_error & error) - { - //no connection means that port is not busy and we can start local server - } #if defined(SINGLE_PROCESS_APP) boost::condition_variable cond; @@ -243,38 +247,15 @@ void CServerHandler::startLocalServerAndConnect() th->update(); //put breakpoint here to attach to server before it does something stupid - justConnectToServer(localhostAddress, 0); + justConnectToServer(localhostAddress, 0, onConnected); logNetwork->trace("\tConnecting to the server: %d ms", th->getDiff()); } -void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port) +void CServerHandler::justConnectToServer(const std::string & addr, const ui16 port, const std::function & onConnected) { + logNetwork->info("Establishing connection..."); state = EClientState::CONNECTING; - while(!c && state != EClientState::CONNECTION_CANCELLED) - { - try - { - logNetwork->info("Establishing connection..."); - c = std::make_shared( - addr.size() ? addr : getHostAddress(), - port ? port : getHostPort(), - NAME, uuid); - } - catch(std::runtime_error & error) - { - logNetwork->warn("\nCannot establish connection. %s Retrying in 1 second", error.what()); - boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); - } - } - - if(state == EClientState::CONNECTION_CANCELLED) - { - logNetwork->info("Connection aborted by player!"); - return; - } - - c->handler = std::make_shared(&CServerHandler::threadHandleConnection, this); if(!addr.empty() && addr != getHostAddress()) { @@ -286,13 +267,39 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po Settings serverPort = settings.write["server"]["port"]; serverPort->Integer() = port; } + + networkClient->start(addr.size() ? addr : getHostAddress(), port ? port : getHostPort()); +} + +void CServerHandler::onConnectionFailed(const std::string & errorMessage) +{ + if(state == EClientState::CONNECTION_CANCELLED) + { + logNetwork->info("Connection aborted by player!"); + return; + } + + logNetwork->warn("\nCannot establish connection. %s Retrying in 1 second", errorMessage); + + //FIXME: replace with asio timer + boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); + + //FIXME: pass parameters from initial attempt + networkClient->start(getHostAddress(), getHostPort()); +} + +void CServerHandler::onConnectionEstablished() +{ + c->enterLobbyConnectionMode(); + sendClientConnecting(); + + //FIXME: call functor provided in CServerHandler::justConnectToServer + assert(0); + //onConnected(); } void CServerHandler::applyPacksOnLobbyScreen() { - if(!c || !c->handler) - return; - boost::unique_lock lock(*mx); while(!packsForLobbyScreen.empty()) { @@ -306,16 +313,6 @@ void CServerHandler::applyPacksOnLobbyScreen() } } -void CServerHandler::stopServerConnection() -{ - if(c->handler) - { - while(!c->handler->timed_join(boost::chrono::milliseconds(50))) - applyPacksOnLobbyScreen(); - c->handler->join(); - } -} - std::set CServerHandler::getHumanColors() { return clientHumanColors(c->connectionID); @@ -421,7 +418,6 @@ void CServerHandler::sendClientDisconnecting() { // Network thread might be applying network pack at this moment auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex); - c->close(); c.reset(); } } @@ -814,7 +810,7 @@ void CServerHandler::restoreLastSession() myNames.push_back(name.String()); resetStateForLobby(StartInfo::LOAD_GAME, &myNames); screenType = ESelectionScreen::loadGame; - justConnectToServer(settings["server"]["server"].String(), settings["server"]["port"].Integer()); + justConnectToServer(settings["server"]["server"].String(), settings["server"]["port"].Integer(), {}); }; auto cleanUpSession = []() @@ -844,9 +840,9 @@ void CServerHandler::debugStartTest(std::string filename, bool save) screenType = ESelectionScreen::newGame; } if(settings["session"]["donotstartserver"].Bool()) - justConnectToServer(localhostAddress, 3030); + justConnectToServer(localhostAddress, 3030, {}); else - startLocalServerAndConnect(); + startLocalServerAndConnect({}); boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); @@ -902,65 +898,49 @@ public: } }; -void CServerHandler::threadHandleConnection() +void CServerHandler::onPacketReceived(const std::vector & message) { - setThreadName("handleConnection"); - c->enterLobbyConnectionMode(); - - try + CPack * pack = c->retrievePack(message); + if(state == EClientState::DISCONNECTING) { - sendClientConnecting(); - while(c && c->connected) - { - while(state == EClientState::STARTING) - boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); - - CPack * pack = c->retrievePack(); - if(state == EClientState::DISCONNECTING) - { - // FIXME: server shouldn't really send netpacks after it's tells client to disconnect - // Though currently they'll be delivered and might cause crash. - vstd::clear_pointer(pack); - } - else - { - ServerHandlerCPackVisitor visitor(*this); - pack->visit(visitor); - } - } + // FIXME: server shouldn't really send netpacks after it's tells client to disconnect + // Though currently they'll be delivered and might cause crash. + vstd::clear_pointer(pack); } - //catch only asio exceptions - catch(const boost::system::system_error & e) + else { - if(state == EClientState::DISCONNECTING) + ServerHandlerCPackVisitor visitor(*this); + pack->visit(visitor); + } +} + +void CServerHandler::onDisconnected() +{ + if(state == EClientState::DISCONNECTING) + { + logNetwork->info("Successfully closed connection to server, ending listening thread!"); + } + else + { + logNetwork->error("Lost connection to server, ending listening thread! Connection has been closed"); + + if(client) { - logNetwork->info("Successfully closed connection to server, ending listening thread!"); + state = EClientState::DISCONNECTING; + + GH.dispatchMainThread([]() + { + CSH->endGameplay(); + GH.defActionsDef = 63; + CMM->menu->switchToTab("main"); + }); } else { - if (e.code() == boost::asio::error::eof) - logNetwork->error("Lost connection to server, ending listening thread! Connection has been closed"); - else - logNetwork->error("Lost connection to server, ending listening thread! Reason: %s", e.what()); - - if(client) - { - state = EClientState::DISCONNECTING; - - GH.dispatchMainThread([]() - { - CSH->endGameplay(); - GH.defActionsDef = 63; - CMM->menu->switchToTab("main"); - }); - } - else - { - auto lcd = new LobbyClientDisconnected(); - lcd->clientId = c->connectionID; - boost::unique_lock lock(*mx); - packsForLobbyScreen.push_back(lcd); - } + auto lcd = new LobbyClientDisconnected(); + lcd->clientId = c->connectionID; + boost::unique_lock lock(*mx); + packsForLobbyScreen.push_back(lcd); } } } diff --git a/client/CServerHandler.h b/client/CServerHandler.h index e4a5d02ca..5192638e9 100644 --- a/client/CServerHandler.h +++ b/client/CServerHandler.h @@ -11,6 +11,7 @@ #include "../lib/CStopWatch.h" +#include "../lib/network/NetworkListener.h" #include "../lib/StartInfo.h" #include "../lib/CondSh.h" @@ -80,8 +81,10 @@ public: }; /// structure to handle running server and connecting to it -class CServerHandler : public IServerAPI, public LobbyInfo +class CServerHandler : public IServerAPI, public LobbyInfo, public INetworkClientListener, boost::noncopyable { + std::unique_ptr networkClient; + friend class ApplyOnLobbyHandlerNetPackVisitor; std::shared_ptr> applier; @@ -95,12 +98,18 @@ class CServerHandler : public IServerAPI, public LobbyInfo std::shared_ptr highScoreCalc; - void threadHandleConnection(); void threadRunServer(); void onServerFinished(); void sendLobbyPack(const CPackForLobby & pack) const override; + void onPacketReceived(const std::vector & message) override; + void onConnectionFailed(const std::string & errorMessage) override; + void onConnectionEstablished() override; + void onDisconnected() override; + public: + std::shared_ptr c; + std::atomic state; //////////////////// // FIXME: Bunch of crutches to glue it all together @@ -115,7 +124,6 @@ public: std::unique_ptr th; std::shared_ptr threadRunLocalServer; - std::shared_ptr c; CClient * client; CondSh campaignServerRestartLock; @@ -123,15 +131,15 @@ public: static const std::string localhostAddress; CServerHandler(); + ~CServerHandler(); std::string getHostAddress() const; ui16 getHostPort() const; void resetStateForLobby(const StartInfo::EMode mode, const std::vector * names = nullptr); - void startLocalServerAndConnect(); - void justConnectToServer(const std::string & addr, const ui16 port); + void startLocalServerAndConnect(const std::function & onConnected); + void justConnectToServer(const std::string & addr, const ui16 port, const std::function & onConnected); void applyPacksOnLobbyScreen(); - void stopServerConnection(); // Helpers for lobby state access std::set getHumanColors(); diff --git a/client/Client.cpp b/client/Client.cpp index 0a6dd7109..71230ab23 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -30,11 +30,12 @@ #include "../lib/UnlockGuard.h" #include "../lib/battle/BattleInfo.h" #include "../lib/serializer/BinaryDeserializer.h" +#include "../lib/serializer/BinarySerializer.h" +#include "../lib/serializer/Connection.h" #include "../lib/mapping/CMapService.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/registerTypes/RegisterTypesClientPacks.h" -#include "../lib/serializer/Connection.h" #include #include diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index a6d0bff9a..aff9f3241 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -27,8 +27,8 @@ #include "../CCallback.h" #include "../lib/filesystem/Filesystem.h" #include "../lib/filesystem/FileInfo.h" -#include "../lib/serializer/Connection.h" #include "../lib/serializer/BinarySerializer.h" +#include "../lib/serializer/Connection.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/VCMI_Lib.h" diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index aee45c228..46173ca20 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/client/NetPacksLobbyClient.cpp @@ -54,8 +54,6 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClient result = false; return; } - - handler.stopServerConnection(); } void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack) diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 2f602e023..6ead7387c 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -47,7 +47,7 @@ #include "../../lib/filesystem/Filesystem.h" #include "../../lib/mapping/CMapInfo.h" #include "../../lib/mapping/CMapHeader.h" -#include "../../lib/serializer/Connection.h" +#include "../../lib/CRandomGenerator.h" ISelectionScreenInfo::ISelectionScreenInfo(ESelectionScreen ScreenType) : screenType(ScreenType) diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index a44c90e5a..0e157458c 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -43,7 +43,6 @@ #include "../../lib/mapping/CMapHeader.h" #include "../../lib/mapping/MapFormat.h" #include "../../lib/TerrainHandler.h" -#include "../../lib/serializer/Connection.h" bool mapSorter::operator()(const std::shared_ptr aaa, const std::shared_ptr bbb) { diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index 058d2cd8e..d4aa5705a 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -45,7 +45,6 @@ #include "../../lib/CGeneralTextHandler.h" #include "../../lib/JsonNode.h" #include "../../lib/campaign/CampaignHandler.h" -#include "../../lib/serializer/Connection.h" #include "../../lib/serializer/CTypeList.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/filesystem/CCompressedStream.h" @@ -559,7 +558,7 @@ CSimpleJoinScreen::CSimpleJoinScreen(bool host) { textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverConnecting")); buttonOk->block(true); - startConnectThread(); + startConnection(); } else { @@ -582,7 +581,7 @@ void CSimpleJoinScreen::connectToServer() buttonOk->block(true); GH.stopTextInput(); - startConnectThread(inputAddress->getText(), boost::lexical_cast(inputPort->getText())); + startConnection(inputAddress->getText(), boost::lexical_cast(inputPort->getText())); } void CSimpleJoinScreen::leaveScreen() @@ -603,7 +602,7 @@ void CSimpleJoinScreen::onChange(const std::string & newText) buttonOk->block(inputAddress->getText().empty() || inputPort->getText().empty()); } -void CSimpleJoinScreen::startConnectThread(const std::string & addr, ui16 port) +void CSimpleJoinScreen::startConnection(const std::string & addr, ui16 port) { #if defined(SINGLE_PROCESS_APP) && defined(VCMI_ANDROID) // in single process build server must use same JNIEnv as client @@ -611,35 +610,31 @@ void CSimpleJoinScreen::startConnectThread(const std::string & addr, ui16 port) // https://github.com/libsdl-org/SDL/blob/main/docs/README-android.md#threads-and-the-java-vm CVCMIServer::reuseClientJNIEnv(SDL_AndroidGetJNIEnv()); #endif - boost::thread connector(&CSimpleJoinScreen::connectThread, this, addr, port); - connector.detach(); -} + auto const & onConnected = [this]() + { + // async call to prevent thread race + GH.dispatchMainThread([this](){ + if(CSH->state == EClientState::CONNECTION_FAILED) + { + CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.mainMenu.serverConnectionFailed"), {}); -void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port) -{ - setThreadName("connectThread"); - if(!addr.length()) - CSH->startLocalServerAndConnect(); + textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverAddressEnter")); + GH.startTextInput(inputAddress->pos); + buttonOk->block(false); + } + + if(GH.windows().isTopWindow(this)) + { + close(); + } + }); + }; + + if(addr.empty()) + CSH->startLocalServerAndConnect(onConnected); else - CSH->justConnectToServer(addr, port); - - // async call to prevent thread race - GH.dispatchMainThread([this](){ - if(CSH->state == EClientState::CONNECTION_FAILED) - { - CInfoWindow::showInfoDialog(CGI->generaltexth->translate("vcmi.mainMenu.serverConnectionFailed"), {}); - - textTitle->setText(CGI->generaltexth->translate("vcmi.mainMenu.serverAddressEnter")); - GH.startTextInput(inputAddress->pos); - buttonOk->block(false); - } - - if(GH.windows().isTopWindow(this)) - { - close(); - } - }); + CSH->justConnectToServer(addr, port, onConnected); } CLoadingScreen::CLoadingScreen() diff --git a/client/mainmenu/CMainMenu.h b/client/mainmenu/CMainMenu.h index ed2cdf585..5c806961f 100644 --- a/client/mainmenu/CMainMenu.h +++ b/client/mainmenu/CMainMenu.h @@ -177,8 +177,7 @@ class CSimpleJoinScreen : public WindowBase void connectToServer(); void leaveScreen(); void onChange(const std::string & newText); - void startConnectThread(const std::string & addr = {}, ui16 port = 0); - void connectThread(const std::string & addr, ui16 port); + void startConnection(const std::string & addr = {}, ui16 port = 0); public: CSimpleJoinScreen(bool host = true); diff --git a/client/serverLobby/LobbyWindow.cpp b/client/serverLobby/LobbyWindow.cpp index 14049cf37..193c56f61 100644 --- a/client/serverLobby/LobbyWindow.cpp +++ b/client/serverLobby/LobbyWindow.cpp @@ -18,12 +18,12 @@ #include "../../lib/MetaString.h" #include "../../lib/CConfigHandler.h" +#include "../../lib/network/NetworkClient.h" LobbyClient::LobbyClient(LobbyWindow * window) - : window(window) -{ - -} + : networkClient(std::make_unique(*this)) + , window(window) +{} static std::string getCurrentTimeFormatted(int timeOffsetSeconds = 0) { @@ -100,7 +100,22 @@ void LobbyClient::sendMessage(const JsonNode & data) std::vector payloadBuffer(payloadBegin, payloadEnd); - sendPacket(payloadBuffer); + networkClient->sendPacket(payloadBuffer); +} + +void LobbyClient::start(const std::string & host, uint16_t port) +{ + networkClient->start(host, port); +} + +void LobbyClient::run() +{ + networkClient->run(); +} + +void LobbyClient::poll() +{ + networkClient->poll(); } LobbyWidget::LobbyWidget(LobbyWindow * window) diff --git a/client/serverLobby/LobbyWindow.h b/client/serverLobby/LobbyWindow.h index 45f108316..4121580e0 100644 --- a/client/serverLobby/LobbyWindow.h +++ b/client/serverLobby/LobbyWindow.h @@ -12,7 +12,7 @@ #include "../gui/InterfaceObjectConfigurable.h" #include "../windows/CWindowObject.h" -#include "../../lib/network/NetworkClient.h" +#include "../../lib/network/NetworkListener.h" class LobbyWindow; @@ -27,8 +27,9 @@ public: std::shared_ptr getGameChat(); }; -class LobbyClient : public NetworkClient +class LobbyClient : public INetworkClientListener { + std::unique_ptr networkClient; LobbyWindow * window; void onPacketReceived(const std::vector & message) override; @@ -40,6 +41,10 @@ public: explicit LobbyClient(LobbyWindow * window); void sendMessage(const JsonNode & data); + void start(const std::string & host, uint16_t port); + void run(); + void poll(); + }; class LobbyWindow : public CWindowObject diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index a9a9f8c74..6ad35ac43 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -478,6 +478,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/network/NetworkClient.h ${MAIN_LIB_DIR}/network/NetworkConnection.h ${MAIN_LIB_DIR}/network/NetworkDefines.h + ${MAIN_LIB_DIR}/network/NetworkListener.h ${MAIN_LIB_DIR}/network/NetworkServer.h ${MAIN_LIB_DIR}/networkPacks/ArtifactLocation.h diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index f40aebcd8..06b64daf7 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -47,8 +47,6 @@ #include "RiverHandler.h" #include "TerrainHandler.h" -#include "serializer/Connection.h" - VCMI_LIB_NAMESPACE_BEGIN void CPrivilegedInfoCallback::getFreeTiles(std::vector & tiles) const diff --git a/lib/StartInfo.cpp b/lib/StartInfo.cpp index cf1a70112..d08b1221c 100644 --- a/lib/StartInfo.cpp +++ b/lib/StartInfo.cpp @@ -123,7 +123,12 @@ bool LobbyInfo::isClientHost(int clientId) const return clientId == hostClientId; } -std::set LobbyInfo::getAllClientPlayers(int clientId) +bool LobbyInfo::isPlayerHost(const PlayerColor & color) const +{ + return vstd::contains(getAllClientPlayers(hostClientId), color); +} + +std::set LobbyInfo::getAllClientPlayers(int clientId) const { std::set players; for(auto & elem : si->playerInfos) diff --git a/lib/StartInfo.h b/lib/StartInfo.h index eed03eb21..8e0684ba2 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -197,7 +197,6 @@ struct DLL_LINKAGE LobbyState struct DLL_LINKAGE LobbyInfo : public LobbyState { - boost::mutex stateMutex; std::string uuid; LobbyInfo() {} @@ -205,7 +204,8 @@ struct DLL_LINKAGE LobbyInfo : public LobbyState void verifyStateBeforeStart(bool ignoreNoHuman = false) const; bool isClientHost(int clientId) const; - std::set getAllClientPlayers(int clientId); + bool isPlayerHost(const PlayerColor & color) const; + std::set getAllClientPlayers(int clientId) const; std::vector getConnectedPlayerIdsForClient(int clientId) const; // Helpers for lobby state access diff --git a/lib/network/NetworkClient.cpp b/lib/network/NetworkClient.cpp index e4ce10ab2..dbcbd9315 100644 --- a/lib/network/NetworkClient.cpp +++ b/lib/network/NetworkClient.cpp @@ -13,10 +13,27 @@ VCMI_LIB_NAMESPACE_BEGIN -NetworkClient::NetworkClient() +DLL_LINKAGE bool checkNetworkPortIsFree(const std::string & host, uint16_t port) +{ + boost::asio::io_service io; + NetworkAcceptor acceptor(io); + + boost::system::error_code ec; + acceptor.open(boost::asio::ip::tcp::v4(), ec); + if (ec) + return false; + + acceptor.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port), ec); + if (ec) + return false; + + return true; +} + +NetworkClient::NetworkClient(INetworkClientListener & listener) : io(new NetworkService) , socket(new NetworkSocket(*io)) -// , timer(new NetworkTimer(*io)) + , listener(listener) { } @@ -32,14 +49,14 @@ void NetworkClient::onConnected(const boost::system::error_code & ec) { if (ec) { - onConnectionFailed(ec.message()); + listener.onConnectionFailed(ec.message()); return; } connection = std::make_shared(socket, *this); connection->start(); - onConnectionEstablished(); + listener.onConnectionEstablished(); } void NetworkClient::run() @@ -59,12 +76,12 @@ void NetworkClient::sendPacket(const std::vector & message) void NetworkClient::onDisconnected(const std::shared_ptr & connection) { - onDisconnected(); + listener.onDisconnected(); } void NetworkClient::onPacketReceived(const std::shared_ptr & connection, const std::vector & message) { - onPacketReceived(message); + listener.onPacketReceived(message); } diff --git a/lib/network/NetworkClient.h b/lib/network/NetworkClient.h index 2f1509313..541d34d23 100644 --- a/lib/network/NetworkClient.h +++ b/lib/network/NetworkClient.h @@ -10,9 +10,14 @@ #pragma once #include "NetworkDefines.h" +#include "NetworkListener.h" VCMI_LIB_NAMESPACE_BEGIN +/// Function that attempts to open specified port on local system to determine whether port is in use +/// Returns: true if port is free and can be used to receive connections +DLL_LINKAGE bool checkNetworkPortIsFree(const std::string & host, uint16_t port); + class NetworkConnection; class DLL_LINKAGE NetworkClient : boost::noncopyable, public INetworkConnectionListener @@ -20,23 +25,19 @@ class DLL_LINKAGE NetworkClient : boost::noncopyable, public INetworkConnectionL std::shared_ptr io; std::shared_ptr socket; std::shared_ptr connection; - std::shared_ptr timer; + + INetworkClientListener & listener; void onConnected(const boost::system::error_code & ec); void onDisconnected(const std::shared_ptr & connection) override; void onPacketReceived(const std::shared_ptr & connection, const std::vector & message) override; -protected: - virtual void onPacketReceived(const std::vector & message) = 0; - virtual void onConnectionFailed(const std::string & errorMessage) = 0; - virtual void onConnectionEstablished() = 0; - virtual void onDisconnected() = 0; +public: + NetworkClient(INetworkClientListener & listener); + virtual ~NetworkClient() = default; void sendPacket(const std::vector & message); -public: - NetworkClient(); - virtual ~NetworkClient() = default; void start(const std::string & host, uint16_t port); void run(); diff --git a/lib/network/NetworkConnection.h b/lib/network/NetworkConnection.h index 7b2259565..cb72903bd 100644 --- a/lib/network/NetworkConnection.h +++ b/lib/network/NetworkConnection.h @@ -10,6 +10,7 @@ #pragma once #include "NetworkDefines.h" +#include "NetworkListener.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/network/NetworkDefines.h b/lib/network/NetworkDefines.h index 8b05370bb..bfd24eced 100644 --- a/lib/network/NetworkDefines.h +++ b/lib/network/NetworkDefines.h @@ -17,17 +17,5 @@ using NetworkService = boost::asio::io_service; using NetworkSocket = boost::asio::basic_stream_socket; using NetworkAcceptor = boost::asio::basic_socket_acceptor; using NetworkBuffer = boost::asio::streambuf; -using NetworkTimer = boost::asio::steady_timer; - -class NetworkConnection; - -class DLL_LINKAGE INetworkConnectionListener -{ - friend class NetworkConnection; - -protected: - virtual void onDisconnected(const std::shared_ptr & connection) = 0; - virtual void onPacketReceived(const std::shared_ptr & connection, const std::vector & message) = 0; -}; VCMI_LIB_NAMESPACE_END diff --git a/lib/network/NetworkListener.h b/lib/network/NetworkListener.h new file mode 100644 index 000000000..7f3200ef5 --- /dev/null +++ b/lib/network/NetworkListener.h @@ -0,0 +1,50 @@ +/* + * NetworkListener.h, 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 + * + */ +#pragma once + +VCMI_LIB_NAMESPACE_BEGIN + +class NetworkConnection; +class NetworkServer; +class NetworkClient; + +class DLL_LINKAGE INetworkConnectionListener +{ + friend class NetworkConnection; +protected: + virtual void onDisconnected(const std::shared_ptr & connection) = 0; + virtual void onPacketReceived(const std::shared_ptr & connection, const std::vector & message) = 0; + + ~INetworkConnectionListener() = default; +}; + +class DLL_LINKAGE INetworkServerListener : public INetworkConnectionListener +{ + friend class NetworkServer; +protected: + virtual void onNewConnection(const std::shared_ptr &) = 0; + + ~INetworkServerListener() = default; +}; + +class DLL_LINKAGE INetworkClientListener +{ + friend class NetworkClient; +protected: + virtual void onPacketReceived(const std::vector & message) = 0; + virtual void onConnectionFailed(const std::string & errorMessage) = 0; + virtual void onConnectionEstablished() = 0; + virtual void onDisconnected() = 0; + + ~INetworkClientListener() = default; +}; + + +VCMI_LIB_NAMESPACE_END diff --git a/lib/network/NetworkServer.cpp b/lib/network/NetworkServer.cpp index d24864041..3c95a284d 100644 --- a/lib/network/NetworkServer.cpp +++ b/lib/network/NetworkServer.cpp @@ -13,6 +13,12 @@ VCMI_LIB_NAMESPACE_BEGIN +NetworkServer::NetworkServer(INetworkServerListener & listener) + :listener(listener) +{ + +} + void NetworkServer::start(uint16_t port) { io = std::make_shared(); @@ -32,6 +38,11 @@ void NetworkServer::run() io->run(); } +void NetworkServer::run(std::chrono::milliseconds duration) +{ + io->run_for(duration); +} + void NetworkServer::connectionAccepted(std::shared_ptr upcomingConnection, const boost::system::error_code & ec) { if(ec) @@ -43,7 +54,7 @@ void NetworkServer::connectionAccepted(std::shared_ptr upcomingCo auto connection = std::make_shared(upcomingConnection, *this); connections.insert(connection); connection->start(); - onNewConnection(connection); + listener.onNewConnection(connection); startAsyncAccept(); } @@ -56,7 +67,12 @@ void NetworkServer::onDisconnected(const std::shared_ptr & co { assert(connections.count(connection)); connections.erase(connection); - onConnectionLost(connection); + listener.onDisconnected(connection); +} + +void NetworkServer::onPacketReceived(const std::shared_ptr & connection, const std::vector & message) +{ + listener.onPacketReceived(connection, message); } VCMI_LIB_NAMESPACE_END diff --git a/lib/network/NetworkServer.h b/lib/network/NetworkServer.h index 716dabe9b..bf3a67c52 100644 --- a/lib/network/NetworkServer.h +++ b/lib/network/NetworkServer.h @@ -10,6 +10,7 @@ #pragma once #include "NetworkDefines.h" +#include "NetworkListener.h" VCMI_LIB_NAMESPACE_BEGIN @@ -21,20 +22,20 @@ class DLL_LINKAGE NetworkServer : boost::noncopyable, public INetworkConnectionL std::shared_ptr acceptor; std::set> connections; + INetworkServerListener & listener; + void connectionAccepted(std::shared_ptr, const boost::system::error_code & ec); void startAsyncAccept(); void onDisconnected(const std::shared_ptr & connection) override; -protected: - virtual void onNewConnection(const std::shared_ptr &) = 0; - virtual void onConnectionLost(const std::shared_ptr &) = 0; + void onPacketReceived(const std::shared_ptr & connection, const std::vector & message) override; +public: + explicit NetworkServer(INetworkServerListener & listener); void sendPacket(const std::shared_ptr &, const std::vector & message); -public: - virtual ~NetworkServer() = default; - void start(uint16_t port); + void run(std::chrono::milliseconds duration); void run(); }; diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index 58914184c..be5a69cdb 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -51,7 +51,7 @@ struct VectorizedObjectInfo }; /// Base class for serializers capable of reading or writing data -class DLL_LINKAGE CSerializer +class DLL_LINKAGE CSerializer : boost::noncopyable { template, bool> = true> static int32_t idToNumber(const Numeric &t) diff --git a/lib/serializer/Connection.cpp b/lib/serializer/Connection.cpp index ed0625296..8d7e8be2e 100644 --- a/lib/serializer/Connection.cpp +++ b/lib/serializer/Connection.cpp @@ -10,9 +10,52 @@ #include "StdInc.h" #include "Connection.h" -#include "../networkPacks/NetPacksBase.h" +#include "BinaryDeserializer.h" +#include "BinarySerializer.h" -#include +//#include "../networkPacks/NetPacksBase.h" + +CConnection::CConnection(std::weak_ptr networkConnection) +{ + +} + +void CConnection::sendPack(const CPack * pack) +{ + +} + +CPack * CConnection::retrievePack(const std::vector & data) +{ + return nullptr; +} + +void CConnection::disableStackSendingByID() +{ + +} + +void CConnection::enterLobbyConnectionMode() +{ + +} + +void CConnection::enterGameplayConnectionMode(CGameState * gs) +{ + +} + +int CConnection::write(const void * data, unsigned size) +{ + return 0; +} + +int CConnection::read(void * data, unsigned size) +{ + return 0; +} + +#if 0 VCMI_LIB_NAMESPACE_BEGIN @@ -362,3 +405,5 @@ std::string CConnection::toString() const } VCMI_LIB_NAMESPACE_END + +#endif diff --git a/lib/serializer/Connection.h b/lib/serializer/Connection.h index 518223246..5dc43b8f6 100644 --- a/lib/serializer/Connection.h +++ b/lib/serializer/Connection.h @@ -9,123 +9,84 @@ */ #pragma once -#include "BinaryDeserializer.h" -#include "BinarySerializer.h" - -#if BOOST_VERSION >= 107000 // Boost version >= 1.70 -#include -using TSocket = boost::asio::basic_stream_socket; -using TAcceptor = boost::asio::basic_socket_acceptor; -#else -namespace boost -{ - namespace asio - { - namespace ip - { - class tcp; - } - -#if BOOST_VERSION >= 106600 // Boost version >= 1.66 - class io_context; - typedef io_context io_service; -#else - class io_service; -#endif - - template class stream_socket_service; - template - class basic_stream_socket; - - template class socket_acceptor_service; - template - class basic_socket_acceptor; - } - class mutex; -} - -typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service > TSocket; -typedef boost::asio::basic_socket_acceptor > TAcceptor; -#endif - +#include "CSerializer.h" VCMI_LIB_NAMESPACE_BEGIN +class BinaryDeserializer; +class BinarySerializer; struct CPack; struct ConnectionBuffers; +class NetworkConnection; /// Main class for network communication /// Allows establishing connection and bidirectional read-write -class DLL_LINKAGE CConnection - : public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this +class DLL_LINKAGE CConnection : public IBinaryReader, public IBinaryWriter, public std::enable_shared_from_this { - void init(); - void reportState(vstd::CLoggerBase * out) override; + /// Non-owning pointer to underlying connection + std::weak_ptr networkConnection; +// void init(); +// void reportState(vstd::CLoggerBase * out) override; +// int write(const void * data, unsigned size) override; int read(void * data, unsigned size) override; - void flushBuffers(); - - std::shared_ptr io_service; //can be empty if connection made from socket - - bool enableBufferedWrite; - bool enableBufferedRead; - std::unique_ptr connectionBuffers; +// void flushBuffers(); +// +// bool enableBufferedWrite; +// bool enableBufferedRead; +// std::unique_ptr connectionBuffers; +// + std::unique_ptr iser; + std::unique_ptr oser; +// +// std::string contactUuid; +// std::string name; //who uses this connection public: - BinaryDeserializer iser; - BinarySerializer oser; - - std::shared_ptr mutexRead; - std::shared_ptr mutexWrite; - std::shared_ptr socket; - bool connected; - bool myEndianess, contactEndianess; //true if little endian, if endianness is different we'll have to revert received multi-byte vars - std::string contactUuid; - std::string name; //who uses this connection std::string uuid; - int connectionID; - std::shared_ptr handler; - CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID); - CConnection(const std::shared_ptr & acceptor, const std::shared_ptr & Io_service, std::string Name, std::string UUID); - CConnection(std::shared_ptr Socket, std::string Name, std::string UUID); //use immediately after accepting connection into socket + CConnection(std::weak_ptr networkConnection); +// CConnection(const std::string & host, ui16 port, std::string Name, std::string UUID); +// CConnection(const std::shared_ptr & acceptor, const std::shared_ptr & Io_service, std::string Name, std::string UUID); +// CConnection(std::shared_ptr Socket, std::string Name, std::string UUID); //use immediately after accepting connection into socket +// virtual ~CConnection(); - void close(); - bool isOpen() const; - template - CConnection &operator&(const T&); - virtual ~CConnection(); - - CPack * retrievePack(); +// void close(); +// bool isOpen() const; +// +// CPack * retrievePack(); void sendPack(const CPack * pack); + CPack * retrievePack(const std::vector & data); +// std::vector serializePack(const CPack * pack); +// void disableStackSendingByID(); - void enableStackSendingByID(); - void disableSmartPointerSerialization(); - void enableSmartPointerSerialization(); - void disableSmartVectorMemberSerialization(); - void enableSmartVectorMemberSerializatoin(); - +// void enableStackSendingByID(); +// void disableSmartPointerSerialization(); +// void enableSmartPointerSerialization(); +// void disableSmartVectorMemberSerialization(); +// void enableSmartVectorMemberSerializatoin(); +// void enterLobbyConnectionMode(); void enterGameplayConnectionMode(CGameState * gs); - - std::string toString() const; - - template - CConnection & operator>>(T &t) - { - iser & t; - return * this; - } - - template - CConnection & operator<<(const T &t) - { - oser & t; - return * this; - } +// +// std::string toString() const; +// +// template +// CConnection & operator>>(T &t) +// { +// iser & t; +// return * this; +// } +// +// template +// CConnection & operator<<(const T &t) +// { +// oser & t; +// return * this; +// } }; VCMI_LIB_NAMESPACE_END diff --git a/lobby/LobbyServer.cpp b/lobby/LobbyServer.cpp index 0d1c9c183..a9eabb3ef 100644 --- a/lobby/LobbyServer.cpp +++ b/lobby/LobbyServer.cpp @@ -104,14 +104,14 @@ void LobbyServer::sendMessage(const std::shared_ptr & target, std::vector payloadBuffer(payloadBegin, payloadEnd); - sendPacket(target, payloadBuffer); + networkServer->sendPacket(target, payloadBuffer); } void LobbyServer::onNewConnection(const std::shared_ptr & connection) { } -void LobbyServer::onConnectionLost(const std::shared_ptr & connection) +void LobbyServer::onDisconnected(const std::shared_ptr & connection) { activeAccounts.erase(connection); } @@ -169,9 +169,20 @@ void LobbyServer::onPacketReceived(const std::shared_ptr & co LobbyServer::LobbyServer() : database(new LobbyDatabase()) + , networkServer(new NetworkServer(*this)) { } +void LobbyServer::start(uint16_t port) +{ + networkServer->start(port); +} + +void LobbyServer::run() +{ + networkServer->run(); +} + int main(int argc, const char * argv[]) { LobbyServer server; diff --git a/lobby/LobbyServer.h b/lobby/LobbyServer.h index ceaad47bc..8ebc0ba55 100644 --- a/lobby/LobbyServer.h +++ b/lobby/LobbyServer.h @@ -41,7 +41,7 @@ public: std::vector getRecentMessageHistory(); }; -class LobbyServer : public NetworkServer +class LobbyServer : public INetworkServerListener { struct AccountState { @@ -51,12 +51,16 @@ class LobbyServer : public NetworkServer std::map, AccountState> activeAccounts; std::unique_ptr database; + std::unique_ptr networkServer; void onNewConnection(const std::shared_ptr &) override; - void onConnectionLost(const std::shared_ptr &) override; + void onDisconnected(const std::shared_ptr &) override; void onPacketReceived(const std::shared_ptr &, const std::vector & message) override; void sendMessage(const std::shared_ptr & target, const JsonNode & json); public: LobbyServer(); + + void start(uint16_t port); + void run(); }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index cc00e345a..5c0f8edb5 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -61,6 +61,7 @@ #include "../lib/serializer/CSaveFile.h" #include "../lib/serializer/CLoadFile.h" +#include "../lib/serializer/Connection.h" #include "../lib/spells/CSpellHandler.h" @@ -969,11 +970,11 @@ void CGameHandler::onNewTurn() synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that } -void CGameHandler::run(bool resume) +void CGameHandler::start(bool resume) { LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume); - for (auto cc : lobby->connections) + for (auto cc : lobby->activeConnections) { auto players = lobby->getAllClientPlayers(cc->connectionID); std::stringstream sbuffer; @@ -1004,18 +1005,11 @@ void CGameHandler::run(bool resume) events::GameResumed::defaultExecute(serverEventBus.get()); turnOrder->onGameStarted(); +} - //wait till game is done - auto clockLast = std::chrono::steady_clock::now(); - while(lobby->getState() == EServerState::GAMEPLAY) - { - const auto clockDuration = std::chrono::steady_clock::now() - clockLast; - const int timePassed = std::chrono::duration_cast(clockDuration).count(); - clockLast += clockDuration; - turnTimerHandler.update(timePassed); - boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); - - } +void CGameHandler::tick(int millisecondsPassed) +{ + turnTimerHandler.update(millisecondsPassed); } void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h) @@ -1677,13 +1671,8 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) void CGameHandler::sendToAllClients(CPackForClient * pack) { logNetwork->trace("\tSending to all clients: %s", typeid(*pack).name()); - for (auto c : lobby->connections) - { - if(!c->isOpen()) - continue; - + for (auto c : lobby->activeConnections) c->sendPack(pack); - } } void CGameHandler::sendAndApply(CPackForClient * pack) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 152452d6a..38ca76e12 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -261,7 +261,8 @@ public: bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id); - void run(bool resume); + void start(bool resume); + void tick(int millisecondsPassed); bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector & slot); void spawnWanderingMonsters(CreatureID creatureID); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 8f4ecaf75..c88941625 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -8,12 +8,11 @@ * */ #include "StdInc.h" -#include +#include #include "../lib/filesystem/Filesystem.h" #include "../lib/campaign/CampaignState.h" #include "../lib/CThreadHelper.h" -#include "../lib/serializer/Connection.h" #include "../lib/CArtHandler.h" #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" @@ -37,12 +36,15 @@ #include "CGameHandler.h" #include "processors/PlayerMessageProcessor.h" #include "../lib/mapping/CMapInfo.h" +#include "../lib/network/NetworkServer.h" +#include "../lib/network/NetworkClient.h" #include "../lib/GameConstants.h" #include "../lib/logging/CBasicLogConfigurator.h" #include "../lib/CConfigHandler.h" #include "../lib/ScopeGuard.h" #include "../lib/serializer/CMemorySerializer.h" #include "../lib/serializer/Cast.h" +#include "../lib/serializer/Connection.h" #include "../lib/UnlockGuard.h" @@ -81,11 +83,8 @@ public: if(checker.getResult()) { - boost::unique_lock stateLock(srv->stateMutex); ApplyOnServerNetPackVisitor applier(*srv); - ptr->visit(applier); - return applier.getResult(); } else @@ -117,48 +116,101 @@ public: } }; +class CVCMIServerPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) +{ +private: + CVCMIServer & handler; + std::shared_ptr gh; + +public: + CVCMIServerPackVisitor(CVCMIServer & handler, std::shared_ptr gh) + :handler(handler), gh(gh) + { + } + + virtual bool callTyped() override { return false; } + + virtual void visitForLobby(CPackForLobby & packForLobby) override + { + handler.handleReceivedPack(std::unique_ptr(&packForLobby)); + } + + virtual void visitForServer(CPackForServer & serverPack) override + { + if (gh) + gh->handleReceivedPack(&serverPack); + else + logNetwork->error("Received pack for game server while in lobby!"); + } + + virtual void visitForClient(CPackForClient & clientPack) override + { + } +}; + std::string SERVER_NAME_AFFIX = "server"; std::string SERVER_NAME = GameConstants::VCMI_VERSION + std::string(" (") + SERVER_NAME_AFFIX + ')'; CVCMIServer::CVCMIServer(boost::program_options::variables_map & opts) - : port(3030), io(std::make_shared()), state(EServerState::LOBBY), cmdLineOptions(opts), currentClientId(1), currentPlayerId(1), restartGameplay(false) + : state(EServerState::LOBBY), cmdLineOptions(opts), currentClientId(1), currentPlayerId(1), restartGameplay(false) { uuid = boost::uuids::to_string(boost::uuids::random_generator()()); logNetwork->trace("CVCMIServer created! UUID: %s", uuid); applier = std::make_shared>(); registerTypesLobbyPacks(*applier); + uint16_t port = 3030; if(cmdLineOptions.count("port")) - port = cmdLineOptions["port"].as(); + port = cmdLineOptions["port"].as(); logNetwork->info("Port %d will be used", port); - try - { - acceptor = std::make_shared(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)); - } - catch(...) - { - logNetwork->info("Port %d is busy, trying to use random port instead", port); - if(cmdLineOptions.count("run-by-client")) - { - logNetwork->error("Port must be specified when run-by-client is used!!"); -#if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE) - ::exit(0); -#else - std::quick_exit(0); -#endif - } - acceptor = std::make_shared(*io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0)); - port = acceptor->local_endpoint().port(); - } + + networkServer = std::make_unique(*this); + networkServer->start(port); logNetwork->info("Listening for connections at port %d", port); } -CVCMIServer::~CVCMIServer() -{ - announceQueue.clear(); +CVCMIServer::~CVCMIServer() = default; - if(announceLobbyThread) - announceLobbyThread->join(); +void CVCMIServer::onNewConnection(const std::shared_ptr & connection) +{ + if (activeConnections.empty()) + establishOutgoingConnection(); + + if(state == EServerState::LOBBY) + activeConnections.push_back(std::make_shared(connection));//, SERVER_NAME, uuid);) + // TODO: else: deny connection + // TODO: else: try to reconnect / send state to reconnected client +} + +void CVCMIServer::onPacketReceived(const std::shared_ptr & connection, const std::vector & message) +{ + std::shared_ptr c = findConnection(connection); + CPack * pack = c->retrievePack(message); + pack->c = c; + CVCMIServerPackVisitor visitor(*this, this->gh); + pack->visit(visitor); + + //FIXME: delete pack? +} + +void CVCMIServer::onPacketReceived(const std::vector & message) +{ + //TODO: handle pack received from lobby +} + +void CVCMIServer::onConnectionFailed(const std::string & errorMessage) +{ + //TODO: handle failure to connect to lobby +} + +void CVCMIServer::onConnectionEstablished() +{ + //TODO: handle connection to lobby - login? +} + +void CVCMIServer::onDisconnected() +{ + //TODO: handle disconnection from lobby } void CVCMIServer::setState(EServerState value) @@ -171,101 +223,60 @@ EServerState CVCMIServer::getState() const return state.load(); } +std::shared_ptr CVCMIServer::findConnection(const std::shared_ptr & netConnection) +{ + //TODO + assert(0); + return nullptr; +} + void CVCMIServer::run() { +#if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP) if(!restartGameplay) { - this->announceLobbyThread = std::make_unique(&CVCMIServer::threadAnnounceLobby, this); - - startAsyncAccept(); - if(!remoteConnectionsThread && cmdLineOptions.count("lobby")) - { - remoteConnectionsThread = std::make_unique(&CVCMIServer::establishRemoteConnections, this); - } - -#if defined(VCMI_ANDROID) -#ifndef SINGLE_PROCESS_APP CAndroidVMHelper vmHelper; vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "onServerReady"); + } #endif -#endif - } - while(state == EServerState::LOBBY || state == EServerState::GAMEPLAY_STARTING) - boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); + static const int serverUpdateIntervalMilliseconds = 50; + auto clockInitial = std::chrono::steady_clock::now(); + int64_t msPassedLast = 0; - logNetwork->info("Thread handling connections ended"); - - if(state == EServerState::GAMEPLAY) - { - gh->run(si->mode == StartInfo::LOAD_GAME); - } - while(state == EServerState::GAMEPLAY_ENDED) - boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); -} - -void CVCMIServer::establishRemoteConnections() -{ - setThreadName("establishConnection"); - - //wait for host connection - while(connections.empty()) - boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); - - uuid = cmdLineOptions["lobby-uuid"].as(); - int numOfConnections = cmdLineOptions["connections"].as(); - for(int i = 0; i < numOfConnections; ++i) - connectToRemote(); -} - -void CVCMIServer::connectToRemote() -{ - std::shared_ptr c; - try - { - auto address = cmdLineOptions["lobby"].as(); - int port = cmdLineOptions["lobby-port"].as(); - - logNetwork->info("Establishing connection to remote at %s:%d with uuid %s", address, port, uuid); - c = std::make_shared(address, port, SERVER_NAME, uuid); - } - catch(...) - { - logNetwork->error("\nCannot establish remote connection!"); - } - - if(c) - { - connections.insert(c); - remoteConnections.insert(c); - c->handler = std::make_shared(&CVCMIServer::threadHandleClient, this, c); - } -} - -void CVCMIServer::threadAnnounceLobby() -{ - setThreadName("announceLobby"); while(state != EServerState::SHUTDOWN) { - { - boost::unique_lock myLock(mx); - while(!announceQueue.empty()) - { - announcePack(std::move(announceQueue.front())); - announceQueue.pop_front(); - } + networkServer->run(std::chrono::milliseconds(serverUpdateIntervalMilliseconds)); - if(acceptor) - { - io->reset(); - io->poll(); - } - } + const auto clockNow = std::chrono::steady_clock::now(); + const auto clockPassed = clockNow - clockInitial; + const int64_t msPassedNow = std::chrono::duration_cast(clockPassed).count(); + const int64_t msDelta = msPassedNow - msPassedLast; + msPassedLast = msPassedNow; - boost::this_thread::sleep_for(boost::chrono::milliseconds(50)); + if (state == EServerState::GAMEPLAY) + gh->tick(msDelta); } } +void CVCMIServer::establishOutgoingConnection() +{ + if(!cmdLineOptions.count("lobby")) + return; + + uuid = cmdLineOptions["lobby-uuid"].as(); + auto address = cmdLineOptions["lobby"].as(); + int port = cmdLineOptions["lobby-port"].as(); + logNetwork->info("Establishing connection to remote at %s:%d with uuid %s", address, port, uuid); + + outgoingConnection = std::make_unique(*this); + + outgoingConnection->start(address, port);//, SERVER_NAME, uuid); + +// connections.insert(c); +// remoteConnections.insert(c); +} + void CVCMIServer::prepareToRestart() { if(state == EServerState::GAMEPLAY) @@ -280,11 +291,9 @@ void CVCMIServer::prepareToRestart() campaignMap = si->campState->currentScenario().value_or(CampaignScenarioID(0)); campaignBonus = si->campState->getBonusID(campaignMap).value_or(-1); } - // FIXME: dirry hack to make sure old CGameHandler::run is finished - boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); } - for(auto c : connections) + for(auto c : activeConnections) { c->enterLobbyConnectionMode(); c->disableStackSendingByID(); @@ -306,10 +315,11 @@ bool CVCMIServer::prepareToStartGame() { if(progressTracking.get() != currentProgress) { + //FIXME: UNGUARDED MULTITHREADED ACCESS!!! currentProgress = progressTracking.get(); std::unique_ptr loadProgress(new LobbyLoadProgress); loadProgress->progress = currentProgress; - addToAnnounceQueue(std::move(loadProgress)); + announcePack(std::move(loadProgress)); } boost::this_thread::sleep(boost::posix_time::milliseconds(50)); } @@ -355,149 +365,55 @@ bool CVCMIServer::prepareToStartGame() return true; } -void CVCMIServer::startGameImmidiately() +void CVCMIServer::startGameImmediately() { - for(auto c : connections) + for(auto c : activeConnections) c->enterGameplayConnectionMode(gh->gs); + gh->start(si->mode == StartInfo::LOAD_GAME); state = EServerState::GAMEPLAY; } -void CVCMIServer::startAsyncAccept() +void CVCMIServer::onDisconnected(const std::shared_ptr & connection) { - assert(!upcomingConnection); - assert(acceptor); + logNetwork->error("Network error receiving a pack. Connection has been closed"); -#if BOOST_VERSION >= 107000 // Boost version >= 1.70 - upcomingConnection = std::make_shared(acceptor->get_executor()); -#else - upcomingConnection = std::make_shared(acceptor->get_io_service()); -#endif - acceptor->async_accept(*upcomingConnection, std::bind(&CVCMIServer::connectionAccepted, this, _1)); -} + std::shared_ptr c = findConnection(connection); -void CVCMIServer::connectionAccepted(const boost::system::error_code & ec) -{ - if(ec) + inactiveConnections.push_back(c); + vstd::erase(activeConnections, c); + + if(activeConnections.empty() || hostClientId == c->connectionID) + state = EServerState::SHUTDOWN; + + if(gh && state == EServerState::GAMEPLAY) { - if(state != EServerState::SHUTDOWN) - logNetwork->info("Something wrong during accepting: %s", ec.message()); - return; - } - - try - { - if(state == EServerState::LOBBY || !hangingConnections.empty()) - { - logNetwork->info("We got a new connection! :)"); - auto c = std::make_shared(upcomingConnection, SERVER_NAME, uuid); - upcomingConnection.reset(); - connections.insert(c); - c->handler = std::make_shared(&CVCMIServer::threadHandleClient, this, c); - } - } - catch(std::exception & e) - { - logNetwork->error("Failure processing new connection! %s", e.what()); - upcomingConnection.reset(); - } - - startAsyncAccept(); -} - -class CVCMIServerPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) -{ -private: - CVCMIServer & handler; - std::shared_ptr gh; - -public: - CVCMIServerPackVisitor(CVCMIServer & handler, std::shared_ptr gh) - :handler(handler), gh(gh) - { - } - - virtual bool callTyped() override { return false; } - - virtual void visitForLobby(CPackForLobby & packForLobby) override - { - handler.handleReceivedPack(std::unique_ptr(&packForLobby)); - } - - virtual void visitForServer(CPackForServer & serverPack) override - { - if (gh) - gh->handleReceivedPack(&serverPack); - else - logNetwork->error("Received pack for game server while in lobby!"); - } - - virtual void visitForClient(CPackForClient & clientPack) override - { - } -}; - -void CVCMIServer::threadHandleClient(std::shared_ptr c) -{ - setThreadName("handleClient"); - c->enterLobbyConnectionMode(); - - while(c->connected) - { - CPack * pack; - - try - { - pack = c->retrievePack(); - pack->c = c; - } - catch(boost::system::system_error & e) - { - if (e.code() == boost::asio::error::eof) - logNetwork->error("Network error receiving a pack. Connection has been closed"); - else - logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what()); - - hangingConnections.insert(c); - connections.erase(c); - if(connections.empty() || hostClient == c) - state = EServerState::SHUTDOWN; - - if(gh && state == EServerState::GAMEPLAY) - { - gh->handleClientDisconnection(c); - } - break; - } - - CVCMIServerPackVisitor visitor(*this, this->gh); - pack->visit(visitor); + gh->handleClientDisconnection(c); } boost::unique_lock queueLock(mx); - if(c->connected) - { - auto lcd = std::make_unique(); - lcd->c = c; - lcd->clientId = c->connectionID; - handleReceivedPack(std::move(lcd)); - } - - logNetwork->info("Thread listening for %s ended", c->toString()); - c->handler.reset(); +// if(c->connected) +// { +// auto lcd = std::make_unique(); +// lcd->c = c; +// lcd->clientId = c->connectionID; +// handleReceivedPack(std::move(lcd)); +// } +// +// logNetwork->info("Thread listening for %s ended", c->toString()); } void CVCMIServer::handleReceivedPack(std::unique_ptr pack) { CBaseForServerApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(pack.get())); if(apply->applyOnServerBefore(this, pack.get())) - addToAnnounceQueue(std::move(pack)); + announcePack(std::move(pack)); } void CVCMIServer::announcePack(std::unique_ptr pack) { - for(auto c : connections) + for(auto c : activeConnections) { // FIXME: we need to avoid sending something to client that not yet get answer for LobbyClientConnected // Until UUID set we only pass LobbyClientConnected to this client @@ -515,7 +431,7 @@ void CVCMIServer::announceMessage(const std::string & txt) logNetwork->info("Show message: %s", txt); auto cm = std::make_unique(); cm->message = txt; - addToAnnounceQueue(std::move(cm)); + announcePack(std::move(cm)); } void CVCMIServer::announceTxt(const std::string & txt, const std::string & playerName) @@ -524,25 +440,18 @@ void CVCMIServer::announceTxt(const std::string & txt, const std::string & playe auto cm = std::make_unique(); cm->playerName = playerName; cm->message = txt; - addToAnnounceQueue(std::move(cm)); -} - -void CVCMIServer::addToAnnounceQueue(std::unique_ptr pack) -{ - boost::unique_lock queueLock(mx); - announceQueue.push_back(std::move(pack)); + announcePack(std::move(cm)); } bool CVCMIServer::passHost(int toConnectionId) { - for(auto c : connections) + for(auto c : activeConnections) { if(isClientHost(c->connectionID)) continue; if(c->connectionID != toConnectionId) continue; - hostClient = c; hostClientId = c->connectionID; announceTxt(boost::str(boost::format("Pass host to connection %d") % toConnectionId)); return true; @@ -555,9 +464,8 @@ void CVCMIServer::clientConnected(std::shared_ptr c, std::vectorconnectionID = currentClientId++; - if(!hostClient) + if(hostClientId == -1) { - hostClient = c; hostClientId = c->connectionID; si->mode = mode; } @@ -592,8 +500,9 @@ void CVCMIServer::clientConnected(std::shared_ptr c, std::vector c) { - connections -= c; - if(connections.empty() || hostClient == c) + vstd::erase(activeConnections, c); + + if(activeConnections.empty() || hostClientId == c->connectionID) { state = EServerState::SHUTDOWN; return; @@ -626,7 +535,7 @@ void CVCMIServer::clientDisconnected(std::shared_ptr c) if(gh && si && state == EServerState::GAMEPLAY) { gh->playerMessages->broadcastMessage(playerSettings->color, playerLeftMsgText); - gh->connections[playerSettings->color].insert(hostClient); + // gh->connections[playerSettings->color].insert(hostClient); startAiPack.players.push_back(playerSettings->color); } } @@ -753,7 +662,6 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr mapInfo, void CVCMIServer::updateAndPropagateLobbyState() { - boost::unique_lock stateLock(stateMutex); // Update player settings for RMG // TODO: find appropriate location for this code if(si->mapGenOptions && si->mode == StartInfo::NEW_GAME) @@ -772,7 +680,7 @@ void CVCMIServer::updateAndPropagateLobbyState() auto lus = std::make_unique(); lus->state = *this; - addToAnnounceQueue(std::move(lus)); + announcePack(std::move(lus)); } void CVCMIServer::setPlayer(PlayerColor clickedColor) @@ -1188,32 +1096,9 @@ int main(int argc, const char * argv[]) cond->notify_one(); #endif - try - { - boost::asio::io_service io_service; - CVCMIServer server(opts); + CVCMIServer server(opts); + server.run(); - try - { - while(server.getState() != EServerState::SHUTDOWN) - { - server.run(); - } - io_service.run(); - } - catch(boost::system::system_error & e) //for boost errors just log, not crash - probably client shut down connection - { - logNetwork->error(e.what()); - server.setState(EServerState::SHUTDOWN); - } - } - catch(boost::system::system_error & e) - { - logNetwork->error(e.what()); - //catch any startup errors (e.g. can't access port) errors - //and return non-zero status so client can detect error - throw; - } #if VCMI_ANDROID_DUAL_PROCESS CAndroidVMHelper envHelper; envHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "killServer"); diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index 1bcac979a..d83c3edf0 100644 --- a/server/CVCMIServer.h +++ b/server/CVCMIServer.h @@ -9,10 +9,10 @@ */ #pragma once -#include "../lib/serializer/Connection.h" +#include "../lib/network/NetworkListener.h" #include "../lib/StartInfo.h" -#include +#include #if defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP) #define VCMI_ANDROID_DUAL_PROCESS 1 @@ -24,6 +24,7 @@ class CMapInfo; struct CPackForLobby; +class CConnection; struct StartInfo; struct LobbyInfo; struct PlayerSettings; @@ -46,53 +47,66 @@ enum class EServerState : ui8 SHUTDOWN }; -class CVCMIServer : public LobbyInfo +class CVCMIServer : public LobbyInfo, public INetworkServerListener, public INetworkClientListener { + /// Network server instance that receives and processes incoming connections on active socket + std::unique_ptr networkServer; + + /// Outgoing connection established by this server to game lobby for proxy mode (only in lobby game) + std::unique_ptr outgoingConnection; + +public: + /// List of all active connections + std::vector> activeConnections; + +private: + /// List of all connections that were closed (but can still reconnect later) + std::vector> inactiveConnections; + std::atomic restartGameplay; // FIXME: this is just a hack - std::shared_ptr io; - std::shared_ptr acceptor; - std::shared_ptr upcomingConnection; - std::list> announceQueue; + boost::recursive_mutex mx; std::shared_ptr> applier; - std::unique_ptr announceLobbyThread; std::unique_ptr remoteConnectionsThread; std::atomic state; -public: - std::shared_ptr gh; - ui16 port; + // INetworkServerListener impl + void onDisconnected(const std::shared_ptr & connection) override; + void onPacketReceived(const std::shared_ptr & connection, const std::vector & message) override; + void onNewConnection(const std::shared_ptr &) override; + + // INetworkClientListener impl + void onPacketReceived(const std::vector & message) override; + void onConnectionFailed(const std::string & errorMessage) override; + void onConnectionEstablished() override; + void onDisconnected() override; + + void establishOutgoingConnection(); + + std::shared_ptr findConnection(const std::shared_ptr &); - boost::program_options::variables_map cmdLineOptions; - std::set> connections; - std::set> remoteConnections; - std::set> hangingConnections; //keep connections of players disconnected during the game - std::atomic currentClientId; std::atomic currentPlayerId; - std::shared_ptr hostClient; + +public: + std::shared_ptr gh; + boost::program_options::variables_map cmdLineOptions; CVCMIServer(boost::program_options::variables_map & opts); ~CVCMIServer(); + void run(); + bool prepareToStartGame(); void prepareToRestart(); - void startGameImmidiately(); + void startGameImmediately(); - void establishRemoteConnections(); - void connectToRemote(); - void startAsyncAccept(); - void connectionAccepted(const boost::system::error_code & ec); void threadHandleClient(std::shared_ptr c); - void threadAnnounceLobby(); - void handleReceivedPack(std::unique_ptr pack); void announcePack(std::unique_ptr pack); bool passHost(int toConnectionId); void announceTxt(const std::string & txt, const std::string & playerName = "system"); - void announceMessage(const std::string & txt); - void addToAnnounceQueue(std::unique_ptr pack); void setPlayerConnectedId(PlayerSettings & pset, ui8 player) const; void updateStartInfoOnMapChange(std::shared_ptr mapInfo, std::shared_ptr mapGenOpt = {}); @@ -101,6 +115,11 @@ public: void clientDisconnected(std::shared_ptr c); void reconnectPlayer(int connId); +public: + void announceMessage(const std::string & txt); + + void handleReceivedPack(std::unique_ptr pack); + void updateAndPropagateLobbyState(); void setState(EServerState value); diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index bb99323a5..254d20b22 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/server/NetPacksLobbyServer.cpp @@ -13,11 +13,9 @@ #include "CVCMIServer.h" #include "CGameHandler.h" -#include "../lib/serializer/Connection.h" #include "../lib/StartInfo.h" - -// Campaigns #include "../lib/campaign/CampaignState.h" +#include "../lib/serializer/Connection.h" void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack) { @@ -38,67 +36,18 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitForLobby(CPackForLobby & pac void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack) { - if(srv.gh) - { - for(auto & connection : srv.hangingConnections) - { - if(connection->uuid == pack.uuid) - { - { - result = true; - return; - } - } - } - } - if(srv.getState() == EServerState::LOBBY) { result = true; return; } - //disconnect immediately and ignore this client - srv.connections.erase(pack.c); - if(pack.c && pack.c->isOpen()) - { - pack.c->close(); - pack.c->connected = false; - } - { result = false; return; - } } void ApplyOnServerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack) { - if(srv.gh) - { - for(auto & connection : srv.hangingConnections) - { - if(connection->uuid == pack.uuid) - { - logNetwork->info("Reconnection player"); - pack.c->connectionID = connection->connectionID; - for(auto & playerConnection : srv.gh->connections) - { - for(auto & existingConnection : playerConnection.second) - { - if(existingConnection == connection) - { - playerConnection.second.erase(existingConnection); - playerConnection.second.insert(pack.c); - break; - } - } - } - srv.hangingConnections.erase(connection); - break; - } - } - } - srv.clientConnected(pack.c, pack.names, pack.uuid, pack.mode); // Server need to pass some data to newly connected client pack.clientId = pack.c->connectionID; @@ -121,7 +70,7 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyCl startGameForReconnectedPlayer->initializedStartInfo = srv.si; startGameForReconnectedPlayer->initializedGameState = srv.gh->gameState(); startGameForReconnectedPlayer->clientId = pack.c->connectionID; - srv.addToAnnounceQueue(std::move(startGameForReconnectedPlayer)); + srv.announcePack(std::move(startGameForReconnectedPlayer)); } } @@ -154,38 +103,28 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyC void ApplyOnServerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack) { srv.clientDisconnected(pack.c); - pack.c->close(); - pack.c->connected = false; - result = true; } void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack) { - if(pack.c && pack.c->isOpen()) - { - boost::unique_lock lock(*pack.c->mutexWrite); - pack.c->close(); - pack.c->connected = false; - } - if(pack.shutdownServer) { logNetwork->info("Client requested shutdown, server will close itself..."); srv.setState(EServerState::SHUTDOWN); return; } - else if(srv.connections.empty()) + else if(srv.activeConnections.empty()) { logNetwork->error("Last connection lost, server will close itself..."); srv.setState(EServerState::SHUTDOWN); } - else if(pack.c == srv.hostClient) + else if(pack.c->connectionID == srv.hostClientId) { auto ph = std::make_unique(); - auto newHost = *RandomGeneratorUtil::nextItem(srv.connections, CRandomGenerator::getDefault()); + auto newHost = srv.activeConnections.front(); ph->newHostConnectionId = newHost->connectionID; - srv.addToAnnounceQueue(std::move(ph)); + srv.announcePack(std::move(ph)); } srv.updateAndPropagateLobbyState(); @@ -270,8 +209,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) { - boost::unique_lock stateLock(srv.stateMutex); - for(auto & c : srv.connections) + for(auto & c : srv.activeConnections) { c->enterLobbyConnectionMode(); c->disableStackSendingByID(); @@ -312,10 +250,10 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) { if(pack.clientId == -1) //do not restart game for single client only - srv.startGameImmidiately(); + srv.startGameImmediately(); else { - for(auto & c : srv.connections) + for(auto & c : srv.activeConnections) { if(c->connectionID == pack.clientId) { diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index a3d77db31..95287bfe8 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -20,11 +20,11 @@ #include "../lib/IGameCallback.h" #include "../lib/mapObjects/CGTownInstance.h" +#include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/gameState/CGameState.h" #include "../lib/battle/IBattleState.h" #include "../lib/battle/BattleAction.h" #include "../lib/battle/Unit.h" -#include "../lib/serializer/Connection.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/spells/ISpellMechanics.h" #include "../lib/serializer/Cast.h" diff --git a/server/processors/PlayerMessageProcessor.cpp b/server/processors/PlayerMessageProcessor.cpp index 04b4456a3..4bd176f7a 100644 --- a/server/processors/PlayerMessageProcessor.cpp +++ b/server/processors/PlayerMessageProcessor.cpp @@ -13,7 +13,6 @@ #include "../CGameHandler.h" #include "../CVCMIServer.h" -#include "../../lib/serializer/Connection.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CHeroHandler.h" #include "../../lib/modding/IdentifierStorage.h" @@ -22,11 +21,13 @@ #include "../../lib/StartInfo.h" #include "../../lib/gameState/CGameState.h" #include "../../lib/mapObjects/CGTownInstance.h" +#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/modding/IdentifierStorage.h" #include "../../lib/modding/ModScope.h" #include "../../lib/mapping/CMap.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/StackLocation.h" +#include "../../lib/serializer/Connection.h" PlayerMessageProcessor::PlayerMessageProcessor() :gameHandler(nullptr) @@ -62,10 +63,7 @@ bool PlayerMessageProcessor::handleHostCommand(PlayerColor player, const std::st std::vector words; boost::split(words, message, boost::is_any_of(" ")); - bool isHost = false; - for(auto & c : gameHandler->connections[player]) - if(gameHandler->gameLobby()->isClientHost(c->connectionID)) - isHost = true; + bool isHost = gameHandler->gameLobby()->isPlayerHost(player); if(!isHost || words.size() < 2 || words[0] != "game") return false;