1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

Merge pull request #899 from vcmi/fix-nk-ai-speed

NK: fix AI speed
This commit is contained in:
Andrii Danylchenko 2022-10-01 17:50:09 +03:00 committed by GitHub
commit a7e2e8a362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 264 additions and 150 deletions

View File

@ -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();
}

View File

@ -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<const CArmedInstance *>(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;
}

View File

@ -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);

View File

@ -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<const CGHeroInstance *, HeroRole> 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<const CGHeroInstance *, HeroRole> 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;
}
}

View File

@ -51,6 +51,7 @@ private:
std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
ScanDepth scanDepth;
TResources lockedResources;
bool useHeroChain;
public:
std::unique_ptr<DangerHitMapAnalyzer> 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);
};

View File

@ -162,6 +162,7 @@ namespace Goals
virtual std::string toString() const = 0;
virtual HeroPtr getHero() const = 0;
virtual ~ITask() {}
virtual int getHeroExchangeCount() const = 0;
};
}

View File

@ -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<Invalid>

View File

@ -73,3 +73,8 @@ bool Composition::isElementar() const
{
return subtasks.back()->isElementar();
}
int Composition::getHeroExchangeCount() const
{
return isElementar() ? taskptr(*subtasks.back())->getHeroExchangeCount() : 0;
}

View File

@ -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;
};
}

View File

@ -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);
};

View File

@ -23,23 +23,17 @@ std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
std::set<int3> commitedTiles;
std::set<int3> 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<AIPathNode, 5>(
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<AIPathNode *> 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<AIPathNode *> AINodeStorage::getOrCreateNode(
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
{
if(heroChainPass)
{
calculateTownPortalTeleportations(heroChain);
{
if(heroChainTurn == 0)
calculateTownPortalTeleportations(heroChain);
return heroChain;
}
@ -207,7 +202,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
}
}
calculateTownPortalTeleportations(initialNodes);
if(heroChainTurn == 0)
calculateTownPortalTeleportations(initialNodes);
return initialNodes;
}
@ -406,8 +402,8 @@ public:
AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & 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<size_t>& 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<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
@ -1060,6 +1056,55 @@ struct TowmPortalFinder
}
};
template<class TVector>
void AINodeStorage::calculateTownPortal(
const ChainActor * actor,
const std::map<const CGHeroInstance *, int> & maskMap,
const std::vector<CGPathNode *> & 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<CGPathNode *> & initialNodes)
{
std::set<const ChainActor *> actorsOfInitial;
@ -1068,7 +1113,8 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
{
auto aiNode = getAINode(node);
actorsOfInitial.insert(aiNode->actor->baseActor);
if(aiNode->actor->hero)
actorsOfInitial.insert(aiNode->actor->baseActor);
}
std::map<const CGHeroInstance *, int> maskMap;
@ -1079,50 +1125,28 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
maskMap[basicActor->hero] = basicActor->chainMask;
}
for(const ChainActor * actor : actorsOfInitial)
boost::sort(initialNodes, NodeComparer<CGPathNode>());
std::vector<const ChainActor *> actorsVector(actorsOfInitial.begin(), actorsOfInitial.end());
tbb::concurrent_vector<CGPathNode *> 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<size_t>(0, actorsVector.size()), [&](const blocked_range<size_t> & 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<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
{
std::vector<AIPath> paths;
paths.reserve(NUM_CHAINS / 4);
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);

View File

@ -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<CGPathNode *> & neighbours);
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
private:
template<class TVector>
void calculateTownPortal(
const ChainActor * actor,
const std::map<const CGHeroInstance *, int> & maskMap,
const std::vector<CGPathNode *> & initialNodes,
TVector & output);
};

View File

@ -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))

View File

@ -315,11 +315,28 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> 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<CPackForLobby>(pack))
{
handleReceivedPack(std::unique_ptr<CPackForLobby>(lobbyPack));
@ -329,14 +346,8 @@ void CVCMIServer::threadHandleClient(std::shared_ptr<CConnection> 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<CConnection> c)
state = EServerState::SHUTDOWN;
handleException();
throw;
}*/
}
#endif
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
// if(state != ENDING_AND_STARTING_GAME)