Ver Fonte

Fix hero pool persistency between saves

Ivan Savenko há 2 anos atrás
pai
commit
ec7e046617

+ 1 - 0
lib/gameState/TavernHeroesPool.h

@@ -64,6 +64,7 @@ public:
 	{
 		h & heroesPool;
 		h & pavailable;
+		h & currentTavern;
 	}
 };
 

+ 7 - 0
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;
+}

+ 5 - 0
server/CGameHandler.h

@@ -99,6 +99,8 @@ class CGameHandler : public IGameCallback, public CBattleInfoCallback, public En
 	std::shared_ptr<CApplier<CBaseForGHApply>> applier;
 	std::unique_ptr<boost::thread> battleThread;
 
+	void deserializationFix();
+
 public:
 	std::unique_ptr<HeroPoolProcessor> heroPool;
 
@@ -290,6 +292,9 @@ public:
 		h & heroPool;
 		h & getRandomGenerator();
 
+		if (!h.saving)
+			deserializationFix();
+
 #if SCRIPTING_ENABLED
 		JsonNode scriptsState;
 		if(h.saving)

+ 21 - 12
server/HeroPoolProcessor.cpp

@@ -175,38 +175,43 @@ bool HeroPoolProcessor::hireHero(const CGObjectInstance *obj, const HeroTypeID &
 	return true;
 }
 
-std::set<const CHeroClass *> HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const
+std::vector<const CHeroClass *> HeroPoolProcessor::findAvailableClassesFor(const PlayerColor & player) const
 {
-	std::set<const CHeroClass *> result;
+	std::vector<const CHeroClass *> 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<CGHeroInstance *> HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const
+std::vector<CGHeroInstance *> HeroPoolProcessor::findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const
 {
-	std::set<CGHeroInstance *> result;
+	std::vector<CGHeroInstance *> 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<const CHeroClass *> potentialClasses = findAvailableClassesFor(player);
-	std::set<const CHeroClass *> possibleClasses;
+	std::vector<const CHeroClass *> potentialClasses = findAvailableClassesFor(player);
+	std::vector<const CHeroClass *> 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<CGHeroInstance *> possibleHeroes = findAvailableHeroesFor(player, heroClass);
+	std::vector<CGHeroInstance *> 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<CRandomGenerator>(seed));
+	}
 
-	return playerSeed.at(player);
+	return *playerSeed.at(player);
 }

+ 6 - 6
server/HeroPoolProcessor.h

@@ -26,23 +26,24 @@ class CGameHandler;
 
 class HeroPoolProcessor : boost::noncopyable
 {
-	CGameHandler * gameHandler;
-
 	/// per-player random generators
-	std::map<PlayerColor, CRandomGenerator> playerSeed;
+	std::map<PlayerColor, std::unique_ptr<CRandomGenerator>> playerSeed;
 
 	void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
 	void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy);
 
-	std::set<const CHeroClass *> findAvailableClassesFor(const PlayerColor & player) const;
-	std::set<CGHeroInstance *> findAvailableHeroesFor(const PlayerColor & player, const CHeroClass * heroClass) const;
+	std::vector<const CHeroClass *> findAvailableClassesFor(const PlayerColor & player) const;
+	std::vector<CGHeroInstance *> 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 <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & gameHandler;
 		h & playerSeed;
 	}
 };