From 2434fe3f6fc7beecd19bba1ef6e2e44f1ccea36b Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Tue, 6 Sep 2022 21:14:22 +0300 Subject: [PATCH] NK: fix AI speed --- AI/Nullkiller/AIGateway.cpp | 4 +- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 19 ++- AI/Nullkiller/Behaviors/DefenceBehavior.cpp | 28 +++- AI/Nullkiller/Engine/Nullkiller.cpp | 142 +++++++++------- AI/Nullkiller/Engine/Nullkiller.h | 4 +- AI/Nullkiller/Goals/AbstractGoal.h | 1 + AI/Nullkiller/Goals/CGoal.h | 2 + AI/Nullkiller/Goals/Composition.cpp | 5 + AI/Nullkiller/Goals/Composition.h | 1 + AI/Nullkiller/Goals/ExecuteHeroChain.h | 2 + AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 152 ++++++++++-------- AI/Nullkiller/Pathfinding/AINodeStorage.h | 19 ++- AI/Nullkiller/Pathfinding/Actors.cpp | 3 + server/CVCMIServer.cpp | 32 ++-- 14 files changed, 264 insertions(+), 150 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 00434ca0c..f657d3ecf 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -766,10 +766,10 @@ void AIGateway::makeTurn() logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn."); return; } - /*catch (std::exception & e) + catch (std::exception & e) { logAi->debug("Making turn thread has caught an exception: %s", e.what()); - }*/ + } endTurn(); } diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index dacacf393..65301992b 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -120,18 +120,23 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons auto blocker = blockers.front(); + if(isObjectPassable(ai, blocker)) + continue; + if(blocker->ID == Obj::GARRISON - || blocker->ID == Obj::MONSTER - || blocker->ID == Obj::GARRISON2 - || blocker->ID == Obj::BORDERGUARD - || blocker->ID == Obj::BORDER_GATE - || blocker->ID == Obj::SHIPYARD) + || blocker->ID == Obj::GARRISON2) { - if(!isObjectPassable(ai, blocker)) + if(dynamic_cast(blocker)->getArmyStrength() == 0) + continue; + else return blocker; } - if(blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked) + if(blocker->ID == Obj::MONSTER + || blocker->ID == Obj::BORDERGUARD + || blocker->ID == Obj::BORDER_GATE + || blocker->ID == Obj::SHIPYARD + || (blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked)) { return blocker; } diff --git a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp index da108da88..41422b0b9 100644 --- a/AI/Nullkiller/Behaviors/DefenceBehavior.cpp +++ b/AI/Nullkiller/Behaviors/DefenceBehavior.cpp @@ -47,14 +47,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const { - logAi->debug("Evaluating defence for %s", town->name); + logAi->trace("Evaluating defence for %s", town->name); auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town); auto treats = { treatNode.fastestDanger, treatNode.maximumDanger }; if(!treatNode.fastestDanger.hero) { - logAi->debug("No treat found for town %s", town->name); + logAi->trace("No treat found for town %s", town->name); return; } @@ -73,7 +73,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta return; } - logAi->debug( + logAi->trace( "Hero %s in garrison of town %s is suposed to defend the town", town->garrisonHero->name, town->name); @@ -85,7 +85,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta if(reinforcement) { - logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement); + logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement); tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f))); } @@ -93,7 +93,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta for(auto & treat : treats) { - logAi->debug( + logAi->trace( "Town %s has treat %lld in %s turns, hero: %s", town->name, treat.danger, @@ -104,6 +104,12 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta for(AIPath & path : paths) { + if(town->visitingHero && path.targetHero != town->visitingHero.get()) + continue; + + if(town->visitingHero && path.getHeroStrength() < town->visitingHero->getHeroStrength()) + continue; + if(path.getHeroStrength() > treat.danger) { if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger)) @@ -111,11 +117,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta || path.turn() < treat.turn - 1 || (path.turn() < treat.turn && treat.turn >= 2)) { - logAi->debug( +#if AI_TRACE_LEVEL >= 1 + logAi->trace( "Hero %s can eliminate danger for town %s using path %s.", path.targetHero->name, town->name, path.toString()); +#endif treatIsUnderControl = true; break; @@ -140,7 +148,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES) { - logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name); +#if AI_TRACE_LEVEL >= 1 + logAi->trace("Hero %s can be recruited to defend %s", hero->name, town->name); +#endif tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1))); continue; } @@ -174,7 +184,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta if(paths.empty()) { - logAi->debug("No ways to defend town %s", town->name); + logAi->trace("No ways to defend town %s", town->name); continue; } @@ -197,9 +207,11 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta #endif if(path.turn() <= treat.turn - 2) { +#if AI_TRACE_LEVEL >= 1 logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun", town->name, path.targetHero->name); +#endif defferedPaths[path.targetHero].push_back(i); diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index 2ef3cc580..add63e4bd 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -116,9 +116,10 @@ void Nullkiller::resetAiState() playerID = ai->playerID; lockedHeroes.clear(); dangerHitMap->reset(); + useHeroChain = true; } -void Nullkiller::updateAiState(int pass) +void Nullkiller::updateAiState(int pass, bool fast) { boost::this_thread::interruption_point(); @@ -126,39 +127,42 @@ void Nullkiller::updateAiState(int pass) activeHero = nullptr; - memory->removeInvisibleObjects(cb.get()); - - dangerHitMap->updateHitMap(); - - boost::this_thread::interruption_point(); - - heroManager->update(); - logAi->trace("Updating paths"); - - std::map activeHeroes; - - for(auto hero : cb->getHeroesInfo()) + if(!fast) { - if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE) - continue; + memory->removeInvisibleObjects(cb.get()); - activeHeroes[hero] = heroManager->getHeroRole(hero); + dangerHitMap->updateHitMap(); + + boost::this_thread::interruption_point(); + + heroManager->update(); + logAi->trace("Updating paths"); + + std::map activeHeroes; + + for(auto hero : cb->getHeroesInfo()) + { + if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE) + continue; + + activeHeroes[hero] = heroManager->getHeroRole(hero); + } + + PathfinderSettings cfg; + cfg.useHeroChain = useHeroChain; + cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT; + + if(scanDepth != ScanDepth::FULL) + { + cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1); + } + + pathfinder->updatePaths(activeHeroes, cfg); + + objectClusterizer->clusterize(); } - PathfinderSettings cfg; - cfg.useHeroChain = true; - cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT; - - if(scanDepth != ScanDepth::FULL) - { - cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1); - } - - pathfinder->updatePaths(activeHeroes, cfg); - armyManager->update(); - - objectClusterizer->clusterize(); buildAnalyzer->update(); decomposer->reset(); @@ -213,13 +217,30 @@ void Nullkiller::makeTurn() { updateAiState(i); + Goals::TTask bestTask = taskptr(Goals::Invalid()); + + do + { + Goals::TTaskVec fastTasks = { + choseBestTask(sptr(BuyArmyBehavior()), 1), + choseBestTask(sptr(RecruitHeroBehavior()), 1), + choseBestTask(sptr(BuildingBehavior()), 1) + }; + + bestTask = choseBestTask(fastTasks); + + if(bestTask->priority >= 1) + { + executeTask(bestTask); + updateAiState(i, true); + } + } while(bestTask->priority >= 1); + Goals::TTaskVec bestTasks = { - choseBestTask(sptr(BuyArmyBehavior()), 1), + bestTask, choseBestTask(sptr(CaptureObjectsBehavior()), 1), choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH), - choseBestTask(sptr(RecruitHeroBehavior()), 1), choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH), - choseBestTask(sptr(BuildingBehavior()), 1), choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH) }; @@ -228,19 +249,25 @@ void Nullkiller::makeTurn() bestTasks.push_back(choseBestTask(sptr(StartupBehavior()), 1)); } - Goals::TTask bestTask = choseBestTask(bestTasks); + bestTask = choseBestTask(bestTasks); + HeroPtr hero = bestTask->getHero(); + HeroRole heroRole = HeroRole::MAIN; + + if(hero.validAndSet()) + heroRole = heroManager->getHeroRole(hero); + + if(heroRole != HeroRole::MAIN || bestTask->getHeroExchangeCount() <= 1) + useHeroChain = false; + if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY && scanDepth != ScanDepth::FULL) { - HeroRole heroRole = HeroRole::MAIN; - - if(hero.validAndSet()) - heroRole = heroManager->getHeroRole(hero); - if(heroRole == HeroRole::MAIN || bestTask->priority < MIN_PRIORITY) { + useHeroChain = false; + logAi->trace( "Goal %s has too low priority %f so increasing scan depth", bestTask->toString(), @@ -258,26 +285,31 @@ void Nullkiller::makeTurn() return; } - std::string taskDescr = bestTask->toString(); + executeTask(bestTask); + } +} - boost::this_thread::interruption_point(); - logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, bestTask->priority); +void Nullkiller::executeTask(Goals::TTask task) +{ + std::string taskDescr = task->toString(); - try - { - bestTask->accept(ai.get()); - } - catch(goalFulfilledException &) - { - logAi->trace("Task %s completed", bestTask->toString()); - } - catch(std::exception & e) - { - logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr); - logAi->debug("The error message was: %s", e.what()); + boost::this_thread::interruption_point(); + logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, task->priority); - return; - } + try + { + task->accept(ai.get()); + } + catch(goalFulfilledException &) + { + logAi->trace("Task %s completed", task->toString()); + } + catch(std::exception & e) + { + logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr); + logAi->debug("The error message was: %s", e.what()); + + throw; } } diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index 6f95ba17f..9bb3304c0 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -51,6 +51,7 @@ private: std::map lockedHeroes; ScanDepth scanDepth; TResources lockedResources; + bool useHeroChain; public: std::unique_ptr dangerHitMap; @@ -86,7 +87,8 @@ public: private: void resetAiState(); - void updateAiState(int pass); + void updateAiState(int pass, bool fast = false); Goals::TTask choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const; Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const; + void executeTask(Goals::TTask task); }; diff --git a/AI/Nullkiller/Goals/AbstractGoal.h b/AI/Nullkiller/Goals/AbstractGoal.h index 64bcd6b8d..7407a5fc4 100644 --- a/AI/Nullkiller/Goals/AbstractGoal.h +++ b/AI/Nullkiller/Goals/AbstractGoal.h @@ -162,6 +162,7 @@ namespace Goals virtual std::string toString() const = 0; virtual HeroPtr getHero() const = 0; virtual ~ITask() {} + virtual int getHeroExchangeCount() const = 0; }; } diff --git a/AI/Nullkiller/Goals/CGoal.h b/AI/Nullkiller/Goals/CGoal.h index 5e0d341c8..e5f9679c8 100644 --- a/AI/Nullkiller/Goals/CGoal.h +++ b/AI/Nullkiller/Goals/CGoal.h @@ -90,6 +90,8 @@ namespace Goals virtual bool isElementar() const override { return true; } virtual HeroPtr getHero() const override { return AbstractGoal::hero; } + + virtual int getHeroExchangeCount() const override { return 0; } }; class DLL_EXPORT Invalid : public ElementarGoal diff --git a/AI/Nullkiller/Goals/Composition.cpp b/AI/Nullkiller/Goals/Composition.cpp index 61151f388..4adafdfe0 100644 --- a/AI/Nullkiller/Goals/Composition.cpp +++ b/AI/Nullkiller/Goals/Composition.cpp @@ -72,4 +72,9 @@ Composition & Composition::addNext(TSubgoal goal) bool Composition::isElementar() const { return subtasks.back()->isElementar(); +} + +int Composition::getHeroExchangeCount() const +{ + return isElementar() ? taskptr(*subtasks.back())->getHeroExchangeCount() : 0; } \ No newline at end of file diff --git a/AI/Nullkiller/Goals/Composition.h b/AI/Nullkiller/Goals/Composition.h index 7906b4841..ce8b66313 100644 --- a/AI/Nullkiller/Goals/Composition.h +++ b/AI/Nullkiller/Goals/Composition.h @@ -36,5 +36,6 @@ namespace Goals Composition & addNext(TSubgoal goal); virtual TGoalVec decompose() const override; virtual bool isElementar() const override; + virtual int getHeroExchangeCount() const override; }; } diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.h b/AI/Nullkiller/Goals/ExecuteHeroChain.h index 8e3b4085b..7aaf20ad3 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.h +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.h @@ -31,6 +31,8 @@ namespace Goals virtual bool operator==(const ExecuteHeroChain & other) const override; const AIPath & getPath() const { return chainPath; } + virtual int getHeroExchangeCount() const override { return chainPath.exchangeCount; } + private: bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile); }; diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 44159791c..b62e047cb 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -23,23 +23,17 @@ std::shared_ptr> AISharedStorage::shared; std::set commitedTiles; std::set commitedTilesInitial; -#ifdef ENVIRONMENT64 -const int BUCKET_COUNT = 11; -#else -const int BUCKET_COUNT = 7; -#endif // ENVIRONMENT64 const uint64_t FirstActorMask = 1; -const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER; -const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE; const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000; const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000; +const uint64_t CHAIN_MAX_DEPTH = 4; AISharedStorage::AISharedStorage(int3 sizes) { if(!shared){ shared.reset(new boost::multi_array( - boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS])); + boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS])); } nodes = shared; @@ -139,16 +133,16 @@ boost::optional AINodeStorage::getOrCreateNode( const EPathfindingLayer layer, const ChainActor * actor) { - int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT; - int bucketOffset = bucketIndex * BUCKET_SIZE; - auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer + int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT; + int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE; + auto chains = nodes.get(pos, layer); if(chains[0].blocked()) { return boost::none; } - for(auto i = BUCKET_SIZE - 1; i >= 0; i--) + for(auto i = AIPathfinding::BUCKET_SIZE - 1; i >= 0; i--) { AIPathNode & node = chains[i + bucketOffset]; @@ -171,8 +165,9 @@ boost::optional AINodeStorage::getOrCreateNode( std::vector AINodeStorage::getInitialNodes() { if(heroChainPass) -{ - calculateTownPortalTeleportations(heroChain); + { + if(heroChainTurn == 0) + calculateTownPortalTeleportations(heroChain); return heroChain; } @@ -207,7 +202,8 @@ std::vector AINodeStorage::getInitialNodes() } } - calculateTownPortalTeleportations(initialNodes); + if(heroChainTurn == 0) + calculateTownPortalTeleportations(initialNodes); return initialNodes; } @@ -406,8 +402,8 @@ public: AINodeStorage & storage, AISharedStorage & nodes, const std::vector & tiles, uint64_t chainMask, int heroChainTurn) :existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles) { - existingChains.reserve(NUM_CHAINS); - newChains.reserve(NUM_CHAINS); + existingChains.reserve(AIPathfinding::NUM_CHAINS); + newChains.reserve(AIPathfinding::NUM_CHAINS); } void execute(const blocked_range& r) @@ -623,6 +619,9 @@ void HeroChainCalculationTask::calculateHeroChain( if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0) continue; + if(node->actor->actorExchangeCount + srcNode->actor->actorExchangeCount > CHAIN_MAX_DEPTH) + continue; + if(node->action == CGPathNode::ENodeAction::BATTLE || node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE || node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL @@ -994,8 +993,6 @@ struct TowmPortalFinder CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown) { - CGPathNode * bestNode = nullptr; - for(CGPathNode * node : initialNodes) { auto aiNode = nodeStorage->getAINode(node); @@ -1018,11 +1015,10 @@ struct TowmPortalFinder continue; } - if(!bestNode || bestNode->getCost() > node->getCost()) - bestNode = node; + return node; } - return bestNode; + return nullptr; } boost::optional createTownPortalNode(const CGTownInstance * targetTown) @@ -1060,6 +1056,55 @@ struct TowmPortalFinder } }; +template +void AINodeStorage::calculateTownPortal( + const ChainActor * actor, + const std::map & maskMap, + const std::vector & initialNodes, + TVector & output) +{ + auto towns = cb->getTownsInfo(false); + + vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool + { + return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES; + }); + + if(!towns.size()) + { + return; // no towns no need to run loop further + } + + TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this); + + if(townPortalFinder.actorCanCastTownPortal()) + { + for(const CGTownInstance * targetTown : towns) + { + // TODO: allow to hide visiting hero in garrison + if(targetTown->visitingHero) + { + auto basicMask = maskMap.at(targetTown->visitingHero.get()); + bool heroIsInChain = (actor->chainMask & basicMask) != 0; + bool sameActorInTown = actor->chainMask == basicMask; + + if(sameActorInTown || !heroIsInChain) + continue; + } + + auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown); + + if(nodeOptional) + { +#if PATHFINDER_TRACE_LEVEL >= 1 + logAi->trace("Adding town portal node at %s", targetTown->name); +#endif + output.push_back(nodeOptional.get()); + } + } + } +} + void AINodeStorage::calculateTownPortalTeleportations(std::vector & initialNodes) { std::set actorsOfInitial; @@ -1068,7 +1113,8 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector { auto aiNode = getAINode(node); - actorsOfInitial.insert(aiNode->actor->baseActor); + if(aiNode->actor->hero) + actorsOfInitial.insert(aiNode->actor->baseActor); } std::map maskMap; @@ -1079,50 +1125,28 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector maskMap[basicActor->hero] = basicActor->chainMask; } - for(const ChainActor * actor : actorsOfInitial) + boost::sort(initialNodes, NodeComparer()); + + std::vector actorsVector(actorsOfInitial.begin(), actorsOfInitial.end()); + tbb::concurrent_vector output; + + if(actorsVector.size() * initialNodes.size() > 1000) { - if(!actor->hero) - continue; - - auto towns = cb->getTownsInfo(false); - - vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool - { - return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES; - }); - - if(!towns.size()) - { - return; // no towns no need to run loop further - } - - TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this); - - if(townPortalFinder.actorCanCastTownPortal()) - { - for(const CGTownInstance * targetTown : towns) + parallel_for(blocked_range(0, actorsVector.size()), [&](const blocked_range & r) { - // TODO: allow to hide visiting hero in garrison - if(targetTown->visitingHero) + for(int i = r.begin(); i != r.end(); i++) { - auto basicMask = maskMap[targetTown->visitingHero.get()]; - bool heroIsInChain = (actor->chainMask & basicMask) != 0; - bool sameActorInTown = actor->chainMask == basicMask; - - if(sameActorInTown || !heroIsInChain) - continue; + calculateTownPortal(actorsVector[i], maskMap, initialNodes, output); } + }); - auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown); - - if(nodeOptional) - { -#if PATHFINDER_TRACE_LEVEL >= 1 - logAi->trace("Adding town portal node at %s", targetTown->name); -#endif - initialNodes.push_back(nodeOptional.get()); - } - } + std::copy(output.begin(), output.end(), std::back_inserter(initialNodes)); + } + else + { + for(auto actor : actorsVector) + { + calculateTownPortal(actor, maskMap, initialNodes, initialNodes); } } } @@ -1206,7 +1230,7 @@ bool AINodeStorage::hasBetterChain( continue; } -#if AI_TRACE_LEVEL >= 2 +#if PATHFINDER_TRACE_LEVEL >= 2 logAi->trace( "Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i", source->coord.toString(), @@ -1244,7 +1268,7 @@ std::vector AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) { std::vector paths; - paths.reserve(NUM_CHAINS / 4); + paths.reserve(AIPathfinding::NUM_CHAINS / 4); auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL); diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index bd97f88b6..075a90d32 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -25,10 +25,16 @@ namespace AIPathfinding { - const int BUCKET_COUNT = 11; - const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER; +#ifdef ENVIRONMENT64 + const int BUCKET_COUNT = 7; +#else + const int BUCKET_COUNT = 5; +#endif // ENVIRONMENT64 + + const int BUCKET_SIZE = 5; const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE; const int THREAD_COUNT = 8; + const int CHAIN_MAX_DEPTH = 4; } struct AIPathNode : public CGPathNode @@ -239,7 +245,14 @@ public: return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT; } - void calculateTownPortalTeleportations(std::vector & neighbours); void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const; + +private: + template + void calculateTownPortal( + const ChainActor * actor, + const std::map & maskMap, + const std::vector & initialNodes, + TVector & output); }; diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index d4905d462..b5f730a12 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -275,6 +275,9 @@ ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other) if(!differentMasks) return result; + if(actor->allowSpellCast || other->allowSpellCast) + return result; + TResources resources = ai->cb->getResourceAmount(); if(!resources.canAfford(actor->armyCost + other->armyCost)) diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 742ff5194..12f972cd0 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -315,11 +315,28 @@ void CVCMIServer::threadHandleClient(std::shared_ptr c) setThreadName("CVCMIServer::handleConnection"); c->enterLobbyConnectionMode(); +#ifndef _MSC_VER try { +#endif while(c->connected) { - CPack * pack = c->retrievePack(); + CPack * pack; + + try + { + pack = c->retrievePack(); + } + catch(boost::system::system_error & e) + { + logNetwork->error("Network error receiving a pack. Connection %s dies. What happened: %s", c->toString(), e.what()); + + if(state != EServerState::LOBBY) + gh->handleClientDisconnection(c); + + break; + } + if(auto lobbyPack = dynamic_ptr_cast(pack)) { handleReceivedPack(std::unique_ptr(lobbyPack)); @@ -329,14 +346,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr c) gh->handleReceivedPack(serverPack); } } - } - catch(boost::system::system_error & e) - { - (void)e; - if(state != EServerState::LOBBY) - gh->handleClientDisconnection(c); - } - /* +#ifndef _MSC_VER + } catch(const std::exception & e) { (void)e; @@ -348,7 +359,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr c) state = EServerState::SHUTDOWN; handleException(); throw; - }*/ + } +#endif boost::unique_lock queueLock(mx); // if(state != ENDING_AND_STARTING_GAME)