From d83aa828f63597b0afd07c925056edc927108822 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 23 Aug 2023 18:46:30 +0300 Subject: [PATCH] Fix turn ending --- AI/Nullkiller/Analyzers/ArmyManager.cpp | 3 +- include/vcmi/events/PlayerGotTurn.h | 2 +- lib/events/PlayerGotTurn.cpp | 3 +- server/CGameHandler.cpp | 5 ++-- server/NetPacksServer.cpp | 2 +- server/TurnTimerHandler.cpp | 2 +- server/processors/TurnOrderProcessor.cpp | 37 ++++++++++++++++-------- server/processors/TurnOrderProcessor.h | 11 +++++-- 8 files changed, 43 insertions(+), 22 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index 19f48ed65..11bdeecd5 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -225,7 +225,8 @@ std::vector ArmyManager::getBestArmy(const IBonusBearer * armyCarrier, if(weakest->count == 1) { - assert(resultingArmy.size() > 1); + if (resultingArmy.size() == 1) + logAi->warn("Unexpected resulting army size!"); resultingArmy.erase(weakest); } diff --git a/include/vcmi/events/PlayerGotTurn.h b/include/vcmi/events/PlayerGotTurn.h index 162300701..278e03c88 100644 --- a/include/vcmi/events/PlayerGotTurn.h +++ b/include/vcmi/events/PlayerGotTurn.h @@ -29,7 +29,7 @@ public: using ExecHandler = Sub::ExecHandler; static Sub * getRegistry(); - static void defaultExecute(const EventBus * bus, PlayerColor & player); + static void defaultExecute(const EventBus * bus, const PlayerColor & player); virtual PlayerColor getPlayer() const = 0; virtual void setPlayer(const PlayerColor & value) = 0; diff --git a/lib/events/PlayerGotTurn.cpp b/lib/events/PlayerGotTurn.cpp index e4a259e99..ccbffecc5 100644 --- a/lib/events/PlayerGotTurn.cpp +++ b/lib/events/PlayerGotTurn.cpp @@ -24,12 +24,11 @@ SubscriptionRegistry * PlayerGotTurn::getRegistry() return Instance.get(); } -void PlayerGotTurn::defaultExecute(const EventBus * bus, PlayerColor & player) +void PlayerGotTurn::defaultExecute(const EventBus * bus, const PlayerColor & player) { CPlayerGotTurn event; event.setPlayer(player); bus->executeEvent(event); - player = event.getPlayer(); } CPlayerGotTurn::CPlayerGotTurn() = default; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 3253d1cf4..4f2a51f48 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -609,7 +609,8 @@ void CGameHandler::onPlayerTurnStarted(PlayerColor which) void CGameHandler::onPlayerTurnEnded(PlayerColor which) { - + // 7 days without castle + checkVictoryLossConditionsForPlayer(which); } void CGameHandler::onNewTurn() @@ -3550,7 +3551,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player) // 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) - turnOrder->onPlayerEndsTurn(gs->currentPlayer); + turnOrder->onPlayerEndsTurn(gs->currentPlayer, PlayerTurnEndReason::GAME_END); } } diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index b49392edf..69f11d885 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -40,7 +40,7 @@ void ApplyGhNetPackVisitor::visitEndTurn(EndTurn & pack) if (!gh.hasPlayerAt(pack.player, pack.c)) gh.throwAndComplain(&pack, "No such pack.player!"); - result = gh.turnOrder->onPlayerEndsTurn(pack.player); + result = gh.turnOrder->onPlayerEndsTurn(pack.player, PlayerTurnEndReason::CLIENT_REQUEST); } void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack) diff --git a/server/TurnTimerHandler.cpp b/server/TurnTimerHandler.cpp index d2e568273..021ee59e1 100644 --- a/server/TurnTimerHandler.cpp +++ b/server/TurnTimerHandler.cpp @@ -85,7 +85,7 @@ void TurnTimerHandler::onPlayerMakingTurn(PlayerState & state, int waitTime) onPlayerMakingTurn(state, waitTime); } else if(!gameHandler.queries->topQuery(state.color)) - gameHandler.turnOrder->onPlayerEndsTurn(state.color); + gameHandler.turnOrder->onPlayerEndsTurn(state.color, PlayerTurnEndReason::TURN_TIMEOUT); } } diff --git a/server/processors/TurnOrderProcessor.cpp b/server/processors/TurnOrderProcessor.cpp index bd8b0a95e..5cbaea310 100644 --- a/server/processors/TurnOrderProcessor.cpp +++ b/server/processors/TurnOrderProcessor.cpp @@ -54,7 +54,7 @@ bool TurnOrderProcessor::canStartTurn(PlayerColor which) const { for (auto player : awaitingPlayers) { - if (mustActBefore(player, which)) + if (player != which && mustActBefore(player, which)) return false; } @@ -83,18 +83,19 @@ void TurnOrderProcessor::doStartNewDay() gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED); std::swap(actedPlayers, awaitingPlayers); + + gameHandler->onNewTurn(); + tryStartTurnsForPlayers(); } 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); + //Note: on game load, "actingPlayer" might already contain list of players + actingPlayers.insert(which); + awaitingPlayers.erase(which); gameHandler->onPlayerTurnStarted(which); YourTurn yt; @@ -102,20 +103,28 @@ void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which) //Change local daysWithoutCastle counter for local interface message //TODO: needed? yt.daysWithoutCastle = gameHandler->getPlayerState(which)->daysWithoutCastle; gameHandler->sendAndApply(&yt); + + assert(actingPlayers.size() == 1); // No simturns yet :( + assert(gameHandler->getCurrentPlayer() == *actingPlayers.begin()); } -void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which) +void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which, PlayerTurnEndReason reason) { assert(playerMakingTurn(which)); actingPlayers.erase(which); - actedPlayers.insert(which); + if (reason != PlayerTurnEndReason::GAME_END) + actedPlayers.insert(which); if (!awaitingPlayers.empty()) tryStartTurnsForPlayers(); if (actingPlayers.empty()) doStartNewDay(); + + assert(!actingPlayers.empty()); + assert(actingPlayers.size() == 1); // No simturns yet :( + assert(gameHandler->getCurrentPlayer() == *actingPlayers.begin()); } void TurnOrderProcessor::addPlayer(PlayerColor which) @@ -123,7 +132,7 @@ void TurnOrderProcessor::addPlayer(PlayerColor which) awaitingPlayers.insert(which); } -bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which) +bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which, PlayerTurnEndReason reason) { if (!playerMakingTurn(which)) { @@ -143,18 +152,22 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which) return false; } - doEndPlayerTurn(which); + if (reason != PlayerTurnEndReason::GAME_END) + gameHandler->onPlayerTurnEnded(which); + + doEndPlayerTurn(which, reason); + 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); + + tryStartTurnsForPlayers(); } void TurnOrderProcessor::tryStartTurnsForPlayers() diff --git a/server/processors/TurnOrderProcessor.h b/server/processors/TurnOrderProcessor.h index 69a95ec71..fd524d575 100644 --- a/server/processors/TurnOrderProcessor.h +++ b/server/processors/TurnOrderProcessor.h @@ -13,6 +13,13 @@ class CGameHandler; +enum class PlayerTurnEndReason +{ + CLIENT_REQUEST, // client requested end of turn (e.g. press End Turn button) + TURN_TIMEOUT, // Player's turn timer has run out + GAME_END // Player have won or lost the game +}; + class TurnOrderProcessor : boost::noncopyable { CGameHandler * gameHandler; @@ -35,7 +42,7 @@ class TurnOrderProcessor : boost::noncopyable void doStartNewDay(); void doStartPlayerTurn(PlayerColor which); - void doEndPlayerTurn(PlayerColor which); + void doEndPlayerTurn(PlayerColor which, PlayerTurnEndReason reason); public: TurnOrderProcessor(CGameHandler * owner); @@ -44,7 +51,7 @@ public: void addPlayer(PlayerColor which); /// NetPack call-in - bool onPlayerEndsTurn(PlayerColor which); + bool onPlayerEndsTurn(PlayerColor which, PlayerTurnEndReason reason); /// Start game (or resume from save) and send YourTurn pack to player(s) void onGameStarted();