| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 | /* * 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/StartInfo.h"#include "../lib/CRandomGenerator.h"#include "../lib/campaign/CampaignState.h"#include "../lib/entities/faction/CTownHandler.h"#include "../lib/filesystem/Filesystem.h"#include "../lib/gameState/CGameState.h"#include "../lib/mapping/CMapInfo.h"#include "../lib/serializer/Connection.h"void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack){	if(pack.isForServer())	{		result = srv.isClientHost(connection->connectionID);	}}void ApplyOnServerAfterAnnounceNetPackVisitor::visitForLobby(CPackForLobby & pack){	// Propagate options after every CLobbyPackToServer	if(pack.isForServer())	{		srv.updateAndPropagateLobbyState();	}}void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack){	result = srv.getState() == EServerState::LOBBY;}void ApplyOnServerNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack){	auto compatibleVersion = std::min(pack.version, ESerializationVersion::CURRENT);	connection->setSerializationVersion(compatibleVersion);	srv.clientConnected(connection, pack.names, pack.uuid, pack.mode);	// Server need to pass some data to newly connected client	pack.clientId = connection->connectionID;	pack.mode = srv.si->mode;	pack.hostClientId = srv.hostClientId;	pack.version = compatibleVersion;	result = true;}void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyClientConnected & pack){	srv.updateAndPropagateLobbyState();}void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack){	if(pack.clientId != connection->connectionID)	{		result = false;		return;	}	if(pack.shutdownServer)	{		if(!srv.wasStartedByClient())		{			result = false;			return;		}		if(connection->connectionID != srv.hostClientId)		{			result = false;			return;		}	}	result = true;}void ApplyOnServerNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack){	connection->getConnection()->close();	srv.clientDisconnected(connection);	result = true;}void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack){	srv.updateAndPropagateLobbyState();}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 = EStartMode::CAMPAIGN;	srv.si->campState = pack.ourCampaign;	srv.si->turnTimerInfo = TurnTimerInfo{};	bool isCurrentMapConquerable = pack.ourCampaign->currentScenario() && pack.ourCampaign->isAvailable(*pack.ourCampaign->currentScenario());	auto scenarios = pack.ourCampaign->allScenarios();	for(auto scenarioID : boost::adaptors::reverse(scenarios)) // reverse -> on multiple scenario selection set lowest id at the end	{		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(connection->connectionID);}void ClientPermissionsCheckerNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack){	result = srv.isClientHost(connection->connectionID);}void ApplyOnServerNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack){	srv.prepareToRestart();	result = true;}void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyRestartGame(LobbyRestartGame & pack){	for(const auto & connection : srv.activeConnections)		connection->enterLobbyConnectionMode();}void ClientPermissionsCheckerNetPackVisitor::visitLobbyPrepareStartGame(LobbyPrepareStartGame & pack){	result = srv.isClientHost(connection->connectionID);}void ClientPermissionsCheckerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack){	result = srv.isClientHost(connection->connectionID);}void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack){	try	{		srv.verifyStateBeforeStart(true);	}	catch(const std::exception &)	{		result = false;		return;	}		// Server will prepare gamestate and we announce StartInfo to clients	if(!srv.prepareToStartGame())	{		result = false;		return;	}		pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->gameState().getInitialStartInfo());	pack.initializedGameState = srv.gh->gs;	result = true;}void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack){	if(pack.clientId == -1) //do not restart game for single client only		srv.startGameImmediately();	else	{		for(const auto & connection : srv.activeConnections)		{			if(connection->connectionID == pack.clientId)			{				connection->setCallback(srv.gh->gameInfo());				srv.reconnectPlayer(pack.clientId);			}		}	}}void ClientPermissionsCheckerNetPackVisitor::visitLobbyChangeHost(LobbyChangeHost & pack){	result = srv.isClientHost(connection->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(connection->connectionID))	{		result = true;		return;	}	if(vstd::contains(srv.getAllClientPlayers(connection->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, static_cast<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::visitLobbySetPlayerName(LobbySetPlayerName & pack){	srv.setPlayerName(pack.color, pack.name);	result = true;}void ApplyOnServerNetPackVisitor::visitLobbySetPlayerHandicap(LobbySetPlayerHandicap & pack){	srv.setPlayerHandicap(pack.color, pack.handicap);	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::visitLobbySetExtraOptions(LobbySetExtraOptions & pack){	srv.si->extraOptionsInfo = pack.extraOptionsInfo;	result = true;}void ApplyOnServerNetPackVisitor::visitLobbySetDifficulty(LobbySetDifficulty & pack){	srv.si->difficulty = std::clamp<uint8_t>(pack.difficulty, 0, 4);	result = true;}void ApplyOnServerNetPackVisitor::visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack){	srv.si->playerInfos[pack.targetPlayerColor].connectedPlayerIDs.insert(pack.targetConnectedPlayer);	result = true;}void ClientPermissionsCheckerNetPackVisitor::visitLobbyPvPAction(LobbyPvPAction & pack){	result = true;}void ApplyOnServerNetPackVisitor::visitLobbyPvPAction(LobbyPvPAction & pack){	std::vector<FactionID> allowedTowns;	for (auto const & factionID : LIBRARY->townh->getDefaultAllowed())		if(std::find(pack.bannedTowns.begin(), pack.bannedTowns.end(), factionID) == pack.bannedTowns.end())			allowedTowns.push_back(factionID);	std::vector<FactionID> randomFaction1;	std::sample(allowedTowns.begin(), allowedTowns.end(), std::back_inserter(randomFaction1), 1, std::mt19937{std::random_device{}()});	std::vector<FactionID> randomFaction2;	std::sample(allowedTowns.begin(), allowedTowns.end(), std::back_inserter(randomFaction2), 1, std::mt19937{std::random_device{}()});	MetaString txt;	switch(pack.action) {		case LobbyPvPAction::COIN:			txt.appendTextID("vcmi.lobby.pvp.coin.hover");			txt.appendRawString(" - " + std::to_string(CRandomGenerator::getDefault().nextInt(1)));			srv.announceTxt(txt);			break;		case LobbyPvPAction::RANDOM_TOWN:			if(!allowedTowns.size())				break;			txt.appendTextID("core.overview.3");			txt.appendRawString(" - ");			txt.appendTextID(LIBRARY->townh->getById(randomFaction1[0])->getNameTextID());			srv.announceTxt(txt);			break;		case LobbyPvPAction::RANDOM_TOWN_VS:			if(!allowedTowns.size())				break;			txt.appendTextID("core.overview.3");			txt.appendRawString(" - ");			txt.appendTextID(LIBRARY->townh->getById(randomFaction1[0])->getNameTextID());			txt.appendRawString(" ");			txt.appendTextID("vcmi.lobby.pvp.versus");			txt.appendRawString(" ");			txt.appendTextID(LIBRARY->townh->getById(randomFaction2[0])->getNameTextID());			srv.announceTxt(txt);			break;	}	result = true;}void ClientPermissionsCheckerNetPackVisitor::visitLobbyDelete(LobbyDelete & pack){	result = srv.isClientHost(connection->connectionID);}void ApplyOnServerNetPackVisitor::visitLobbyDelete(LobbyDelete & pack){	if(pack.type == LobbyDelete::EType::SAVEGAME || pack.type == LobbyDelete::EType::RANDOMMAP)	{		auto res = ResourcePath(pack.name, pack.type == LobbyDelete::EType::SAVEGAME ? EResType::SAVEGAME : EResType::MAP);		auto name = CResourceHandler::get()->getResourceName(res);		if (!name)		{			logGlobal->error("Failed to find resource with name '%s'", res.getOriginalName());			return;		}		boost::system::error_code ec;		auto file = boost::filesystem::canonical(*name, ec);		if (ec)		{			logGlobal->error("Failed to delete file '%s'. Reason: %s", res.getOriginalName(), ec.message());		}		else		{			boost::filesystem::remove(file);			if(boost::filesystem::is_empty(file.parent_path()))				boost::filesystem::remove(file.parent_path());		}	}	else if(pack.type == LobbyDelete::EType::SAVEGAME_FOLDER)	{		auto res = ResourcePath("Saves/" + pack.name, EResType::DIRECTORY);		auto name = CResourceHandler::get()->getResourceName(res);		if (!name)		{			logGlobal->error("Failed to find folder with name '%s'", res.getOriginalName());			return;		}		boost::system::error_code ec;		auto folder = boost::filesystem::canonical(*name);		if (ec)		{			logGlobal->error("Failed to delete folder '%s'. Reason: %s", res.getOriginalName(), ec.message());		}		else		{			boost::filesystem::remove_all(folder);		}	}	LobbyUpdateState lus;	lus.state = srv;	lus.refreshList = true;	srv.announcePack(lus);}
 |