|
@@ -68,10 +68,10 @@ struct SetGlobalState
|
|
|
AIGateway::AIGateway()
|
|
|
{
|
|
|
LOG_TRACE(logAi);
|
|
|
- makingTurn = nullptr;
|
|
|
destinationTeleport = ObjectInstanceID();
|
|
|
destinationTeleportPos = int3(-1);
|
|
|
nullkiller.reset(new Nullkiller());
|
|
|
+ asyncTasks = std::make_unique<tbb::task_group>();
|
|
|
}
|
|
|
|
|
|
AIGateway::~AIGateway()
|
|
@@ -163,7 +163,7 @@ void AIGateway::showTavernWindow(const CGObjectInstance * object, const CGHeroIn
|
|
|
NET_EVENT_HANDLER;
|
|
|
|
|
|
status.addQuery(queryID, "TavernWindow");
|
|
|
- requestActionASAP([this, queryID](){ answerQuery(queryID, 0); });
|
|
|
+ executeActionAsync("showTavernWindow", [this, queryID](){ answerQuery(queryID, 0); });
|
|
|
}
|
|
|
|
|
|
void AIGateway::showThievesGuildWindow(const CGObjectInstance * obj)
|
|
@@ -296,7 +296,7 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her
|
|
|
|
|
|
status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->getNameTranslated() % firstHero->tempOwner % secondHero->getNameTranslated() % secondHero->tempOwner));
|
|
|
|
|
|
- requestActionASAP([this, firstHero, secondHero, query]()
|
|
|
+ executeActionAsync("heroExchangeStarted", [this, firstHero, secondHero, query]()
|
|
|
{
|
|
|
auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void
|
|
|
{
|
|
@@ -335,7 +335,7 @@ void AIGateway::showRecruitmentDialog(const CGDwelling * dwelling, const CArmedI
|
|
|
|
|
|
status.addQuery(queryID, "RecruitmentDialog");
|
|
|
|
|
|
- requestActionASAP([this, dwelling, dst, queryID](){
|
|
|
+ executeActionAsync("showRecruitmentDialog", [this, dwelling, dst, queryID](){
|
|
|
recruitCreatures(dwelling, dst);
|
|
|
answerQuery(queryID, 0);
|
|
|
});
|
|
@@ -454,7 +454,7 @@ void AIGateway::showUniversityWindow(const IMarket * market, const CGHeroInstanc
|
|
|
NET_EVENT_HANDLER;
|
|
|
|
|
|
status.addQuery(queryID, "UniversityWindow");
|
|
|
- requestActionASAP([this, queryID](){ answerQuery(queryID, 0); });
|
|
|
+ executeActionAsync("showUniversityWindow", [this, queryID](){ answerQuery(queryID, 0); });
|
|
|
}
|
|
|
|
|
|
void AIGateway::heroManaPointsChanged(const CGHeroInstance * hero)
|
|
@@ -530,7 +530,7 @@ void AIGateway::showMarketWindow(const IMarket * market, const CGHeroInstance *
|
|
|
NET_EVENT_HANDLER;
|
|
|
|
|
|
status.addQuery(queryID, "MarketWindow");
|
|
|
- requestActionASAP([this, queryID](){ answerQuery(queryID, 0); });
|
|
|
+ executeActionAsync("showMarketWindow", [this, queryID](){ answerQuery(queryID, 0); });
|
|
|
}
|
|
|
|
|
|
void AIGateway::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain)
|
|
@@ -586,14 +586,17 @@ void AIGateway::yourTurn(QueryID queryID)
|
|
|
NET_EVENT_HANDLER;
|
|
|
nullkiller->invalidatePathfinderData();
|
|
|
status.addQuery(queryID, "YourTurn");
|
|
|
- requestActionASAP([this, queryID](){ answerQuery(queryID, 0); });
|
|
|
+ executeActionAsync("yourTurn", [this, queryID](){ answerQuery(queryID, 0); });
|
|
|
status.startedTurn();
|
|
|
|
|
|
- if (makingTurn && makingTurn->joinable())
|
|
|
- makingTurn->join(); // leftover from previous turn
|
|
|
-
|
|
|
nullkiller->makingTurnInterrupption.reset();
|
|
|
- makingTurn = std::make_unique<std::thread>(&AIGateway::makeTurn, this);
|
|
|
+
|
|
|
+ asyncTasks->run([this]()
|
|
|
+ {
|
|
|
+ ScopedThreadName guard("NKAI::makingTurn");
|
|
|
+ makeTurn();
|
|
|
+ });
|
|
|
+ tbb::this_task_arena::enqueue([this](){asyncTasks->wait();});
|
|
|
}
|
|
|
|
|
|
void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID)
|
|
@@ -604,7 +607,7 @@ void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, s
|
|
|
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->getNameTranslated() % hero->level));
|
|
|
HeroPtr hPtr = hero;
|
|
|
|
|
|
- requestActionASAP([this, hPtr, skills, queryID]()
|
|
|
+ executeActionAsync("heroGotLevel", [this, hPtr, skills, queryID]()
|
|
|
{
|
|
|
int sel = 0;
|
|
|
|
|
@@ -626,7 +629,7 @@ void AIGateway::commanderGotLevel(const CCommanderInstance * commander, std::vec
|
|
|
LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
|
|
|
NET_EVENT_HANDLER;
|
|
|
status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->armyObj->nodeName() % (int)commander->level));
|
|
|
- requestActionASAP([this, queryID](){ answerQuery(queryID, 0); });
|
|
|
+ executeActionAsync("commanderGotLevel", [this, queryID](){ answerQuery(queryID, 0); });
|
|
|
}
|
|
|
|
|
|
void AIGateway::showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel, bool safeToAutoaccept)
|
|
@@ -641,7 +644,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
|
|
|
|
|
if(!selection && cancel)
|
|
|
{
|
|
|
- requestActionASAP([this, hero, target, askID]()
|
|
|
+ executeActionAsync("showBlockingDialog", [this, hero, target, askID]()
|
|
|
{
|
|
|
//yes&no -> always answer yes, we are a brave AI :)
|
|
|
bool answer = true;
|
|
@@ -689,7 +692,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- requestActionASAP([this, selection, components, hero, askID]()
|
|
|
+ executeActionAsync("showBlockingDialog", [this, selection, components, hero, askID]()
|
|
|
{
|
|
|
int sel = 0;
|
|
|
|
|
@@ -751,7 +754,7 @@ void AIGateway::showTeleportDialog(const CGHeroInstance * hero, TeleportChannelI
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- requestActionASAP([this, askID, chosenExit]()
|
|
|
+ executeActionAsync("showTeleportDialog", [this, askID, chosenExit]()
|
|
|
{
|
|
|
answerQuery(askID, chosenExit);
|
|
|
});
|
|
@@ -768,7 +771,7 @@ void AIGateway::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstan
|
|
|
status.addQuery(queryID, boost::str(boost::format("Garrison dialog with %s and %s") % s1 % s2));
|
|
|
|
|
|
//you can't request action from action-response thread
|
|
|
- requestActionASAP([this, up, down, removableUnits, queryID]()
|
|
|
+ executeActionAsync("showGarrisonDialog", [this, up, down, removableUnits, queryID]()
|
|
|
{
|
|
|
if(removableUnits && up->tempOwner == down->tempOwner && nullkiller->settings->isGarrisonTroopsUsageAllowed() && !cb->getStartInfo()->isRestorationOfErathiaCampaign())
|
|
|
{
|
|
@@ -783,7 +786,7 @@ void AIGateway::showMapObjectSelectDialog(QueryID askID, const Component & icon,
|
|
|
{
|
|
|
NET_EVENT_HANDLER;
|
|
|
status.addQuery(askID, "Map object select query");
|
|
|
- requestActionASAP([this, askID](){ answerQuery(askID, selectedObject.getNum()); });
|
|
|
+ executeActionAsync("showMapObjectSelectDialog", [this, askID](){ answerQuery(askID, selectedObject.getNum()); });
|
|
|
}
|
|
|
|
|
|
bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
|
|
@@ -1218,10 +1221,10 @@ void AIGateway::battleEnd(const BattleID & battleID, const BattleResult * br, Qu
|
|
|
{
|
|
|
status.addQuery(queryID, "Confirm battle query");
|
|
|
|
|
|
- requestActionASAP([this, queryID]()
|
|
|
- {
|
|
|
- answerQuery(queryID, 0);
|
|
|
- });
|
|
|
+ executeActionAsync("battleEnd", [this, queryID]()
|
|
|
+ {
|
|
|
+ answerQuery(queryID, 0);
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1592,24 +1595,26 @@ void AIGateway::finish()
|
|
|
{
|
|
|
nullkiller->makingTurnInterrupption.interruptThread();
|
|
|
|
|
|
- if(makingTurn)
|
|
|
+ if (asyncTasks)
|
|
|
{
|
|
|
- makingTurn->join();
|
|
|
- makingTurn.reset();
|
|
|
+ asyncTasks->wait();
|
|
|
+ asyncTasks.reset();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void AIGateway::requestActionASAP(std::function<void()> whatToDo)
|
|
|
+void AIGateway::executeActionAsync(const std::string & description, const std::function<void()> & whatToDo)
|
|
|
{
|
|
|
- std::thread newThread([this, whatToDo]()
|
|
|
+ if (!asyncTasks)
|
|
|
+ throw std::runtime_error("Attempt to execute task on shut down AI state!");
|
|
|
+
|
|
|
+ asyncTasks->run([this, description, whatToDo]()
|
|
|
{
|
|
|
- setThreadName("AIGateway::requestActionASAP::whatToDo");
|
|
|
+ ScopedThreadName guard("NKAI::" + description);
|
|
|
SET_GLOBAL_STATE(this);
|
|
|
std::shared_lock gsLock(CGameState::mutex);
|
|
|
whatToDo();
|
|
|
});
|
|
|
-
|
|
|
- newThread.detach();
|
|
|
+ tbb::this_task_arena::enqueue([this](){asyncTasks->wait();});
|
|
|
}
|
|
|
|
|
|
void AIGateway::lostHero(HeroPtr h)
|