/* * NetPacksLobbyServer.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 "LobbyNetPackVisitors.h" #include "CVCMIServer.h" #include "CGameHandler.h" #include "../lib/NetPacksLobby.h" #include "../lib/serializer/Connection.h" #include "../lib/StartInfo.h" // Campaigns #include "../lib/campaign/CampaignState.h" void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack) { if(pack.isForServer()) { result = srv.isClientHost(pack.c->connectionID); } } void ApplyOnServerAfterAnnounceNetPackVisitor::visitForLobby(CPackForLobby & pack) { // Propogate options after every CLobbyPackToServer if(pack.isForServer()) { srv.updateAndPropagateLobbyState(); } } 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; pack.mode = srv.si->mode; pack.hostClientId = srv.hostClientId; result = true; } void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack) { // FIXME: we need to avoid senting something to client that not yet get answer for LobbyClientConnected // Until UUID set we only pass LobbyClientConnected to this client pack.c->uuid = pack.uuid; srv.updateAndPropagateLobbyState(); if(srv.getState() == EServerState::GAMEPLAY) { //immediately start game std::unique_ptr startGameForReconnectedPlayer(new LobbyStartGame); startGameForReconnectedPlayer->initializedStartInfo = srv.si; startGameForReconnectedPlayer->initializedGameState = srv.gh->gameState(); startGameForReconnectedPlayer->clientId = pack.c->connectionID; srv.addToAnnounceQueue(std::move(startGameForReconnectedPlayer)); } } void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack) { if(pack.clientId != pack.c->connectionID) { result = false; return; } if(pack.shutdownServer) { if(!srv.cmdLineOptions.count("run-by-client")) { result = false; return; } if(pack.c->uuid != srv.cmdLineOptions["uuid"].as()) { result = false; return; } } result = true; } 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()) { logNetwork->error("Last connection lost, server will close itself..."); srv.setState(EServerState::SHUTDOWN); } else if(pack.c == srv.hostClient) { auto ph = std::make_unique(); auto newHost = *RandomGeneratorUtil::nextItem(srv.connections, CRandomGenerator::getDefault()); ph->newHostConnectionId = newHost->connectionID; srv.addToAnnounceQueue(std::move(ph)); } srv.updateAndPropagateLobbyState(); if(srv.getState() != EServerState::SHUTDOWN && srv.remoteConnections.count(pack.c)) { srv.remoteConnections -= pack.c; srv.connectToRemote(); } } void ClientPermissionsCheckerNetPackVisitor::visitLobbyChatMessage(LobbyChatMessage & pack) { result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack) { if(srv.getState() != EServerState::LOBBY) { result = false; return; } srv.updateStartInfoOnMapChange(pack.mapInfo, pack.mapGenOpts); result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetCampaign(LobbySetCampaign & pack) { srv.si->mapname = pack.ourCampaign->getFilename(); srv.si->mode = StartInfo::CAMPAIGN; srv.si->campState = pack.ourCampaign; srv.si->turnTimerInfo = TurnTimerInfo{}; bool isCurrentMapConquerable = pack.ourCampaign->currentScenario() && pack.ourCampaign->isAvailable(*pack.ourCampaign->currentScenario()); for(auto scenarioID : pack.ourCampaign->allScenarios()) { if(pack.ourCampaign->isAvailable(scenarioID)) { if(!isCurrentMapConquerable || (isCurrentMapConquerable && scenarioID == *pack.ourCampaign->currentScenario())) { srv.setCampaignMap(scenarioID); } } } result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetCampaignMap(LobbySetCampaignMap & pack) { srv.setCampaignMap(pack.mapId); result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetCampaignBonus(LobbySetCampaignBonus & pack) { srv.setCampaignBonus(pack.bonusId); result = true; } void ClientPermissionsCheckerNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack) { result = srv.isClientHost(pack.c->connectionID); } void ClientPermissionsCheckerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) { result = srv.isClientHost(pack.c->connectionID); } void ApplyOnServerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) { srv.prepareToRestart(); result = true; } void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack) { boost::unique_lock stateLock(srv.stateMutex); for(auto & c : srv.connections) { c->enterLobbyConnectionMode(); c->disableStackSendingByID(); } } void ClientPermissionsCheckerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) { result = srv.isClientHost(pack.c->connectionID); } void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) { try { srv.verifyStateBeforeStart(true); } catch(...) { result = false; return; } // Server will prepare gamestate and we announce StartInfo to clients if(!srv.prepareToStartGame()) { result = false; return; } pack.initializedStartInfo = std::make_shared(*srv.gh->getStartInfo(true)); pack.initializedGameState = srv.gh->gameState(); srv.setState(EServerState::GAMEPLAY_STARTING); result = true; } void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) { if(pack.clientId == -1) //do not restart game for single client only srv.startGameImmidiately(); else { for(auto & c : srv.connections) { if(c->connectionID == pack.clientId) { c->enterGameplayConnectionMode(srv.gh->gameState()); srv.reconnectPlayer(pack.clientId); } } } } void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack) { result = srv.isClientHost(pack.c->connectionID); } void ApplyOnServerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack) { result = true; } void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack) { auto result = srv.passHost(pack.newHostConnectionId); if(!result) { logGlobal->error("passHost returned false. What does it mean?"); } } void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) { if(srv.isClientHost(pack.c->connectionID)) { result = true; return; } if(vstd::contains(srv.getAllClientPlayers(pack.c->connectionID), pack.color)) { result = true; return; } result = false; } void ApplyOnServerNetPackVisitor::visitLobbyChangePlayerOption(LobbyChangePlayerOption & pack) { switch(pack.what) { case LobbyChangePlayerOption::TOWN_ID: srv.optionSetCastle(pack.color, FactionID(pack.value)); break; case LobbyChangePlayerOption::TOWN: srv.optionNextCastle(pack.color, pack.value); break; case LobbyChangePlayerOption::HERO_ID: srv.optionSetHero(pack.color, HeroTypeID(pack.value)); break; case LobbyChangePlayerOption::HERO: srv.optionNextHero(pack.color, pack.value); break; case LobbyChangePlayerOption::BONUS_ID: srv.optionSetBonus(pack.color, PlayerStartingBonus(pack.value)); break; case LobbyChangePlayerOption::BONUS: srv.optionNextBonus(pack.color, pack.value); break; } result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetPlayer(LobbySetPlayer & pack) { srv.setPlayer(pack.clickedColor); result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetSimturns(LobbySetSimturns & pack) { srv.si->simturnsInfo = pack.simturnsInfo; result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetTurnTime(LobbySetTurnTime & pack) { srv.si->turnTimerInfo = pack.turnTimerInfo; result = true; } void ApplyOnServerNetPackVisitor::visitLobbySetDifficulty(LobbySetDifficulty & pack) { srv.si->difficulty = std::clamp(pack.difficulty, 0, 4); result = true; } void ApplyOnServerNetPackVisitor::visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) { srv.si->playerInfos[pack.targetPlayerColor].connectedPlayerIDs.insert(pack.targetConnectedPlayer); result = true; }