2
0
Эх сурвалжийг харах

Split CQuery file into multiple files

Ivan Savenko 2 жил өмнө
parent
commit
323772fc2e

+ 3 - 2
server/CGameHandler.cpp

@@ -16,7 +16,8 @@
 #include "battles/BattleProcessor.h"
 #include "processors/HeroPoolProcessor.h"
 #include "processors/PlayerMessageProcessor.h"
-#include "queries/CQuery.h"
+#include "queries/QueriesProcessor.h"
+#include "queries/MapQueries.h"
 
 #include "../lib/ArtifactUtils.h"
 #include "../lib/CArtHandler.h"
@@ -544,7 +545,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
 	: lobby(lobby)
 	, heroPool(std::make_unique<HeroPoolProcessor>(this))
 	, battles(std::make_unique<BattleProcessor>(this))
-	, queries(std::make_unique<Queries>())
+	, queries(std::make_unique<QueriesProcessor>())
 	, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
 	, complainNoCreatures("No creatures to split")
 	, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")

+ 2 - 2
server/CGameHandler.h

@@ -47,7 +47,7 @@ class CVCMIServer;
 class CBaseForGHApply;
 class PlayerMessageProcessor;
 class BattleProcessor;
-class Queries;
+class QueriesProcessor;
 class CObjectVisitQuery;
 
 struct PlayerStatus
@@ -87,7 +87,7 @@ public:
 
 	std::unique_ptr<HeroPoolProcessor> heroPool;
 	std::unique_ptr<BattleProcessor> battles;
-	std::unique_ptr<Queries> queries;
+	std::unique_ptr<QueriesProcessor> queries;
 
 	//use enums as parameters, because doMove(sth, true, false, true) is not readable
 	enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS};

+ 6 - 0
server/CMakeLists.txt

@@ -3,7 +3,10 @@ set(server_SRCS
 
 		battles/BattleProcessor.cpp
 
+		queries/BattleQueries.cpp
 		queries/CQuery.cpp
+		queries/MapQueries.cpp
+		queries/QueriesProcessor.cpp
 
 		processors/HeroPoolProcessor.cpp
 		processors/PlayerMessageProcessor.cpp
@@ -20,7 +23,10 @@ set(server_HEADERS
 
 		battles/BattleProcessor.h
 
+		queries/BattleQueries.h
 		queries/CQuery.h
+		queries/MapQueries.h
+		queries/QueriesProcessor.h
 
 		processors/HeroPoolProcessor.h
 		processors/PlayerMessageProcessor.h

+ 1 - 1
server/NetPacksServer.cpp

@@ -14,7 +14,7 @@
 #include "battles/BattleProcessor.h"
 #include "processors/HeroPoolProcessor.h"
 #include "processors/PlayerMessageProcessor.h"
-#include "queries/CQuery.h"
+#include "queries/QueriesProcessor.h"
 
 #include "../lib/IGameCallback.h"
 #include "../lib/mapObjects/CGTownInstance.h"

+ 2 - 0
server/ServerSpellCastEnvironment.cpp

@@ -11,9 +11,11 @@
 #include "ServerSpellCastEnvironment.h"
 
 #include "CGameHandler.h"
+#include "queries/QueriesProcessor.h"
 #include "queries/CQuery.h"
 
 #include "../lib/gameState/CGameState.h"
+#include "../lib/NetPacks.h"
 
 ///ServerSpellCastEnvironment
 ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh)

+ 6 - 5
server/battles/BattleProcessor.cpp

@@ -13,21 +13,23 @@
 #include "../CGameHandler.h"
 #include "../CVCMIServer.h"
 #include "../processors/HeroPoolProcessor.h"
-#include "../queries/CQuery.h"
+#include "../queries/QueriesProcessor.h"
+#include "../queries/BattleQueries.h"
 
 #include "../../lib/ArtifactUtils.h"
 #include "../../lib/CGeneralTextHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/CStack.h"
 #include "../../lib/CondSh.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/ScopeGuard.h"
 #include "../../lib/TerrainHandler.h"
+#include "../../lib/UnlockGuard.h"
 #include "../../lib/battle/BattleInfo.h"
 #include "../../lib/battle/CUnitState.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapping/CMap.h"
+#include "../../lib/modding/IdentifierStorage.h"
 #include "../../lib/serializer/Cast.h"
 #include "../../lib/spells/AbilityCaster.h"
 #include "../../lib/spells/BonusCaster.h"
@@ -350,7 +352,7 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
 
 	setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
 
-	auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries.topQuery(gameHandler->gameState()->curB->sides[0].color));
+	auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(gameHandler->gameState()->curB->sides[0].color));
 
 	//existing battle query for retying auto-combat
 	if(lastBattleQuery)
@@ -2001,7 +2003,7 @@ void BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], co
 	engageIntoBattle(bs.info->sides[0].color);
 	engageIntoBattle(bs.info->sides[1].color);
 
-	auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries.topQuery(bs.info->sides[0].color));
+	auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(bs.info->sides[0].color));
 	bs.info->replayAllowed = lastBattleQuery == nullptr && !bs.info->sides[1].color.isValidPlayer();
 
 	gameHandler->sendAndApply(&bs);
@@ -2020,7 +2022,6 @@ void BattleProcessor::checkBattleStateChanges()
 	}
 }
 
-
 bool BattleProcessor::makeBattleAction(BattleAction &ba)
 {
 	bool ok = true;

+ 1 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -16,7 +16,7 @@
 #include "../../lib/serializer/Connection.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/CModHandler.h"
+#include "../../lib/modding/IdentifierStorage.h"
 #include "../../lib/CPlayerState.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/NetPacks.h"

+ 75 - 0
server/queries/BattleQueries.cpp

@@ -0,0 +1,75 @@
+/*
+ * BattleQueries.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "BattleQueries.h"
+#include "MapQueries.h"
+
+#include "../CGameHandler.h"
+#include "../battles/BattleProcessor.h"
+
+#include "../../lib/battle/BattleInfo.h"
+
+void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	if(result)
+		objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
+}
+
+CBattleQuery::CBattleQuery(CGameHandler * owner, const BattleInfo * Bi):
+	CGhQuery(owner)
+{
+	belligerents[0] = Bi->sides[0].armyObject;
+	belligerents[1] = Bi->sides[1].armyObject;
+
+	bi = Bi;
+
+	for(auto & side : bi->sides)
+		addPlayer(side.color);
+}
+
+CBattleQuery::CBattleQuery(CGameHandler * owner):
+	CGhQuery(owner), bi(nullptr)
+{
+	belligerents[0] = belligerents[1] = nullptr;
+}
+
+bool CBattleQuery::blocksPack(const CPack * pack) const
+{
+	const char * name = typeid(*pack).name();
+	return strcmp(name, typeid(MakeAction).name()) && strcmp(name, typeid(MakeCustomAction).name());
+}
+
+void CBattleQuery::onRemoval(PlayerColor color)
+{
+	if(result)
+		gh->battles->battleAfterLevelUp(*result);
+}
+
+CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
+	CDialogQuery(owner)
+{
+	bi = Bi;
+
+	for(auto & side : bi->sides)
+		addPlayer(side.color);
+}
+
+void CBattleDialogQuery::onRemoval(PlayerColor color)
+{
+	assert(answer);
+	if(*answer == 1)
+	{
+		gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
+	}
+	else
+	{
+		gh->battles->endBattleConfirm(bi);
+	}
+}

+ 40 - 0
server/queries/BattleQueries.h

@@ -0,0 +1,40 @@
+/*
+ * BattleQueries.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "CQuery.h"
+
+#include "../../lib/NetPacks.h"
+
+class CBattleQuery : public CGhQuery
+{
+public:
+	std::array<const CArmedInstance *,2> belligerents;
+	std::array<int, 2> initialHeroMana;
+
+	const BattleInfo *bi;
+	std::optional<BattleResult> result;
+
+	CBattleQuery(CGameHandler * owner);
+	CBattleQuery(CGameHandler * owner, const BattleInfo * Bi); //TODO
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+	virtual bool blocksPack(const CPack *pack) const override;
+	virtual void onRemoval(PlayerColor color) override;
+};
+
+class CBattleDialogQuery : public CDialogQuery
+{
+public:
+	CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi);
+
+	const BattleInfo * bi;
+
+	virtual void onRemoval(PlayerColor color) override;
+};

+ 6 - 392
server/queries/CQuery.cpp

@@ -10,14 +10,12 @@
 #include "StdInc.h"
 #include "CQuery.h"
 
+#include "QueriesProcessor.h"
+
 #include "../CGameHandler.h"
-#include "../battles/BattleProcessor.h"
 
-#include "../../lib/battle/BattleInfo.h"
-#include "../../lib/mapObjects/MiscObjects.h"
 #include "../../lib/serializer/Cast.h"
-
-boost::mutex Queries::mx;
+#include "../../lib/NetPacks.h"
 
 template <typename Container>
 std::string formatContainer(const Container & c, std::string delimeter = ", ", std::string opener = "(", std::string closer=")")
@@ -47,10 +45,10 @@ std::ostream & operator<<(std::ostream & out, QueryPtr query)
 	return out << "[" << query.get() << "] " << query->toString();
 }
 
-CQuery::CQuery(Queries * Owner):
+CQuery::CQuery(QueriesProcessor * Owner):
 	owner(Owner)
 {
-	boost::unique_lock<boost::mutex> l(Queries::mx);
+	boost::unique_lock<boost::mutex> l(QueriesProcessor::mx);
 
 	static QueryID QID = QueryID(0);
 
@@ -58,7 +56,6 @@ CQuery::CQuery(Queries * Owner):
 	logGlobal->trace("Created a new query with id %d", queryID);
 }
 
-
 CQuery::~CQuery()
 {
 	logGlobal->trace("Destructed the query with id %d", queryID);
@@ -150,349 +147,6 @@ CGhQuery::CGhQuery(CGameHandler * owner):
 
 }
 
-CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, int3 Tile):
-	CGhQuery(owner), visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false)
-{
-	addPlayer(Hero->tempOwner);
-}
-
-bool CObjectVisitQuery::blocksPack(const CPack *pack) const
-{
-	//During the visit itself ALL actions are blocked.
-	//(However, the visit may trigger a query above that'll pass some.)
-	return true;
-}
-
-void CObjectVisitQuery::onRemoval(PlayerColor color)
-{
-	gh->objectVisitEnded(*this);
-
-	//TODO or should it be destructor?
-	//Can object visit affect 2 players and what would be desired behavior?
-	if(removeObjectAfterVisit)
-		gh->removeObject(visitedObject);
-}
-
-void CObjectVisitQuery::onExposure(QueryPtr topQuery)
-{
-	//Object may have been removed and deleted.
-	if(gh->isValidObject(visitedObject))
-		topQuery->notifyObjectAboutRemoval(*this);
-
-	owner->popIfTop(*this);
-}
-
-void Queries::popQuery(PlayerColor player, QueryPtr query)
-{
-	LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
-	if(topQuery(player) != query)
-	{
-		logGlobal->trace("Cannot remove, not a top!");
-		return;
-	}
-
-	queries[player] -= query;
-	auto nextQuery = topQuery(player);
-
-	query->onRemoval(player);
-
-	//Exposure on query below happens only if removal didn't trigger any new query
-	if(nextQuery && nextQuery == topQuery(player))
-		nextQuery->onExposure(query);
-}
-
-void Queries::popQuery(const CQuery &query)
-{
-	LOG_TRACE_PARAMS(logGlobal, "query='%s'", query);
-
-	assert(query.players.size());
-	for(auto player : query.players)
-	{
-		auto top = topQuery(player);
-		if(top.get() == &query)
-			popQuery(top);
-		else
-		{
-			logGlobal->trace("Cannot remove query %s", query.toString());
-			logGlobal->trace("Queries found:");
-			for(auto q : queries[player])
-			{
-				logGlobal->trace(q->toString());
-			}
-		}
-	}
-}
-
-void Queries::popQuery(QueryPtr query)
-{
-	for(auto player : query->players)
-		popQuery(player, query);
-}
-
-void Queries::addQuery(QueryPtr query)
-{
-	for(auto player : query->players)
-		addQuery(player, query);
-
-	for(auto player : query->players)
-		query->onAdded(player);
-}
-
-void Queries::addQuery(PlayerColor player, QueryPtr query)
-{
-	LOG_TRACE_PARAMS(logGlobal, "player='%d', query='%s'", player.getNum() % query);
-	query->onAdding(player);
-	queries[player].push_back(query);
-}
-
-QueryPtr Queries::topQuery(PlayerColor player)
-{
-	return vstd::backOrNull(queries[player]);
-}
-
-void Queries::popIfTop(QueryPtr query)
-{
-	LOG_TRACE_PARAMS(logGlobal, "query='%d'", query);
-	if(!query)
-		logGlobal->error("The query is nullptr! Ignoring.");
-
-	popIfTop(*query);
-}
-
-void Queries::popIfTop(const CQuery & query)
-{
-	for(PlayerColor color : query.players)
-		if(topQuery(color).get() == &query)
-			popQuery(color, topQuery(color));
-}
-
-std::vector<std::shared_ptr<const CQuery>> Queries::allQueries() const
-{
-	std::vector<std::shared_ptr<const CQuery>> ret;
-	for(auto & playerQueries : queries)
-		for(auto & query : playerQueries.second)
-			ret.push_back(query);
-
-	return ret;
-}
-
-std::vector<QueryPtr> Queries::allQueries()
-{
-	//TODO code duplication with const function :(
-	std::vector<QueryPtr> ret;
-	for(auto & playerQueries : queries)
-		for(auto & query : playerQueries.second)
-			ret.push_back(query);
-
-	return ret;
-}
-
-QueryPtr Queries::getQuery(QueryID queryID)
-{
-	for(auto & playerQueries : queries)
-		for(auto & query : playerQueries.second)
-			if(query->queryID == queryID)
-				return query;
-	return nullptr;
-}
-
-void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	if(result)
-		objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
-}
-
-CBattleQuery::CBattleQuery(CGameHandler * owner, const BattleInfo * Bi):
-	CGhQuery(owner)
-{
-	belligerents[0] = Bi->sides[0].armyObject;
-	belligerents[1] = Bi->sides[1].armyObject;
-
-	bi = Bi;
-
-	for(auto & side : bi->sides)
-		addPlayer(side.color);
-}
-
-CBattleQuery::CBattleQuery(CGameHandler * owner):
-	CGhQuery(owner), bi(nullptr)
-{
-	belligerents[0] = belligerents[1] = nullptr;
-}
-
-bool CBattleQuery::blocksPack(const CPack * pack) const
-{
-	const char * name = typeid(*pack).name();
-	return strcmp(name, typeid(MakeAction).name()) && strcmp(name, typeid(MakeCustomAction).name());
-}
-
-void CBattleQuery::onRemoval(PlayerColor color)
-{
-	if(result)
-		gh->battles->battleAfterLevelUp(*result);
-}
-
-void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero);
-}
-
-CGarrisonDialogQuery::CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance * up, const CArmedInstance * down):
-	CDialogQuery(owner)
-{
-	exchangingArmies[0] = up;
-	exchangingArmies[1] = down;
-
-	addPlayer(up->tempOwner);
-	addPlayer(down->tempOwner);
-}
-
-bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
-{
-	std::set<ObjectInstanceID> ourIds;
-	ourIds.insert(this->exchangingArmies[0]->id);
-	ourIds.insert(this->exchangingArmies[1]->id);
-
-	if(auto stacks = dynamic_ptr_cast<ArrangeStacks>(pack))
-		return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2);
-
-	if(auto stacks = dynamic_ptr_cast<BulkSplitStack>(pack))
-		return !vstd::contains(ourIds, stacks->srcOwner);
-
-	if(auto stacks = dynamic_ptr_cast<BulkMergeStacks>(pack))
-		return !vstd::contains(ourIds, stacks->srcOwner);
-
-	if(auto stacks = dynamic_ptr_cast<BulkSmartSplitStack>(pack))
-		return !vstd::contains(ourIds, stacks->srcOwner);
-
-	if(auto stacks = dynamic_ptr_cast<BulkMoveArmy>(pack))
-		return !vstd::contains(ourIds, stacks->srcArmy) || !vstd::contains(ourIds, stacks->destArmy);
-
-	if(auto arts = dynamic_ptr_cast<ExchangeArtifacts>(pack))
-	{
-		if(auto id1 = std::visit(GetEngagedHeroIds(), arts->src.artHolder))
-			if(!vstd::contains(ourIds, *id1))
-				return true;
-
-		if(auto id2 = std::visit(GetEngagedHeroIds(), arts->dst.artHolder))
-			if(!vstd::contains(ourIds, *id2))
-				return true;
-		return false;
-	}
-	if(auto dismiss = dynamic_ptr_cast<DisbandCreature>(pack))
-		return !vstd::contains(ourIds, dismiss->id);
-	
-	if(auto arts = dynamic_ptr_cast<BulkExchangeArtifacts>(pack))
-		return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero);
-
-	if(auto art = dynamic_ptr_cast<EraseArtifactByClient>(pack))
-	{
-		if (auto id = std::visit(GetEngagedHeroIds(), art->al.artHolder))
-			return !vstd::contains(ourIds, *id);
-	}
-
-	if(auto dismiss = dynamic_ptr_cast<AssembleArtifacts>(pack))
-		return !vstd::contains(ourIds, dismiss->heroID);
-
-	if(auto upgrade = dynamic_ptr_cast<UpgradeCreature>(pack))
-		return !vstd::contains(ourIds, upgrade->id);
-
-	if(auto formation = dynamic_ptr_cast<SetFormation>(pack))
-		return !vstd::contains(ourIds, formation->hid);
-
-	return CDialogQuery::blocksPack(pack);
-}
-	
-CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
-	CDialogQuery(owner)
-{
-	bi = Bi;
-	
-	for(auto & side : bi->sides)
-		addPlayer(side.color);
-}
-
-void CBattleDialogQuery::onRemoval(PlayerColor color)
-{
-	assert(answer);
-	if(*answer == 1)
-	{
-		gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
-	}
-	else
-	{
-		gh->battles->endBattleConfirm(bi);
-	}
-}
-
-void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	assert(answer);
-	objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer);
-}
-
-CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog & bd):
-	CDialogQuery(owner)
-{
-	this->bd = bd;
-	addPlayer(bd.player);
-}
-
-void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	// do not change to dynamic_ptr_cast - SIGSEGV!
-	auto obj = dynamic_cast<const CGTeleport*>(objectVisit.visitedObject);
-	if(obj)
-		obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits);
-	else
-		logGlobal->error("Invalid instance in teleport query");
-}
-
-CTeleportDialogQuery::CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog & td):
-	CDialogQuery(owner)
-{
-	this->td = td;
-	addPlayer(td.player);
-}
-
-CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp & Hlu, const CGHeroInstance * Hero):
-	CDialogQuery(owner), hero(Hero)
-{
-	hlu = Hlu;
-	addPlayer(hero->tempOwner);
-}
-
-void CHeroLevelUpDialogQuery::onRemoval(PlayerColor color)
-{
-	assert(answer);
-	logGlobal->trace("Completing hero level-up query. %s gains skill %d", hero->getObjectName(), answer.value());
-	gh->levelUpHero(hero, hlu.skills[*answer]);
-}
-
-void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
-}
-
-CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp & Clu, const CGHeroInstance * Hero):
-	CDialogQuery(owner), hero(Hero)
-{
-	clu = Clu;
-	addPlayer(hero->tempOwner);
-}
-
-void CCommanderLevelUpDialogQuery::onRemoval(PlayerColor color)
-{
-	assert(answer);
-	logGlobal->trace("Completing commander level-up query. Commander of hero %s gains skill %s", hero->getObjectName(), answer.value());
-	gh->levelUpCommander(hero->commander, clu.skills[*answer]);
-}
-
-void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
-{
-	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
-}
-
 CDialogQuery::CDialogQuery(CGameHandler * owner):
 	CGhQuery(owner)
 {
@@ -515,47 +169,7 @@ void CDialogQuery::setReply(const JsonNode & reply)
 		answer = reply.Integer();
 }
 
-CHeroMovementQuery::CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory):
-	CGhQuery(owner), tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero)
-{
-	players.push_back(hero->tempOwner);
-}
-
-void CHeroMovementQuery::onExposure(QueryPtr topQuery)
-{
-	assert(players.size() == 1);
-
-	if(visitDestAfterVictory && hero->tempOwner == players[0]) //hero still alive, so he won with the guard
-		//TODO what if there were H4-like escape? we should also check pos
-	{
-		logGlobal->trace("Hero %s after victory over guard finishes visit to %s", hero->getNameTranslated(), tmh.end.toString());
-		//finish movement
-		visitDestAfterVictory = false;
-		gh->visitObjectOnTile(*gh->getTile(hero->convertToVisitablePos(tmh.end)), hero);
-	}
-
-	owner->popIfTop(*this);
-}
-
-void CHeroMovementQuery::onRemoval(PlayerColor color)
-{
-	PlayerBlocked pb;
-	pb.player = color;
-	pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
-	pb.startOrEnd = PlayerBlocked::BLOCKADE_ENDED;
-	gh->sendAndApply(&pb);
-}
-
-void CHeroMovementQuery::onAdding(PlayerColor color)
-{
-	PlayerBlocked pb;
-	pb.player = color;
-	pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
-	pb.startOrEnd = PlayerBlocked::BLOCKADE_STARTED;
-	gh->sendAndApply(&pb);
-}
-
-CGenericQuery::CGenericQuery(Queries * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback):
+CGenericQuery::CGenericQuery(QueriesProcessor * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback):
 	CQuery(Owner), callback(Callback)
 {
 	addPlayer(color);

+ 6 - 149
server/queries/CQuery.h

@@ -10,21 +10,17 @@
 #pragma once
 
 #include "../../lib/GameConstants.h"
-#include "../../lib/NetPacks.h"
+#include "../../lib/JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class JsonNode;
-class CGObjectInstance;
-class CGHeroInstance;
-class CArmedInstance;
+struct CPack;
 
 VCMI_LIB_NAMESPACE_END
 
-class CGameHandler;
 class CObjectVisitQuery;
+class QueriesProcessor;
 class CQuery;
-class Queries;
 
 using QueryPtr = std::shared_ptr<CQuery>;
 
@@ -42,7 +38,7 @@ public:
 	std::vector<PlayerColor> players; //players that are affected (often "blocked") by query
 	QueryID queryID;
 
-	CQuery(Queries * Owner);
+	CQuery(QueriesProcessor * Owner);
 
 
 	virtual bool blocksPack(const CPack *pack) const; //query can block attempting actions by player. Eg. he can't move hero during the battle.
@@ -60,7 +56,7 @@ public:
 
 	virtual ~CQuery();
 protected:
-	Queries * owner;
+	QueriesProcessor * owner;
 	void addPlayer(PlayerColor color);
 	bool blockAllButReply(const CPack * pack) const;
 };
@@ -76,55 +72,6 @@ protected:
 	CGameHandler * gh;
 };
 
-//Created when hero visits object.
-//Removed when query above is resolved (or immediately after visit if no queries were created)
-class CObjectVisitQuery : public CGhQuery
-{
-public:
-	const CGObjectInstance *visitedObject;
-	const CGHeroInstance *visitingHero;
-	int3 tile; //may be different than hero pos -> eg. visit via teleport
-	bool removeObjectAfterVisit;
-
-	CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile);
-
-	virtual bool blocksPack(const CPack *pack) const override;
-	virtual void onRemoval(PlayerColor color) override;
-	virtual void onExposure(QueryPtr topQuery) override;
-};
-
-class CBattleQuery : public CGhQuery
-{
-public:
-	std::array<const CArmedInstance *,2> belligerents;
-	std::array<int, 2> initialHeroMana;
-
-	const BattleInfo *bi;
-	std::optional<BattleResult> result;
-
-	CBattleQuery(CGameHandler * owner);
-	CBattleQuery(CGameHandler * owner, const BattleInfo * Bi); //TODO
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-	virtual bool blocksPack(const CPack *pack) const override;
-	virtual void onRemoval(PlayerColor color) override;
-};
-
-//Created when hero attempts move and something happens
-//(not necessarily position change, could be just an object interaction).
-class CHeroMovementQuery : public CGhQuery
-{
-public:
-	TryMoveHero tmh;
-	bool visitDestAfterVictory; //if hero moved to guarded tile and it should be visited once guard is defeated
-	const CGHeroInstance *hero;
-
-	virtual void onExposure(QueryPtr topQuery) override;
-
-	CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory = false);
-	virtual void onAdding(PlayerColor color) override;
-	virtual void onRemoval(PlayerColor color) override;
-};
-
 class CDialogQuery : public CGhQuery
 {
 public:
@@ -136,75 +83,10 @@ protected:
 	std::optional<ui32> answer;
 };
 
-class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange dialogs
-{
-public:
-	std::array<const CArmedInstance *,2> exchangingArmies;
-
-	CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance *up, const CArmedInstance *down);
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-	virtual bool blocksPack(const CPack *pack) const override;
-};
-
-class CBattleDialogQuery : public CDialogQuery
-{
-public:
-	CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi);
-
-	const BattleInfo * bi;
-
-	virtual void onRemoval(PlayerColor color) override;
-};
-
-//yes/no and component selection dialogs
-class CBlockingDialogQuery : public CDialogQuery
-{
-public:
-	BlockingDialog bd; //copy of pack... debug purposes
-
-	CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog &bd);
-
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-};
-
-class CTeleportDialogQuery : public CDialogQuery
-{
-public:
-	TeleportDialog td; //copy of pack... debug purposes
-
-	CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog &td);
-
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-};
-
-class CHeroLevelUpDialogQuery : public CDialogQuery
-{
-public:
-	CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp &Hlu, const CGHeroInstance * Hero);
-
-	virtual void onRemoval(PlayerColor color) override;
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-
-	HeroLevelUp hlu;
-	const CGHeroInstance * hero;
-};
-
-class CCommanderLevelUpDialogQuery : public CDialogQuery
-{
-public:
-	CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp &Clu, const CGHeroInstance * Hero);
-
-	virtual void onRemoval(PlayerColor color) override;
-	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
-
-	CommanderLevelUp clu;
-	const CGHeroInstance * hero;
-};
-
 class CGenericQuery : public CQuery
 {
 public:
-	CGenericQuery(Queries * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback);
+	CGenericQuery(QueriesProcessor * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback);
 
 	bool blocksPack(const CPack * pack) const override;
 	bool endsByPlayerAnswer() const override;
@@ -215,28 +97,3 @@ private:
 	std::function<void(const JsonNode &)> callback;
 	JsonNode reply;
 };
-
-class Queries
-{
-private:
-	void addQuery(PlayerColor player, QueryPtr query);
-	void popQuery(PlayerColor player, QueryPtr query);
-
-	std::map<PlayerColor, std::vector<QueryPtr>> queries; //player => stack of queries
-
-public:
-	static boost::mutex mx;
-
-	void addQuery(QueryPtr query);
-	void popQuery(const CQuery &query);
-	void popQuery(QueryPtr query);
-	void popIfTop(const CQuery &query); //removes this query if it is at the top (otherwise, do nothing)
-	void popIfTop(QueryPtr query); //removes this query if it is at the top (otherwise, do nothing)
-
-	QueryPtr topQuery(PlayerColor player);
-
-	std::vector<std::shared_ptr<const CQuery>> allQueries() const;
-	std::vector<QueryPtr> allQueries();
-	QueryPtr getQuery(QueryID queryID);
-	//void removeQuery
-};

+ 227 - 0
server/queries/MapQueries.cpp

@@ -0,0 +1,227 @@
+/*
+ * MapQueries.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "MapQueries.h"
+
+#include "QueriesProcessor.h"
+#include "../CGameHandler.h"
+#include "../../lib/mapObjects/MiscObjects.h"
+#include "../../lib/serializer/Cast.h"
+
+CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, int3 Tile):
+	CGhQuery(owner), visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false)
+{
+	addPlayer(Hero->tempOwner);
+}
+
+bool CObjectVisitQuery::blocksPack(const CPack *pack) const
+{
+	//During the visit itself ALL actions are blocked.
+	//(However, the visit may trigger a query above that'll pass some.)
+	return true;
+}
+
+void CObjectVisitQuery::onRemoval(PlayerColor color)
+{
+	gh->objectVisitEnded(*this);
+
+	//TODO or should it be destructor?
+	//Can object visit affect 2 players and what would be desired behavior?
+	if(removeObjectAfterVisit)
+		gh->removeObject(visitedObject);
+}
+
+void CObjectVisitQuery::onExposure(QueryPtr topQuery)
+{
+	//Object may have been removed and deleted.
+	if(gh->isValidObject(visitedObject))
+		topQuery->notifyObjectAboutRemoval(*this);
+
+	owner->popIfTop(*this);
+}
+
+void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero);
+}
+
+CGarrisonDialogQuery::CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance * up, const CArmedInstance * down):
+	CDialogQuery(owner)
+{
+	exchangingArmies[0] = up;
+	exchangingArmies[1] = down;
+
+	addPlayer(up->tempOwner);
+	addPlayer(down->tempOwner);
+}
+
+bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const
+{
+	std::set<ObjectInstanceID> ourIds;
+	ourIds.insert(this->exchangingArmies[0]->id);
+	ourIds.insert(this->exchangingArmies[1]->id);
+
+	if(auto stacks = dynamic_ptr_cast<ArrangeStacks>(pack))
+		return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2);
+
+	if(auto stacks = dynamic_ptr_cast<BulkSplitStack>(pack))
+		return !vstd::contains(ourIds, stacks->srcOwner);
+
+	if(auto stacks = dynamic_ptr_cast<BulkMergeStacks>(pack))
+		return !vstd::contains(ourIds, stacks->srcOwner);
+
+	if(auto stacks = dynamic_ptr_cast<BulkSmartSplitStack>(pack))
+		return !vstd::contains(ourIds, stacks->srcOwner);
+
+	if(auto stacks = dynamic_ptr_cast<BulkMoveArmy>(pack))
+		return !vstd::contains(ourIds, stacks->srcArmy) || !vstd::contains(ourIds, stacks->destArmy);
+
+	if(auto arts = dynamic_ptr_cast<ExchangeArtifacts>(pack))
+	{
+		if(auto id1 = std::visit(GetEngagedHeroIds(), arts->src.artHolder))
+			if(!vstd::contains(ourIds, *id1))
+				return true;
+
+		if(auto id2 = std::visit(GetEngagedHeroIds(), arts->dst.artHolder))
+			if(!vstd::contains(ourIds, *id2))
+				return true;
+		return false;
+	}
+	if(auto dismiss = dynamic_ptr_cast<DisbandCreature>(pack))
+		return !vstd::contains(ourIds, dismiss->id);
+
+	if(auto arts = dynamic_ptr_cast<BulkExchangeArtifacts>(pack))
+		return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero);
+
+	if(auto art = dynamic_ptr_cast<EraseArtifactByClient>(pack))
+	{
+		if (auto id = std::visit(GetEngagedHeroIds(), art->al.artHolder))
+			return !vstd::contains(ourIds, *id);
+	}
+
+	if(auto dismiss = dynamic_ptr_cast<AssembleArtifacts>(pack))
+		return !vstd::contains(ourIds, dismiss->heroID);
+
+	if(auto upgrade = dynamic_ptr_cast<UpgradeCreature>(pack))
+		return !vstd::contains(ourIds, upgrade->id);
+
+	if(auto formation = dynamic_ptr_cast<SetFormation>(pack))
+		return !vstd::contains(ourIds, formation->hid);
+
+	return CDialogQuery::blocksPack(pack);
+}
+
+void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	assert(answer);
+	objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer);
+}
+
+CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog & bd):
+	CDialogQuery(owner)
+{
+	this->bd = bd;
+	addPlayer(bd.player);
+}
+
+void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	// do not change to dynamic_ptr_cast - SIGSEGV!
+	auto obj = dynamic_cast<const CGTeleport*>(objectVisit.visitedObject);
+	if(obj)
+		obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits);
+	else
+		logGlobal->error("Invalid instance in teleport query");
+}
+
+CTeleportDialogQuery::CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog & td):
+	CDialogQuery(owner)
+{
+	this->td = td;
+	addPlayer(td.player);
+}
+
+CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp & Hlu, const CGHeroInstance * Hero):
+	CDialogQuery(owner), hero(Hero)
+{
+	hlu = Hlu;
+	addPlayer(hero->tempOwner);
+}
+
+void CHeroLevelUpDialogQuery::onRemoval(PlayerColor color)
+{
+	assert(answer);
+	logGlobal->trace("Completing hero level-up query. %s gains skill %d", hero->getObjectName(), answer.value());
+	gh->levelUpHero(hero, hlu.skills[*answer]);
+}
+
+void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
+}
+
+CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp & Clu, const CGHeroInstance * Hero):
+	CDialogQuery(owner), hero(Hero)
+{
+	clu = Clu;
+	addPlayer(hero->tempOwner);
+}
+
+void CCommanderLevelUpDialogQuery::onRemoval(PlayerColor color)
+{
+	assert(answer);
+	logGlobal->trace("Completing commander level-up query. Commander of hero %s gains skill %s", hero->getObjectName(), answer.value());
+	gh->levelUpCommander(hero->commander, clu.skills[*answer]);
+}
+
+void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
+{
+	objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
+}
+
+CHeroMovementQuery::CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory):
+	CGhQuery(owner), tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero)
+{
+	players.push_back(hero->tempOwner);
+}
+
+void CHeroMovementQuery::onExposure(QueryPtr topQuery)
+{
+	assert(players.size() == 1);
+
+	if(visitDestAfterVictory && hero->tempOwner == players[0]) //hero still alive, so he won with the guard
+		//TODO what if there were H4-like escape? we should also check pos
+	{
+		logGlobal->trace("Hero %s after victory over guard finishes visit to %s", hero->getNameTranslated(), tmh.end.toString());
+		//finish movement
+		visitDestAfterVictory = false;
+		gh->visitObjectOnTile(*gh->getTile(hero->convertToVisitablePos(tmh.end)), hero);
+	}
+
+	owner->popIfTop(*this);
+}
+
+void CHeroMovementQuery::onRemoval(PlayerColor color)
+{
+	PlayerBlocked pb;
+	pb.player = color;
+	pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
+	pb.startOrEnd = PlayerBlocked::BLOCKADE_ENDED;
+	gh->sendAndApply(&pb);
+}
+
+void CHeroMovementQuery::onAdding(PlayerColor color)
+{
+	PlayerBlocked pb;
+	pb.player = color;
+	pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
+	pb.startOrEnd = PlayerBlocked::BLOCKADE_STARTED;
+	gh->sendAndApply(&pb);
+}

+ 103 - 0
server/queries/MapQueries.h

@@ -0,0 +1,103 @@
+/*
+ * MapQueries.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "CQuery.h"
+
+#include "../../lib/NetPacks.h"
+
+//Created when hero visits object.
+//Removed when query above is resolved (or immediately after visit if no queries were created)
+class CObjectVisitQuery : public CGhQuery
+{
+public:
+	const CGObjectInstance *visitedObject;
+	const CGHeroInstance *visitingHero;
+	int3 tile; //may be different than hero pos -> eg. visit via teleport
+	bool removeObjectAfterVisit;
+
+	CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile);
+
+	virtual bool blocksPack(const CPack *pack) const override;
+	virtual void onRemoval(PlayerColor color) override;
+	virtual void onExposure(QueryPtr topQuery) override;
+};
+
+//Created when hero attempts move and something happens
+//(not necessarily position change, could be just an object interaction).
+class CHeroMovementQuery : public CGhQuery
+{
+public:
+	TryMoveHero tmh;
+	bool visitDestAfterVictory; //if hero moved to guarded tile and it should be visited once guard is defeated
+	const CGHeroInstance *hero;
+
+	virtual void onExposure(QueryPtr topQuery) override;
+
+	CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory = false);
+	virtual void onAdding(PlayerColor color) override;
+	virtual void onRemoval(PlayerColor color) override;
+};
+
+
+class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange dialogs
+{
+public:
+	std::array<const CArmedInstance *,2> exchangingArmies;
+
+	CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance *up, const CArmedInstance *down);
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+	virtual bool blocksPack(const CPack *pack) const override;
+};
+
+//yes/no and component selection dialogs
+class CBlockingDialogQuery : public CDialogQuery
+{
+public:
+	BlockingDialog bd; //copy of pack... debug purposes
+
+	CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog &bd);
+
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+};
+
+class CTeleportDialogQuery : public CDialogQuery
+{
+public:
+	TeleportDialog td; //copy of pack... debug purposes
+
+	CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog &td);
+
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+};
+
+class CHeroLevelUpDialogQuery : public CDialogQuery
+{
+public:
+	CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp &Hlu, const CGHeroInstance * Hero);
+
+	virtual void onRemoval(PlayerColor color) override;
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+
+	HeroLevelUp hlu;
+	const CGHeroInstance * hero;
+};
+
+class CCommanderLevelUpDialogQuery : public CDialogQuery
+{
+public:
+	CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp &Clu, const CGHeroInstance * Hero);
+
+	virtual void onRemoval(PlayerColor color) override;
+	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
+
+	CommanderLevelUp clu;
+	const CGHeroInstance * hero;
+};

+ 129 - 0
server/queries/QueriesProcessor.cpp

@@ -0,0 +1,129 @@
+/*
+ * CQuery.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "QueriesProcessor.h"
+
+#include "CQuery.h"
+
+boost::mutex QueriesProcessor::mx;
+
+void QueriesProcessor::popQuery(PlayerColor player, QueryPtr query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
+	if(topQuery(player) != query)
+	{
+		logGlobal->trace("Cannot remove, not a top!");
+		return;
+	}
+
+	queries[player] -= query;
+	auto nextQuery = topQuery(player);
+
+	query->onRemoval(player);
+
+	//Exposure on query below happens only if removal didn't trigger any new query
+	if(nextQuery && nextQuery == topQuery(player))
+		nextQuery->onExposure(query);
+}
+
+void QueriesProcessor::popQuery(const CQuery &query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "query='%s'", query);
+
+	assert(query.players.size());
+	for(auto player : query.players)
+	{
+		auto top = topQuery(player);
+		if(top.get() == &query)
+			popQuery(top);
+		else
+		{
+			logGlobal->trace("Cannot remove query %s", query.toString());
+			logGlobal->trace("Queries found:");
+			for(auto q : queries[player])
+			{
+				logGlobal->trace(q->toString());
+			}
+		}
+	}
+}
+
+void QueriesProcessor::popQuery(QueryPtr query)
+{
+	for(auto player : query->players)
+		popQuery(player, query);
+}
+
+void QueriesProcessor::addQuery(QueryPtr query)
+{
+	for(auto player : query->players)
+		addQuery(player, query);
+
+	for(auto player : query->players)
+		query->onAdded(player);
+}
+
+void QueriesProcessor::addQuery(PlayerColor player, QueryPtr query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "player='%d', query='%s'", player.getNum() % query);
+	query->onAdding(player);
+	queries[player].push_back(query);
+}
+
+QueryPtr QueriesProcessor::topQuery(PlayerColor player)
+{
+	return vstd::backOrNull(queries[player]);
+}
+
+void QueriesProcessor::popIfTop(QueryPtr query)
+{
+	LOG_TRACE_PARAMS(logGlobal, "query='%d'", query);
+	if(!query)
+		logGlobal->error("The query is nullptr! Ignoring.");
+
+	popIfTop(*query);
+}
+
+void QueriesProcessor::popIfTop(const CQuery & query)
+{
+	for(PlayerColor color : query.players)
+		if(topQuery(color).get() == &query)
+			popQuery(color, topQuery(color));
+}
+
+std::vector<std::shared_ptr<const CQuery>> QueriesProcessor::allQueries() const
+{
+	std::vector<std::shared_ptr<const CQuery>> ret;
+	for(auto & playerQueries : queries)
+		for(auto & query : playerQueries.second)
+			ret.push_back(query);
+
+	return ret;
+}
+
+std::vector<QueryPtr> QueriesProcessor::allQueries()
+{
+	//TODO code duplication with const function :(
+	std::vector<QueryPtr> ret;
+	for(auto & playerQueries : queries)
+		for(auto & query : playerQueries.second)
+			ret.push_back(query);
+
+	return ret;
+}
+
+QueryPtr QueriesProcessor::getQuery(QueryID queryID)
+{
+	for(auto & playerQueries : queries)
+		for(auto & query : playerQueries.second)
+			if(query->queryID == queryID)
+				return query;
+	return nullptr;
+}

+ 40 - 0
server/queries/QueriesProcessor.h

@@ -0,0 +1,40 @@
+/*
+ * QueriesProcessor.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../../lib/GameConstants.h"
+
+class CQuery;
+using QueryPtr = std::shared_ptr<CQuery>;
+
+class QueriesProcessor
+{
+private:
+	void addQuery(PlayerColor player, QueryPtr query);
+	void popQuery(PlayerColor player, QueryPtr query);
+
+	std::map<PlayerColor, std::vector<QueryPtr>> queries; //player => stack of queries
+
+public:
+	static boost::mutex mx;
+
+	void addQuery(QueryPtr query);
+	void popQuery(const CQuery &query);
+	void popQuery(QueryPtr query);
+	void popIfTop(const CQuery &query); //removes this query if it is at the top (otherwise, do nothing)
+	void popIfTop(QueryPtr query); //removes this query if it is at the top (otherwise, do nothing)
+
+	QueryPtr topQuery(PlayerColor player);
+
+	std::vector<std::shared_ptr<const CQuery>> allQueries() const;
+	std::vector<QueryPtr> allQueries();
+	QueryPtr getQuery(QueryID queryID);
+	//void removeQuery
+};