From ec7e0466173b794534f140305680826357148eb0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 11 Jul 2023 20:29:44 +0300 Subject: [PATCH] Fix hero pool persistency between saves --- lib/gameState/TavernHeroesPool.h | 1 + server/CGameHandler.cpp | 7 +++++++ server/CGameHandler.h | 5 +++++ server/HeroPoolProcessor.cpp | 33 ++++++++++++++++++++------------ server/HeroPoolProcessor.h | 12 ++++++------ 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/gameState/TavernHeroesPool.h b/lib/gameState/TavernHeroesPool.h index 79bcccc8b..e77cfe499 100644 --- a/lib/gameState/TavernHeroesPool.h +++ b/lib/gameState/TavernHeroesPool.h @@ -64,6 +64,7 @@ public: { h & heroesPool; h & pavailable; + h & currentTavern; } }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a2e988392..c96f8d808 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -7242,3 +7242,10 @@ void CGameHandler::createObject(const int3 & visitablePosition, Obj type, int32_ no.targetPos = visitablePosition; sendAndApply(&no); } + +void CGameHandler::deserializationFix() +{ + //FIXME: pointer to GameHandler itself can't be deserialized at the moment since GameHandler is top-level entity in serialization + // restore any places that requires such pointer manually + heroPool->gameHandler = this; +} diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 7da768eaa..2d9f24c6e 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -99,6 +99,8 @@ class CGameHandler : public IGameCallback, public CBattleInfoCallback, public En std::shared_ptr> applier; std::unique_ptr battleThread; + void deserializationFix(); + public: std::unique_ptr heroPool; @@ -290,6 +292,9 @@ public: h & heroPool; h & getRandomGenerator(); + if (!h.saving) + deserializationFix(); + #if SCRIPTING_ENABLED JsonNode scriptsState; if(h.saving) diff --git a/server/HeroPoolProcessor.cpp b/server/HeroPoolProcessor.cpp index 2ad418822..08253531d 100644 --- a/server/HeroPoolProcessor.cpp +++ b/server/HeroPoolProcessor.cpp @@ -175,38 +175,43 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID & return true; } -std::set HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const +std::vector HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const { - std::set result; + std::vector result; const auto & hpool = gameHandler->gameState()->hpool; FactionID factionID = gameHandler->getPlayerSettings(player)->castle; for(auto & elem : hpool->unusedHeroesFromPool()) { + if (vstd::contains(result, elem.second->type->heroClass)) + continue; + bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); bool heroClassBanned = elem.second->type->heroClass->selectionProbability[factionID] == 0; if(heroAvailable && !heroClassBanned) - result.insert(elem.second->type->heroClass); + result.push_back(elem.second->type->heroClass); } return result; } -std::set HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const +std::vector HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const { - std::set result; + std::vector result; const auto & hpool = gameHandler->gameState()->hpool; for(auto & elem : hpool->unusedHeroesFromPool()) { + assert(!vstd::contains(result, elem.second)); + bool heroAvailable = hpool->isHeroAvailableFor(elem.first, player); bool heroClassMatches = elem.second->type->heroClass == heroClass; if(heroAvailable && heroClassMatches) - result.insert(elem.second); + result.push_back(elem.second); } return result; @@ -224,8 +229,8 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo const auto & hpool = gameHandler->gameState()->hpool; const auto & currentTavern = hpool->getHeroesFor(player); - std::set potentialClasses = findAvailableClassesFor(player); - std::set possibleClasses; + std::vector potentialClasses = findAvailableClassesFor(player); + std::vector possibleClasses; if(potentialClasses.empty()) { @@ -245,7 +250,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo if (hasSameClass) continue; - possibleClasses.insert(heroClass); + possibleClasses.push_back(heroClass); } if (possibleClasses.empty()) @@ -259,6 +264,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo totalWeight += heroClass->selectionProbability.at(factionID); int roll = getRandomGenerator(player).nextInt(totalWeight - 1); + for(const auto & heroClass : possibleClasses) { roll -= heroClass->selectionProbability.at(factionID); @@ -276,7 +282,7 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, const PlayerColor if(!heroClass) return nullptr; - std::set possibleHeroes = findAvailableHeroesFor(player, heroClass); + std::vector possibleHeroes = findAvailableHeroesFor(player, heroClass); assert(!possibleHeroes.empty()); if(possibleHeroes.empty()) @@ -288,7 +294,10 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, const PlayerColor CRandomGenerator & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player) { if (playerSeed.count(player) == 0) - playerSeed.emplace(player, CRandomGenerator(gameHandler->getRandomGenerator().nextInt())); + { + int seed = gameHandler->getRandomGenerator().nextInt(); + playerSeed.emplace(player, std::make_unique(seed)); + } - return playerSeed.at(player); + return *playerSeed.at(player); } diff --git a/server/HeroPoolProcessor.h b/server/HeroPoolProcessor.h index f92c401b4..3934ba225 100644 --- a/server/HeroPoolProcessor.h +++ b/server/HeroPoolProcessor.h @@ -26,23 +26,24 @@ class CGameHandler; class HeroPoolProcessor : boost::noncopyable { - CGameHandler * gameHandler; - /// per-player random generators - std::map playerSeed; + std::map> playerSeed; void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot); void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy); - std::set findAvailableClassesFor(const PlayerColor & player) const; - std::set findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const; + std::vector findAvailableClassesFor(const PlayerColor & player) const; + std::vector findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const; const CHeroClass * pickClassFor(bool isNative, const PlayerColor & player); CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player); CRandomGenerator & getRandomGenerator(const PlayerColor & player); + public: + CGameHandler * gameHandler; + HeroPoolProcessor(); HeroPoolProcessor(CGameHandler * gameHandler); @@ -55,7 +56,6 @@ public: template void serialize(Handler &h, const int version) { - h & gameHandler; h & playerSeed; } };