mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Moved management of turn order into a new class
This commit is contained in:
		| @@ -29,7 +29,7 @@ public: | ||||
| 	using ExecHandler = Sub::ExecHandler; | ||||
|  | ||||
| 	static Sub * getRegistry(); | ||||
| 	static void defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player); | ||||
| 	static void defaultExecute(const EventBus * bus, PlayerColor & player); | ||||
|  | ||||
| 	virtual PlayerColor getPlayer() const = 0; | ||||
| 	virtual void setPlayer(const PlayerColor & value) = 0; | ||||
|   | ||||
| @@ -24,11 +24,11 @@ SubscriptionRegistry<PlayerGotTurn> * PlayerGotTurn::getRegistry() | ||||
| 	return Instance.get(); | ||||
| } | ||||
|  | ||||
| void PlayerGotTurn::defaultExecute(const EventBus * bus, const ExecHandler & execHandler, PlayerColor & player) | ||||
| void PlayerGotTurn::defaultExecute(const EventBus * bus, PlayerColor & player) | ||||
| { | ||||
| 	CPlayerGotTurn event; | ||||
| 	event.setPlayer(player); | ||||
| 	bus->executeEvent(event, execHandler); | ||||
| 	bus->executeEvent(event); | ||||
| 	player = event.getPlayer(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
| #include "battles/BattleProcessor.h" | ||||
| #include "processors/HeroPoolProcessor.h" | ||||
| #include "processors/PlayerMessageProcessor.h" | ||||
| #include "processors/TurnOrderProcessor.h" | ||||
| #include "queries/QueriesProcessor.h" | ||||
| #include "queries/MapQueries.h" | ||||
|  | ||||
| @@ -92,7 +93,7 @@ public: | ||||
| 		T *ptr = static_cast<T*>(pack); | ||||
| 		try | ||||
| 		{ | ||||
| 			ApplyGhNetPackVisitor applier(*gh, *gs); | ||||
| 			ApplyGhNetPackVisitor applier(*gh); | ||||
|  | ||||
| 			ptr->visit(applier); | ||||
|  | ||||
| @@ -123,51 +124,6 @@ static inline double distance(int3 a, int3 b) | ||||
| 	return std::sqrt((double)(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)); | ||||
| } | ||||
|  | ||||
| PlayerStatus PlayerStatuses::operator[](PlayerColor player) | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> l(mx); | ||||
| 	if (players.find(player) != players.end()) | ||||
| 	{ | ||||
| 		return players.at(player); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		throw std::runtime_error("No such player!"); | ||||
| 	} | ||||
| } | ||||
| void PlayerStatuses::addPlayer(PlayerColor player) | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> l(mx); | ||||
| 	players[player]; | ||||
| } | ||||
|  | ||||
| bool PlayerStatuses::checkFlag(PlayerColor player, bool PlayerStatus::*flag) | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> l(mx); | ||||
| 	if (players.find(player) != players.end()) | ||||
| 	{ | ||||
| 		return players[player].*flag; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		throw std::runtime_error("No such player!"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void PlayerStatuses::setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val) | ||||
| { | ||||
| 	boost::unique_lock<boost::mutex> l(mx); | ||||
| 	if (players.find(player) != players.end()) | ||||
| 	{ | ||||
| 		players[player].*flag = val; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		throw std::runtime_error("No such player!"); | ||||
| 	} | ||||
| 	cv.notify_all(); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| void callWith(std::vector<T> args, std::function<void(T)> fun, ui32 which) | ||||
| { | ||||
| @@ -480,7 +436,7 @@ void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill wh | ||||
|  | ||||
| void CGameHandler::handleClientDisconnection(std::shared_ptr<CConnection> c) | ||||
| { | ||||
| 	if(lobby->state == EServerState::SHUTDOWN || !gs || !gs->scenarioOps) | ||||
| 	if(lobby->getState() == EServerState::SHUTDOWN || !gs || !gs->scenarioOps) | ||||
| 		return; | ||||
| 	 | ||||
| 	for(auto & playerConnections : connections) | ||||
| @@ -546,6 +502,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby) | ||||
| 	: lobby(lobby) | ||||
| 	, heroPool(std::make_unique<HeroPoolProcessor>(this)) | ||||
| 	, battles(std::make_unique<BattleProcessor>(this)) | ||||
| 	, turnOrder(std::make_unique<TurnOrderProcessor>(this)) | ||||
| 	, queries(std::make_unique<QueriesProcessor>()) | ||||
| 	, playerMessages(std::make_unique<PlayerMessageProcessor>(this)) | ||||
| 	, complainNoCreatures("No creatures to split") | ||||
| @@ -592,9 +549,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack | ||||
| 	getRandomGenerator().resetSeed(); | ||||
|  | ||||
| 	for (auto & elem : gs->players) | ||||
| 	{ | ||||
| 		states.addPlayer(elem.first); | ||||
| 	} | ||||
| 		turnOrder->addPlayer(elem.first); | ||||
|  | ||||
| 	reinitScripting(); | ||||
| } | ||||
| @@ -646,7 +601,18 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa | ||||
| 		} | ||||
| } | ||||
|  | ||||
| void CGameHandler::newTurn() | ||||
| void CGameHandler::onPlayerTurnStarted(PlayerColor which) | ||||
| { | ||||
| 	events::PlayerGotTurn::defaultExecute(serverEventBus.get(), which); | ||||
| 	turnTimerHandler.onPlayerGetTurn(gs->players[which]); | ||||
| } | ||||
|  | ||||
| void CGameHandler::onPlayerTurnEnded(PlayerColor which) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CGameHandler::onNewTurn() | ||||
| { | ||||
| 	logGlobal->trace("Turn %d", gs->day+1); | ||||
| 	NewTurn n; | ||||
| @@ -965,6 +931,7 @@ void CGameHandler::newTurn() | ||||
|  | ||||
| 	synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that | ||||
| } | ||||
|  | ||||
| void CGameHandler::run(bool resume) | ||||
| { | ||||
| 	LOG_TRACE_PARAMS(logGlobal, "resume=%d", resume); | ||||
| @@ -989,114 +956,31 @@ void CGameHandler::run(bool resume) | ||||
| 	services()->scripts()->run(serverScripts); | ||||
| #endif | ||||
|  | ||||
| 	if(resume) | ||||
| 	if (!resume) | ||||
| 	{ | ||||
| 		onNewTurn(); | ||||
| 		events::TurnStarted::defaultExecute(serverEventBus.get()); | ||||
| 		for(auto & player : gs->players) | ||||
| 			turnTimerHandler.onGameplayStart(player.second); | ||||
| 	} | ||||
| 	else | ||||
| 		events::GameResumed::defaultExecute(serverEventBus.get()); | ||||
|  | ||||
| 	auto playerTurnOrder = generatePlayerTurnOrder(); | ||||
| 	 | ||||
| 	if(!resume) | ||||
| 		for(auto & playerColor : playerTurnOrder) | ||||
| 			turnTimerHandler.onGameplayStart(gs->players[playerColor]); | ||||
| 	 | ||||
| 	while(lobby->state == EServerState::GAMEPLAY) | ||||
| 	turnOrder->onGameStarted(); | ||||
|  | ||||
| 	//wait till game is done | ||||
| 	while(lobby->getState() == EServerState::GAMEPLAY) | ||||
| 	{ | ||||
| 		if(!resume) | ||||
| 		{ | ||||
| 			newTurn(); | ||||
| 			events::TurnStarted::defaultExecute(serverEventBus.get()); | ||||
| 		} | ||||
| 		const int waitTime = 100; //ms | ||||
|  | ||||
| 		std::list<PlayerColor>::iterator it; | ||||
| 		if (resume) | ||||
| 		{ | ||||
| 			it = std::find(playerTurnOrder.begin(), playerTurnOrder.end(), gs->currentPlayer); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			it = playerTurnOrder.begin(); | ||||
| 		} | ||||
| 		turnTimerHandler.onPlayerMakingTurn(gs->players[gs->getCurrentPlayer()], waitTime); | ||||
| 		if(gs->curB) | ||||
| 			turnTimerHandler.onBattleLoop(waitTime); | ||||
|  | ||||
| 		resume = false; | ||||
| 		for (; (it != playerTurnOrder.end()) && (lobby->state == EServerState::GAMEPLAY) ; it++) | ||||
| 		{ | ||||
| 			auto playerColor = *it; | ||||
|  | ||||
| 			auto onGetTurn = [&](events::PlayerGotTurn & event) | ||||
| 			{ | ||||
| 				//if player runs out of time, he shouldn't get the turn (especially AI) | ||||
| 				//pre-trigger may change anything, should check before each player | ||||
| 				//TODO: is it enough to check only one player? | ||||
| 				checkVictoryLossConditionsForAll(); | ||||
|  | ||||
| 				auto player = event.getPlayer(); | ||||
|  | ||||
| 				const PlayerState * playerState = &gs->players[player]; | ||||
|  | ||||
| 				if(playerState->status != EPlayerStatus::INGAME) | ||||
| 				{ | ||||
| 					event.setPlayer(PlayerColor::CANNOT_DETERMINE); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					states.setFlag(player, &PlayerStatus::makingTurn, true); | ||||
|  | ||||
| 					YourTurn yt; | ||||
| 					yt.player = player; | ||||
| 					//Change local daysWithoutCastle counter for local interface message //TODO: needed? | ||||
| 					yt.daysWithoutCastle = playerState->daysWithoutCastle; | ||||
| 					applyAndSend(&yt); | ||||
| 					 | ||||
| 					turnTimerHandler.onPlayerGetTurn(gs->players[player]); | ||||
| 				} | ||||
| 			}; | ||||
|  | ||||
| 			events::PlayerGotTurn::defaultExecute(serverEventBus.get(), onGetTurn, playerColor); | ||||
|  | ||||
| 			if(playerColor != PlayerColor::CANNOT_DETERMINE) | ||||
| 			{ | ||||
| 				//wait till turn is done | ||||
| 				const int waitTime = 100; //ms | ||||
| 				boost::unique_lock<boost::mutex> lock(states.mx); | ||||
| 				while(states.players.at(playerColor).makingTurn && lobby->state == EServerState::GAMEPLAY) | ||||
| 				{ | ||||
| 					turnTimerHandler.onPlayerMakingTurn(gs->players[playerColor], waitTime); | ||||
| 					if(gs->curB) | ||||
| 						turnTimerHandler.onBattleLoop(waitTime); | ||||
|  | ||||
| 					states.cv.wait_for(lock, boost::chrono::milliseconds(waitTime)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		//additional check that game is not finished | ||||
| 		bool activePlayer = false; | ||||
| 		for (auto player : playerTurnOrder) | ||||
| 		{ | ||||
| 			if (gs->players[player].status == EPlayerStatus::INGAME) | ||||
| 					activePlayer = true; | ||||
| 		} | ||||
| 		if(!activePlayer) | ||||
| 			lobby->state = EServerState::GAMEPLAY_ENDED; | ||||
| 		boost::this_thread::sleep_for(boost::chrono::milliseconds(waitTime)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::list<PlayerColor> CGameHandler::generatePlayerTurnOrder() const | ||||
| { | ||||
| 	// Generate player turn order | ||||
| 	std::list<PlayerColor> playerTurnOrder; | ||||
|  | ||||
| 	for (const auto & player : gs->players) // add human players first | ||||
| 	{ | ||||
| 		if (player.second.human) | ||||
| 			playerTurnOrder.push_back(player.first); | ||||
| 	} | ||||
| 	for (const auto & player : gs->players) // then add non-human players | ||||
| 	{ | ||||
| 		if (!player.second.human) | ||||
| 			playerTurnOrder.push_back(player.first); | ||||
| 	} | ||||
| 	return playerTurnOrder; | ||||
| } | ||||
|  | ||||
| void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h) | ||||
| { | ||||
| 	if (!h->hasSpellbook()) | ||||
| @@ -3617,7 +3501,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player) | ||||
|  | ||||
| 			if(p->human) | ||||
| 			{ | ||||
| 				lobby->state = EServerState::GAMEPLAY_ENDED; | ||||
| 				lobby->setState(EServerState::GAMEPLAY_ENDED); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| @@ -3662,12 +3546,11 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player) | ||||
| 		} | ||||
|  | ||||
| 		auto playerInfo = getPlayerState(gs->currentPlayer, false); | ||||
|  | ||||
| 		// If we are called before the actual game start, there might be no current player | ||||
| 		// If player making turn has lost his turn must be over as well | ||||
| 		if (playerInfo && playerInfo->status != EPlayerStatus::INGAME) | ||||
| 		{ | ||||
| 			// If player making turn has lost his turn must be over as well | ||||
| 			states.setFlag(gs->currentPlayer, &PlayerStatus::makingTurn, false); | ||||
| 		} | ||||
| 			turnOrder->onPlayerEndsTurn(gs->currentPlayer); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -4229,15 +4112,6 @@ void CGameHandler::createObject(const int3 & visitablePosition, Obj type, int32_ | ||||
| 	sendAndApply(&no); | ||||
| } | ||||
|  | ||||
| void CGameHandler::deserializationFix() | ||||
| { | ||||
| 	//FIXME: pointer to GameHandler itself can't be deserialized at the moment since GameHandler is top-level entity in serialization | ||||
| 	// restore any places that requires such pointer manually | ||||
| 	heroPool->gameHandler = this; | ||||
| 	battles->setGameHandler(this); | ||||
| 	playerMessages->gameHandler = this; | ||||
| } | ||||
|  | ||||
| void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, const CGTownInstance *town) | ||||
| { | ||||
| 	battles->startBattlePrimary(army1, army2, tile, hero1, hero2, creatureBank, town); | ||||
|   | ||||
| @@ -49,36 +49,10 @@ class CVCMIServer; | ||||
| class CBaseForGHApply; | ||||
| class PlayerMessageProcessor; | ||||
| class BattleProcessor; | ||||
| class TurnOrderProcessor; | ||||
| class QueriesProcessor; | ||||
| class CObjectVisitQuery; | ||||
|  | ||||
| struct PlayerStatus | ||||
| { | ||||
| 	bool makingTurn; | ||||
|  | ||||
| 	PlayerStatus():makingTurn(false){}; | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & makingTurn; | ||||
| 	} | ||||
| }; | ||||
| class PlayerStatuses | ||||
| { | ||||
| public: | ||||
| 	std::map<PlayerColor,PlayerStatus> players; | ||||
| 	boost::mutex mx; | ||||
| 	boost::condition_variable cv; //notifies when any changes are made | ||||
|  | ||||
| 	void addPlayer(PlayerColor player); | ||||
| 	PlayerStatus operator[](PlayerColor player); | ||||
| 	bool checkFlag(PlayerColor player, bool PlayerStatus::*flag); | ||||
| 	void setFlag(PlayerColor player, bool PlayerStatus::*flag, bool val); | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & players; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class CGameHandler : public IGameCallback, public CBattleInfoCallback, public Environment | ||||
| { | ||||
| 	CVCMIServer * lobby; | ||||
| @@ -90,6 +64,7 @@ public: | ||||
| 	std::unique_ptr<HeroPoolProcessor> heroPool; | ||||
| 	std::unique_ptr<BattleProcessor> battles; | ||||
| 	std::unique_ptr<QueriesProcessor> queries; | ||||
| 	std::unique_ptr<TurnOrderProcessor> turnOrder; | ||||
|  | ||||
| 	//use enums as parameters, because doMove(sth, true, false, true) is not readable | ||||
| 	enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS}; | ||||
| @@ -99,13 +74,11 @@ public: | ||||
| 	std::unique_ptr<PlayerMessageProcessor> playerMessages; | ||||
|  | ||||
| 	std::map<PlayerColor, std::set<std::shared_ptr<CConnection>>> connections; //player color -> connection to client with interface of that player | ||||
| 	PlayerStatuses states; //player color -> player state | ||||
|  | ||||
| 	//queries stuff | ||||
| 	boost::recursive_mutex gsm; | ||||
| 	ui32 QID; | ||||
|  | ||||
|  | ||||
| 	SpellCastEnvironment * spellEnv; | ||||
| 	 | ||||
| 	TurnTimerHandler turnTimerHandler; | ||||
| @@ -237,6 +210,10 @@ public: | ||||
| 	void save(const std::string &fname); | ||||
| 	bool load(const std::string &fname); | ||||
|  | ||||
| 	void onPlayerTurnStarted(PlayerColor which); | ||||
| 	void onPlayerTurnEnded(PlayerColor which); | ||||
| 	void onNewTurn(); | ||||
|  | ||||
| 	void handleTimeEvents(); | ||||
| 	void handleTownEvents(CGTownInstance *town, NewTurn &n); | ||||
| 	bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true | ||||
| @@ -248,14 +225,11 @@ public: | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & QID; | ||||
| 		h & states; | ||||
| 		h & battles; | ||||
| 		h & heroPool; | ||||
| 		h & getRandomGenerator(); | ||||
| 		h & playerMessages; | ||||
|  | ||||
| 		if (!h.saving) | ||||
| 			deserializationFix(); | ||||
| 		h & *battles; | ||||
| 		h & *heroPool; | ||||
| 		h & *playerMessages; | ||||
| 		h & *turnOrder; | ||||
|  | ||||
| #if SCRIPTING_ENABLED | ||||
| 		JsonNode scriptsState; | ||||
| @@ -282,7 +256,6 @@ public: | ||||
| 	bool isPlayerOwns(CPackForServer * pack, ObjectInstanceID id); | ||||
|  | ||||
| 	void run(bool resume); | ||||
| 	void newTurn(); | ||||
| 	bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactPosition> & slot); | ||||
| 	void spawnWanderingMonsters(CreatureID creatureID); | ||||
|  | ||||
| @@ -298,8 +271,6 @@ public: | ||||
| 	scripting::Pool * getContextPool() const override; | ||||
| #endif | ||||
|  | ||||
| 	std::list<PlayerColor> generatePlayerTurnOrder() const; | ||||
|  | ||||
| 	friend class CVCMIServer; | ||||
| private: | ||||
| 	std::unique_ptr<events::EventBus> serverEventBus; | ||||
| @@ -308,7 +279,6 @@ private: | ||||
| #endif | ||||
|  | ||||
| 	void reinitScripting(); | ||||
| 	void deserializationFix(); | ||||
|  | ||||
| 	void getVictoryLossMessage(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult, InfoWindow & out) const; | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ set(server_SRCS | ||||
|  | ||||
| 		processors/HeroPoolProcessor.cpp | ||||
| 		processors/PlayerMessageProcessor.cpp | ||||
| 		processors/TurnOrderProcessor.cpp | ||||
|  | ||||
| 		CGameHandler.cpp | ||||
| 		ServerSpellCastEnvironment.cpp | ||||
| @@ -37,6 +38,7 @@ set(server_HEADERS | ||||
|  | ||||
| 		processors/HeroPoolProcessor.h | ||||
| 		processors/PlayerMessageProcessor.h | ||||
| 		processors/TurnOrderProcessor.h | ||||
|  | ||||
| 		CGameHandler.h | ||||
| 		ServerSpellCastEnvironment.h | ||||
|   | ||||
| @@ -158,6 +158,16 @@ CVCMIServer::~CVCMIServer() | ||||
| 		announceLobbyThread->join(); | ||||
| } | ||||
|  | ||||
| void CVCMIServer::setState(EServerState value) | ||||
| { | ||||
| 	state.store(value); | ||||
| } | ||||
|  | ||||
| EServerState CVCMIServer::getState() const | ||||
| { | ||||
| 	return state.load(); | ||||
| } | ||||
|  | ||||
| void CVCMIServer::run() | ||||
| { | ||||
| 	if(!restartGameplay) | ||||
| @@ -1159,7 +1169,7 @@ int main(int argc, const char * argv[]) | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			while(server.state != EServerState::SHUTDOWN) | ||||
| 			while(server.getState() != EServerState::SHUTDOWN) | ||||
| 			{ | ||||
| 				server.run(); | ||||
| 			} | ||||
| @@ -1168,7 +1178,7 @@ int main(int argc, const char * argv[]) | ||||
| 		catch(boost::system::system_error & e) //for boost errors just log, not crash - probably client shut down connection | ||||
| 		{ | ||||
| 			logNetwork->error(e.what()); | ||||
| 			server.state = EServerState::SHUTDOWN; | ||||
| 			server.setState(EServerState::SHUTDOWN); | ||||
| 		} | ||||
| 	} | ||||
| 	catch(boost::system::system_error & e) | ||||
|   | ||||
| @@ -56,10 +56,10 @@ class CVCMIServer : public LobbyInfo | ||||
| 	boost::recursive_mutex mx; | ||||
| 	std::shared_ptr<CApplier<CBaseForServerApply>> applier; | ||||
| 	std::unique_ptr<boost::thread> announceLobbyThread, remoteConnectionsThread; | ||||
| 	std::atomic<EServerState> state; | ||||
|  | ||||
| public: | ||||
| 	std::shared_ptr<CGameHandler> gh; | ||||
| 	std::atomic<EServerState> state; | ||||
| 	ui16 port; | ||||
|  | ||||
| 	boost::program_options::variables_map cmdLineOptions; | ||||
| @@ -101,6 +101,9 @@ public: | ||||
|  | ||||
| 	void updateAndPropagateLobbyState(); | ||||
|  | ||||
| 	void setState(EServerState value); | ||||
| 	EServerState getState() const; | ||||
|  | ||||
| 	// Work with LobbyInfo | ||||
| 	void setPlayer(PlayerColor clickedColor); | ||||
| 	void optionNextHero(PlayerColor player, int dir); //dir == -1 or +1 | ||||
|   | ||||
| @@ -53,11 +53,11 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyClientConnected(LobbyClie | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(srv.state == EServerState::LOBBY) | ||||
| 		{ | ||||
| 	if(srv.getState() == EServerState::LOBBY) | ||||
| 	{ | ||||
| 		result = true; | ||||
| 		return; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	//disconnect immediately and ignore this client | ||||
| 	srv.connections.erase(pack.c); | ||||
| @@ -115,7 +115,7 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientConnected(LobbyCl | ||||
| 	// Until UUID set we only pass LobbyClientConnected to this client | ||||
| 	pack.c->uuid = pack.uuid; | ||||
| 	srv.updateAndPropagateLobbyState(); | ||||
| 	if(srv.state == EServerState::GAMEPLAY) | ||||
| 	if(srv.getState() == EServerState::GAMEPLAY) | ||||
| 	{ | ||||
| 		//immediately start game | ||||
| 		std::unique_ptr<LobbyStartGame> startGameForReconnectedPlayer(new LobbyStartGame); | ||||
| @@ -173,13 +173,13 @@ void ApplyOnServerAfterAnnounceNetPackVisitor::visitLobbyClientDisconnected(Lobb | ||||
| 	if(pack.shutdownServer) | ||||
| 	{ | ||||
| 		logNetwork->info("Client requested shutdown, server will close itself..."); | ||||
| 		srv.state = EServerState::SHUTDOWN; | ||||
| 		srv.setState(EServerState::SHUTDOWN); | ||||
| 		return; | ||||
| 	} | ||||
| 	else if(srv.connections.empty()) | ||||
| 	{ | ||||
| 		logNetwork->error("Last connection lost, server will close itself..."); | ||||
| 		srv.state = EServerState::SHUTDOWN; | ||||
| 		srv.setState(EServerState::SHUTDOWN); | ||||
| 	} | ||||
| 	else if(pack.c == srv.hostClient) | ||||
| 	{ | ||||
| @@ -198,7 +198,7 @@ void ClientPermissionsCheckerNetPackVisitor::visitLobbyChatMessage(LobbyChatMess | ||||
|  | ||||
| void ApplyOnServerNetPackVisitor::visitLobbySetMap(LobbySetMap & pack) | ||||
| { | ||||
| 	if(srv.state != EServerState::LOBBY) | ||||
| 	if(srv.getState() != EServerState::LOBBY) | ||||
| 	{ | ||||
| 		result = false; | ||||
| 		return; | ||||
| @@ -300,7 +300,7 @@ void ApplyOnServerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack) | ||||
| 	pack.initializedStartInfo = std::make_shared<StartInfo>(*srv.gh->getStartInfo(true)); | ||||
| 	pack.initializedGameState = srv.gh->gameState(); | ||||
|  | ||||
| 	srv.state = EServerState::GAMEPLAY_STARTING; | ||||
| 	srv.setState(EServerState::GAMEPLAY_STARTING); | ||||
| 	result = true; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include "battles/BattleProcessor.h" | ||||
| #include "processors/HeroPoolProcessor.h" | ||||
| #include "processors/PlayerMessageProcessor.h" | ||||
| #include "processors/TurnOrderProcessor.h" | ||||
| #include "queries/QueriesProcessor.h" | ||||
|  | ||||
| #include "../lib/IGameCallback.h" | ||||
| @@ -36,24 +37,10 @@ void ApplyGhNetPackVisitor::visitSaveGame(SaveGame & pack) | ||||
|  | ||||
| void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack) | ||||
| { | ||||
| 	PlayerColor currentPlayer = gs.currentPlayer; | ||||
| 	if(pack.player != currentPlayer) | ||||
| 	{ | ||||
| 		if(gh.getPlayerStatus(pack.player) == EPlayerStatus::INGAME) | ||||
| 			gh.throwAndComplain(&pack, "pack.player attempted to end turn for another pack.player!"); | ||||
| 	if (!gh.hasPlayerAt(pack.player, pack.c)) | ||||
| 		gh.throwAndComplain(&pack, "No such pack.player!"); | ||||
|  | ||||
| 		logGlobal->debug("pack.player attempted to end turn after game over. Ignoring this request."); | ||||
|  | ||||
| 		result = true; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	gh.throwOnWrongPlayer(&pack, pack.player); | ||||
| 	if(gh.queries->topQuery(pack.player)) | ||||
| 		gh.throwAndComplain(&pack, "Cannot end turn before resolving queries!"); | ||||
|  | ||||
| 	gh.states.setFlag(gs.currentPlayer, &PlayerStatus::makingTurn, false); | ||||
| 	result = true; | ||||
| 	result = gh.turnOrder->onPlayerEndsTurn(pack.player); | ||||
| } | ||||
|  | ||||
| void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack) | ||||
| @@ -274,8 +261,6 @@ void ApplyGhNetPackVisitor::visitQueryReply(QueryReply & pack) | ||||
| 	if(pack.qid == QueryID(-1)) | ||||
| 		gh.throwAndComplain(&pack, "Cannot answer the query with pack.id -1!"); | ||||
|  | ||||
| 	assert(vstd::contains(gh.states.players, pack.player)); | ||||
|  | ||||
| 	result = gh.queryReply(pack.qid, pack.reply, pack.player); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,11 +16,10 @@ class ApplyGhNetPackVisitor : public VCMI_LIB_WRAP_NAMESPACE(ICPackVisitor) | ||||
| private: | ||||
| 	bool result; | ||||
| 	CGameHandler & gh; | ||||
| 	CGameState & gs; | ||||
|  | ||||
| public: | ||||
| 	ApplyGhNetPackVisitor(CGameHandler & gh, CGameState & gs) | ||||
| 		:gh(gh), gs(gs), result(false) | ||||
| 	ApplyGhNetPackVisitor(CGameHandler & gh) | ||||
| 		:gh(gh), result(false) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include "CGameHandler.h" | ||||
| #include "battles/BattleProcessor.h" | ||||
| #include "queries/QueriesProcessor.h" | ||||
| #include "processors/TurnOrderProcessor.h" | ||||
| #include "../lib/battle/BattleInfo.h" | ||||
| #include "../lib/gameState/CGameState.h" | ||||
| #include "../lib/CPlayerState.h" | ||||
| @@ -83,8 +84,8 @@ void TurnTimerHandler::onPlayerMakingTurn(PlayerState & state, int waitTime) | ||||
| 			state.turnTimer.baseTimer = 0; | ||||
| 			onPlayerMakingTurn(state, waitTime); | ||||
| 		} | ||||
| 		else if(!gameHandler.queries->topQuery(state.color)) //wait for replies to avoid pending queries | ||||
| 			gameHandler.states.players.at(state.color).makingTurn = false; //force end turn | ||||
| 		else if(!gameHandler.queries->topQuery(state.color)) | ||||
| 			gameHandler.turnOrder->onPlayerEndsTurn(state.color); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -286,7 +286,6 @@ const CStack * BattleFlowProcessor::getNextStack() | ||||
|  | ||||
| void BattleFlowProcessor::activateNextStack() | ||||
| { | ||||
| 	//TODO: activate next round if next == nullptr | ||||
| 	const auto & curB = *gameHandler->gameState()->curB; | ||||
|  | ||||
| 	// Find next stack that requires manual control | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| #include "StdInc.h" | ||||
| #include "HeroPoolProcessor.h" | ||||
|  | ||||
| #include "TurnOrderProcessor.h" | ||||
| #include "../CGameHandler.h" | ||||
|  | ||||
| #include "../../lib/CHeroHandler.h" | ||||
| @@ -32,29 +33,6 @@ HeroPoolProcessor::HeroPoolProcessor(CGameHandler * gameHandler) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool HeroPoolProcessor::playerEndedTurn(const PlayerColor & player) | ||||
| { | ||||
| 	// our player is acting right now and have not ended turn | ||||
| 	if (player == gameHandler->gameState()->currentPlayer) | ||||
| 		return false; | ||||
|  | ||||
| 	auto turnOrder = gameHandler->generatePlayerTurnOrder(); | ||||
|  | ||||
| 	for (auto const & entry : turnOrder) | ||||
| 	{ | ||||
| 		// our player is yet to start turn | ||||
| 		if (entry == gameHandler->gameState()->currentPlayer) | ||||
| 			return false; | ||||
|  | ||||
| 		// our player have finished turn | ||||
| 		if (entry == player) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	assert(false); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID) | ||||
| { | ||||
| 	const auto & heroesPool = gameHandler->gameState()->heroesPool; | ||||
| @@ -90,7 +68,7 @@ TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, | ||||
| void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHeroInstance * hero) | ||||
| { | ||||
| 	SetAvailableHero sah; | ||||
| 	if (playerEndedTurn(color)) | ||||
| 	if (gameHandler->turnOrder->playerAwaitsNewDay(color)) | ||||
| 		sah.roleID = TavernSlotRole::SURRENDERED_TODAY; | ||||
| 	else | ||||
| 		sah.roleID = TavernSlotRole::SURRENDERED; | ||||
| @@ -104,7 +82,7 @@ void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHer | ||||
| void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroInstance * hero) | ||||
| { | ||||
| 	SetAvailableHero sah; | ||||
| 	if (playerEndedTurn(color)) | ||||
| 	if (gameHandler->turnOrder->playerAwaitsNewDay(color)) | ||||
| 		sah.roleID = TavernSlotRole::RETREATED_TODAY; | ||||
| 	else | ||||
| 		sah.roleID = TavernSlotRole::RETREATED; | ||||
|   | ||||
| @@ -43,7 +43,6 @@ class HeroPoolProcessor : boost::noncopyable | ||||
|  | ||||
| 	TavernHeroSlot selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID); | ||||
|  | ||||
| 	bool playerEndedTurn(const PlayerColor & player); | ||||
| public: | ||||
| 	CGameHandler * gameHandler; | ||||
|  | ||||
|   | ||||
| @@ -71,7 +71,7 @@ bool PlayerMessageProcessor::handleHostCommand(PlayerColor player, const std::st | ||||
| 	if(words[1] == "exit" || words[1] == "quit" || words[1] == "end") | ||||
| 	{ | ||||
| 		broadcastSystemMessage("game was terminated"); | ||||
| 		gameHandler->gameLobby()->state = EServerState::SHUTDOWN; | ||||
| 		gameHandler->gameLobby()->setState(EServerState::SHUTDOWN); | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										183
									
								
								server/processors/TurnOrderProcessor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								server/processors/TurnOrderProcessor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | ||||
| /* | ||||
|  * TurnOrderProcessor.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 "TurnOrderProcessor.h" | ||||
|  | ||||
| #include "../queries/QueriesProcessor.h" | ||||
| #include "../CGameHandler.h" | ||||
| #include "../CVCMIServer.h" | ||||
|  | ||||
| #include "../../lib/CPlayerState.h" | ||||
| #include "../../lib/NetPacks.h" | ||||
|  | ||||
| TurnOrderProcessor::TurnOrderProcessor(CGameHandler * owner): | ||||
| 	gameHandler(owner) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::canActSimultaneously(PlayerColor active, PlayerColor waiting) const | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::mustActBefore(PlayerColor left, PlayerColor right) const | ||||
| { | ||||
| 	const auto * leftInfo = gameHandler->getPlayerState(left, false); | ||||
| 	const auto * rightInfo = gameHandler->getPlayerState(right, false); | ||||
|  | ||||
| 	assert(left != right); | ||||
| 	assert(leftInfo && rightInfo); | ||||
|  | ||||
| 	if (!leftInfo) | ||||
| 		return false; | ||||
| 	if (!rightInfo) | ||||
| 		return true; | ||||
|  | ||||
| 	if (leftInfo->isHuman() && !rightInfo->isHuman()) | ||||
| 		return true; | ||||
|  | ||||
| 	if (!leftInfo->isHuman() && rightInfo->isHuman()) | ||||
| 		return false; | ||||
|  | ||||
| 	return left < right; | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::canStartTurn(PlayerColor which) const | ||||
| { | ||||
| 	for (auto player : awaitingPlayers) | ||||
| 	{ | ||||
| 		if (mustActBefore(player, which)) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	for (auto player : actingPlayers) | ||||
| 	{ | ||||
| 		if (!canActSimultaneously(player, which)) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::doStartNewDay() | ||||
| { | ||||
| 	assert(awaitingPlayers.empty()); | ||||
| 	assert(actingPlayers.empty()); | ||||
|  | ||||
| 	bool activePlayer = false; | ||||
| 	for (auto player : actedPlayers) | ||||
| 	{ | ||||
| 		if (gameHandler->getPlayerState(player)->status == EPlayerStatus::INGAME) | ||||
| 			activePlayer = true; | ||||
| 	} | ||||
|  | ||||
| 	if(!activePlayer) | ||||
| 		gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED); | ||||
|  | ||||
| 	std::swap(actedPlayers, awaitingPlayers); | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which) | ||||
| { | ||||
| 	//if player runs out of time, he shouldn't get the turn (especially AI) | ||||
| 	//pre-trigger may change anything, should check before each player | ||||
| 	//TODO: is it enough to check only one player? | ||||
| 	gameHandler->checkVictoryLossConditionsForAll(); | ||||
|  | ||||
| 	assert(gameHandler->getPlayerState(which)); | ||||
| 	assert(gameHandler->getPlayerState(which)->status == EPlayerStatus::INGAME); | ||||
|  | ||||
| 	gameHandler->onPlayerTurnStarted(which); | ||||
|  | ||||
| 	YourTurn yt; | ||||
| 	yt.player = which; | ||||
| 	//Change local daysWithoutCastle counter for local interface message //TODO: needed? | ||||
| 	yt.daysWithoutCastle = gameHandler->getPlayerState(which)->daysWithoutCastle; | ||||
| 	gameHandler->sendAndApply(&yt); | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which) | ||||
| { | ||||
| 	assert(playerMakingTurn(which)); | ||||
|  | ||||
| 	actingPlayers.erase(which); | ||||
| 	actedPlayers.insert(which); | ||||
|  | ||||
| 	if (!awaitingPlayers.empty()) | ||||
| 		tryStartTurnsForPlayers(); | ||||
|  | ||||
| 	if (actingPlayers.empty()) | ||||
| 		doStartNewDay(); | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::addPlayer(PlayerColor which) | ||||
| { | ||||
| 	awaitingPlayers.insert(which); | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which) | ||||
| { | ||||
| 	if (!playerMakingTurn(which)) | ||||
| 	{ | ||||
| 		gameHandler->complain("Can not end turn for player that is not acting!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if(gameHandler->getPlayerStatus(which) != EPlayerStatus::INGAME) | ||||
| 	{ | ||||
| 		gameHandler->complain("Can not end turn for player that is not in game!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if(gameHandler->queries->topQuery(which) != nullptr) | ||||
| 	{ | ||||
| 		gameHandler->complain("Cannot end turn before resolving queries!"); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	doEndPlayerTurn(which); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::onGameStarted() | ||||
| { | ||||
| 	tryStartTurnsForPlayers(); | ||||
|  | ||||
| 	// this may be game load - send notification to players that they can act | ||||
| 	auto actingPlayersCopy = actingPlayers; | ||||
| 	for (auto player : actingPlayersCopy) | ||||
| 		doStartPlayerTurn(player); | ||||
| } | ||||
|  | ||||
| void TurnOrderProcessor::tryStartTurnsForPlayers() | ||||
| { | ||||
| 	auto awaitingPlayersCopy = awaitingPlayers; | ||||
| 	for (auto player : awaitingPlayersCopy) | ||||
| 	{ | ||||
| 		if (canStartTurn(player)) | ||||
| 			doStartPlayerTurn(player); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::playerAwaitsTurn(PlayerColor which) const | ||||
| { | ||||
| 	return vstd::contains(awaitingPlayers, which); | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::playerMakingTurn(PlayerColor which) const | ||||
| { | ||||
| 	return vstd::contains(actingPlayers, which); | ||||
| } | ||||
|  | ||||
| bool TurnOrderProcessor::playerAwaitsNewDay(PlayerColor which) const | ||||
| { | ||||
| 	return vstd::contains(actedPlayers, which); | ||||
| } | ||||
							
								
								
									
										67
									
								
								server/processors/TurnOrderProcessor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								server/processors/TurnOrderProcessor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * TurnOrderProcessor.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 | ||||
|  | ||||
| #include "../../lib/GameConstants.h" | ||||
|  | ||||
| class CGameHandler; | ||||
|  | ||||
| class TurnOrderProcessor : boost::noncopyable | ||||
| { | ||||
| 	CGameHandler * gameHandler; | ||||
|  | ||||
| 	std::set<PlayerColor> awaitingPlayers; | ||||
| 	std::set<PlayerColor> actingPlayers; | ||||
| 	std::set<PlayerColor> actedPlayers; | ||||
|  | ||||
| 	/// Returns true if waiting player can act alongside with currently acting player | ||||
| 	bool canActSimultaneously(PlayerColor active, PlayerColor waiting) const; | ||||
|  | ||||
| 	/// Returns true if left player must act before right player | ||||
| 	bool mustActBefore(PlayerColor left, PlayerColor right) const; | ||||
|  | ||||
| 	/// Returns true if player is ready to start turn | ||||
| 	bool canStartTurn(PlayerColor which) const; | ||||
|  | ||||
| 	/// Starts turn for all players that can start turn | ||||
| 	void tryStartTurnsForPlayers(); | ||||
|  | ||||
| 	void doStartNewDay(); | ||||
| 	void doStartPlayerTurn(PlayerColor which); | ||||
| 	void doEndPlayerTurn(PlayerColor which); | ||||
|  | ||||
| public: | ||||
| 	TurnOrderProcessor(CGameHandler * owner); | ||||
|  | ||||
| 	/// Add new player to handle (e.g. on game start) | ||||
| 	void addPlayer(PlayerColor which); | ||||
|  | ||||
| 	/// NetPack call-in | ||||
| 	bool onPlayerEndsTurn(PlayerColor which); | ||||
|  | ||||
| 	/// Start game (or resume from save) and send YourTurn pack to player(s) | ||||
| 	void onGameStarted(); | ||||
|  | ||||
| 	/// Returns true if player turn has not started today | ||||
| 	bool playerAwaitsTurn(PlayerColor which) const; | ||||
|  | ||||
| 	/// Returns true if player is currently making his turn | ||||
| 	bool playerMakingTurn(PlayerColor which) const; | ||||
|  | ||||
| 	/// Returns true if player has finished his turn and is waiting for new day | ||||
| 	bool playerAwaitsNewDay(PlayerColor which) const; | ||||
|  | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 	{ | ||||
| 		h & awaitingPlayers; | ||||
| 		h & actingPlayers; | ||||
| 		h & actedPlayers; | ||||
| 	} | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user