diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 36d5ab062..a32983423 100644 --- a/server/CGameHandler.cpp +++ b/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(this)) , battles(std::make_unique(this)) - , queries(std::make_unique()) + , queries(std::make_unique()) , playerMessages(std::make_unique(this)) , complainNoCreatures("No creatures to split") , complainNotEnoughCreatures("Cannot split that stack, not enough creatures!") diff --git a/server/CGameHandler.h b/server/CGameHandler.h index fd5c44677..1f2cc3637 100644 --- a/server/CGameHandler.h +++ b/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 heroPool; std::unique_ptr battles; - std::unique_ptr queries; + std::unique_ptr queries; //use enums as parameters, because doMove(sth, true, false, true) is not readable enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS}; diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 26050e6f6..ed7881e32 100644 --- a/server/CMakeLists.txt +++ b/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 diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index b46d82a5b..3a5db6dcb 100644 --- a/server/NetPacksServer.cpp +++ b/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" diff --git a/server/ServerSpellCastEnvironment.cpp b/server/ServerSpellCastEnvironment.cpp index f61007795..4f0fe8cba 100644 --- a/server/ServerSpellCastEnvironment.cpp +++ b/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) diff --git a/server/battles/BattleProcessor.cpp b/server/battles/BattleProcessor.cpp index f09a2f663..0b858213d 100644 --- a/server/battles/BattleProcessor.cpp +++ b/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(gameHandler->queries.topQuery(gameHandler->gameState()->curB->sides[0].color)); + auto lastBattleQuery = std::dynamic_pointer_cast(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(gameHandler->queries.topQuery(bs.info->sides[0].color)); + auto lastBattleQuery = std::dynamic_pointer_cast(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; diff --git a/server/processors/PlayerMessageProcessor.cpp b/server/processors/PlayerMessageProcessor.cpp index c9c0f3fd3..d077233c8 100644 --- a/server/processors/PlayerMessageProcessor.cpp +++ b/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" diff --git a/server/queries/BattleQueries.cpp b/server/queries/BattleQueries.cpp new file mode 100644 index 000000000..46d9ec7cb --- /dev/null +++ b/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); + } +} diff --git a/server/queries/BattleQueries.h b/server/queries/BattleQueries.h new file mode 100644 index 000000000..03fce57a7 --- /dev/null +++ b/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 belligerents; + std::array initialHeroMana; + + const BattleInfo *bi; + std::optional 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; +}; diff --git a/server/queries/CQuery.cpp b/server/queries/CQuery.cpp index 3e3c326aa..b8c1e072c 100644 --- a/server/queries/CQuery.cpp +++ b/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 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 l(Queries::mx); + boost::unique_lock 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> Queries::allQueries() const -{ - std::vector> ret; - for(auto & playerQueries : queries) - for(auto & query : playerQueries.second) - ret.push_back(query); - - return ret; -} - -std::vector Queries::allQueries() -{ - //TODO code duplication with const function :( - std::vector 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 ourIds; - ourIds.insert(this->exchangingArmies[0]->id); - ourIds.insert(this->exchangingArmies[1]->id); - - if(auto stacks = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2); - - if(auto stacks = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, stacks->srcOwner); - - if(auto stacks = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, stacks->srcOwner); - - if(auto stacks = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, stacks->srcOwner); - - if(auto stacks = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, stacks->srcArmy) || !vstd::contains(ourIds, stacks->destArmy); - - if(auto arts = dynamic_ptr_cast(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(pack)) - return !vstd::contains(ourIds, dismiss->id); - - if(auto arts = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero); - - if(auto art = dynamic_ptr_cast(pack)) - { - if (auto id = std::visit(GetEngagedHeroIds(), art->al.artHolder)) - return !vstd::contains(ourIds, *id); - } - - if(auto dismiss = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, dismiss->heroID); - - if(auto upgrade = dynamic_ptr_cast(pack)) - return !vstd::contains(ourIds, upgrade->id); - - if(auto formation = dynamic_ptr_cast(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(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 Callback): +CGenericQuery::CGenericQuery(QueriesProcessor * Owner, PlayerColor color, std::function Callback): CQuery(Owner), callback(Callback) { addPlayer(color); diff --git a/server/queries/CQuery.h b/server/queries/CQuery.h index 54497e460..0d105ca37 100644 --- a/server/queries/CQuery.h +++ b/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; @@ -42,7 +38,7 @@ public: std::vector 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 belligerents; - std::array initialHeroMana; - - const BattleInfo *bi; - std::optional 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 answer; }; -class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange dialogs -{ -public: - std::array 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 Callback); + CGenericQuery(QueriesProcessor * Owner, PlayerColor color, std::function Callback); bool blocksPack(const CPack * pack) const override; bool endsByPlayerAnswer() const override; @@ -215,28 +97,3 @@ private: std::function callback; JsonNode reply; }; - -class Queries -{ -private: - void addQuery(PlayerColor player, QueryPtr query); - void popQuery(PlayerColor player, QueryPtr query); - - std::map> 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> allQueries() const; - std::vector allQueries(); - QueryPtr getQuery(QueryID queryID); - //void removeQuery -}; diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp new file mode 100644 index 000000000..054f3526c --- /dev/null +++ b/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 ourIds; + ourIds.insert(this->exchangingArmies[0]->id); + ourIds.insert(this->exchangingArmies[1]->id); + + if(auto stacks = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2); + + if(auto stacks = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, stacks->srcOwner); + + if(auto stacks = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, stacks->srcOwner); + + if(auto stacks = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, stacks->srcOwner); + + if(auto stacks = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, stacks->srcArmy) || !vstd::contains(ourIds, stacks->destArmy); + + if(auto arts = dynamic_ptr_cast(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(pack)) + return !vstd::contains(ourIds, dismiss->id); + + if(auto arts = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero); + + if(auto art = dynamic_ptr_cast(pack)) + { + if (auto id = std::visit(GetEngagedHeroIds(), art->al.artHolder)) + return !vstd::contains(ourIds, *id); + } + + if(auto dismiss = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, dismiss->heroID); + + if(auto upgrade = dynamic_ptr_cast(pack)) + return !vstd::contains(ourIds, upgrade->id); + + if(auto formation = dynamic_ptr_cast(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(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); +} diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h new file mode 100644 index 000000000..c89be802d --- /dev/null +++ b/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 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; +}; diff --git a/server/queries/QueriesProcessor.cpp b/server/queries/QueriesProcessor.cpp new file mode 100644 index 000000000..259e33a3e --- /dev/null +++ b/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> QueriesProcessor::allQueries() const +{ + std::vector> ret; + for(auto & playerQueries : queries) + for(auto & query : playerQueries.second) + ret.push_back(query); + + return ret; +} + +std::vector QueriesProcessor::allQueries() +{ + //TODO code duplication with const function :( + std::vector 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; +} diff --git a/server/queries/QueriesProcessor.h b/server/queries/QueriesProcessor.h new file mode 100644 index 000000000..d0fd6df35 --- /dev/null +++ b/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; + +class QueriesProcessor +{ +private: + void addQuery(PlayerColor player, QueryPtr query); + void popQuery(PlayerColor player, QueryPtr query); + + std::map> 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> allQueries() const; + std::vector allQueries(); + QueryPtr getQuery(QueryID queryID); + //void removeQuery +};