1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-23 00:28:08 +02:00

Better handling of AI shutdown

This commit is contained in:
Ivan Savenko
2025-03-02 12:56:01 +00:00
parent 5f052165fe
commit 219eea86ff
6 changed files with 46 additions and 18 deletions

View File

@ -884,8 +884,16 @@ void AIGateway::makeTurn()
} }
#endif #endif
try
{
endTurn(); endTurn();
} }
catch (const TerminationRequestedException & e)
{
logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
return;
}
}
void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h) void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {

View File

@ -260,7 +260,7 @@ int CBattleCallback::sendRequest(const CPackForServer & request)
{ {
logGlobal->trace("We'll wait till request %d is answered.\n", requestID); logGlobal->trace("We'll wait till request %d is answered.\n", requestID);
auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting); auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting);
CClient::waitingRequest.waitWhileContains(requestID); cl->waitingRequest.waitWhileContains(requestID);
} }
return requestID; return requestID;
} }

View File

@ -653,6 +653,8 @@ void CServerHandler::showHighScoresAndEndGameplay(PlayerColor player, bool victo
void CServerHandler::endGameplay() void CServerHandler::endGameplay()
{ {
client->finishGameplay();
// Game is ending // Game is ending
// Tell the network thread to reach a stable state // Tell the network thread to reach a stable state
sendClientDisconnecting(); sendClientDisconnecting();
@ -671,6 +673,7 @@ void CServerHandler::endGameplay()
void CServerHandler::restartGameplay() void CServerHandler::restartGameplay()
{ {
client->finishGameplay();
client->endGame(); client->endGame();
client.reset(); client.reset();

View File

@ -46,8 +46,6 @@
#include "lib/CAndroidVMHelper.h" #include "lib/CAndroidVMHelper.h"
#endif #endif
ThreadSafeVector<int> CClient::waitingRequest;
CPlayerEnvironment::CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_) CPlayerEnvironment::CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_)
: player(player_), : player(player_),
cl(cl_), cl(cl_),
@ -181,17 +179,21 @@ void CClient::endNetwork()
} }
} }
void CClient::finishGameplay()
{
waitingRequest.requestTermination();
//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
for(auto & i : playerint)
i.second->finish();
}
void CClient::endGame() void CClient::endGame()
{ {
#if SCRIPTING_ENABLED #if SCRIPTING_ENABLED
clientScripts.reset(); clientScripts.reset();
#endif #endif
//suggest interfaces to finish their stuff (AI should interrupt any bg working threads)
for(auto & i : playerint)
i.second->finish();
{
logNetwork->info("Ending current game!"); logNetwork->info("Ending current game!");
removeGUI(); removeGUI();
@ -199,7 +201,6 @@ void CClient::endGame()
vstd::clear_pointer(gs); vstd::clear_pointer(gs);
logNetwork->info("Deleted mapHandler and gameState."); logNetwork->info("Deleted mapHandler and gameState.");
}
CPlayerInterface::battleInt.reset(); CPlayerInterface::battleInt.reset();
playerint.clear(); playerint.clear();

View File

@ -13,6 +13,8 @@
#include <vcmi/Environment.h> #include <vcmi/Environment.h>
#include "../lib/IGameCallback.h" #include "../lib/IGameCallback.h"
#include "../lib/ConditionalWait.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -50,8 +52,15 @@ class ThreadSafeVector
std::vector<T> items; std::vector<T> items;
std::mutex mx; std::mutex mx;
std::condition_variable cond; std::condition_variable cond;
std::atomic<bool> isTerminating = false;
public: public:
void requestTermination()
{
isTerminating = true;
clear();
}
void clear() void clear()
{ {
TLock lock(mx); TLock lock(mx);
@ -61,6 +70,8 @@ public:
void pushBack(const T & item) void pushBack(const T & item)
{ {
assert(!isTerminating);
TLock lock(mx); TLock lock(mx);
items.push_back(item); items.push_back(item);
cond.notify_all(); cond.notify_all();
@ -68,14 +79,18 @@ public:
void waitWhileContains(const T & item) void waitWhileContains(const T & item)
{ {
//FIXME: should throw exception on destruction
TLock lock(mx); TLock lock(mx);
while(vstd::contains(items, item)) while(vstd::contains(items, item))
cond.wait(lock); cond.wait(lock);
if (isTerminating)
throw TerminationRequestedException();
} }
bool tryRemovingElement(const T & item) //returns false if element was not present bool tryRemovingElement(const T & item) //returns false if element was not present
{ {
assert(!isTerminating);
TLock lock(mx); TLock lock(mx);
auto itr = vstd::find(items, item); auto itr = vstd::find(items, item);
if(itr == items.end()) //not in container if(itr == items.end()) //not in container
@ -129,6 +144,7 @@ public:
void save(const std::string & fname); void save(const std::string & fname);
void endNetwork(); void endNetwork();
void finishGameplay();
void endGame(); void endGame();
void initMapHandler(); void initMapHandler();
@ -139,7 +155,7 @@ public:
void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false); void installNewPlayerInterface(std::shared_ptr<CGameInterface> gameInterface, PlayerColor color, bool battlecb = false);
void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true); void installNewBattleInterface(std::shared_ptr<CBattleGameInterface> battleInterface, PlayerColor color, bool needCallback = true);
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction) ThreadSafeVector<int> waitingRequest;
void handlePack(CPackForClient & pack); //applies the given pack and deletes it void handlePack(CPackForClient & pack); //applies the given pack and deletes it
int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request

View File

@ -882,7 +882,7 @@ void ApplyClientNetPackVisitor::visitEndAction(EndAction & pack)
void ApplyClientNetPackVisitor::visitPackageApplied(PackageApplied & pack) void ApplyClientNetPackVisitor::visitPackageApplied(PackageApplied & pack)
{ {
callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::requestRealized, &pack); callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::requestRealized, &pack);
if(!CClient::waitingRequest.tryRemovingElement(pack.requestID)) if(!cl.waitingRequest.tryRemovingElement(pack.requestID))
logNetwork->warn("Surprising server message! PackageApplied for unknown requestID!"); logNetwork->warn("Surprising server message! PackageApplied for unknown requestID!");
} }