Преглед изворни кода

Merge pull request #1721 from vcmi/fix_rmg_infinite_loop

Fix rmg infinite loop
Andrii Danylchenko пре 2 година
родитељ
комит
aede3da74e
3 измењених фајлова са 65 додато и 17 уклоњено
  1. 52 16
      lib/rmg/CRmgTemplate.cpp
  2. 3 0
      lib/rmg/CRmgTemplate.h
  3. 10 1
      lib/rmg/Functions.cpp

+ 52 - 16
lib/rmg/CRmgTemplate.cpp

@@ -672,28 +672,64 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
 		afterLoad();
 }
 
+std::set<TerrainId> CRmgTemplate::inheritTerrainType(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
+{
+	if (iteration >= 50)
+	{
+		logGlobal->error("Infinite recursion for terrain types detected in template %s", name);
+		return std::set<TerrainId>();
+	}
+	if (zone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE)
+	{
+		iteration++;
+		const auto otherZone = zones.at(zone->getTerrainTypeLikeZone());
+		zone->setTerrainTypes(inheritTerrainType(otherZone, iteration));
+	}
+	return zone->getTerrainTypes();
+}
+
+std::map<TResource, ui16> CRmgTemplate::inheritMineTypes(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
+{
+	if (iteration >= 50)
+	{
+		logGlobal->error("Infinite recursion for mine types detected in template %s", name);
+		return std::map<TResource, ui16>();
+	}
+	if (zone->getMinesLikeZone() != ZoneOptions::NO_ZONE)
+	{
+		iteration++;
+		const auto otherZone = zones.at(zone->getMinesLikeZone());
+		zone->setMinesInfo(inheritMineTypes(otherZone, iteration));
+	}
+	return zone->getMinesInfo();
+}
+
+std::vector<CTreasureInfo> CRmgTemplate::inheritTreasureInfo(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
+{
+	if (iteration >= 50)
+	{
+		logGlobal->error("Infinite recursion for treasures detected in template %s", name);
+		return std::vector<CTreasureInfo>();
+	}
+	if (zone->getTreasureLikeZone() != ZoneOptions::NO_ZONE)
+	{
+		iteration++;
+		const auto otherZone = zones.at(zone->getTreasureLikeZone());
+		zone->setTreasureInfo(inheritTreasureInfo(otherZone, iteration));
+	}
+	return zone->getTreasureInfo();
+}
+
 void CRmgTemplate::afterLoad()
 {
 	for(auto & idAndZone : zones)
 	{
 		auto zone = idAndZone.second;
-		if(zone->getMinesLikeZone() != ZoneOptions::NO_ZONE)
-		{
-			const auto otherZone = zones.at(zone->getMinesLikeZone());
-			zone->setMinesInfo(otherZone->getMinesInfo());
-		}
 
-		if(zone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE)
-		{
-			const auto otherZone = zones.at(zone->getTerrainTypeLikeZone());
-			zone->setTerrainTypes(otherZone->getTerrainTypes());
-		}
-
-		if(zone->getTreasureLikeZone() != ZoneOptions::NO_ZONE)
-		{
-			const auto otherZone = zones.at(zone->getTreasureLikeZone());
-			zone->setTreasureInfo(otherZone->getTreasureInfo());
-		}
+		//Inherit properties recursively.
+		inheritTerrainType(zone);
+		inheritMineTypes(zone);
+		inheritTreasureInfo(zone);
 	}
 
 	for(const auto & connection : connections)

+ 3 - 0
lib/rmg/CRmgTemplate.h

@@ -238,6 +238,9 @@ private:
 	std::set<EWaterContent::EWaterContent> allowedWaterContent;
 
 	void afterLoad();
+	std::set<TerrainId> inheritTerrainType(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
+	std::map<TResource, ui16> inheritMineTypes(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
+	std::vector<CTreasureInfo> inheritTreasureInfo(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 	void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName);
 	void serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & value, const std::string & fieldName);
 };

+ 10 - 1
lib/rmg/Functions.cpp

@@ -134,7 +134,16 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 		}
 		else
 		{
-			zone.setTerrainType(*RandomGeneratorUtil::nextItem(zone.getTerrainTypes(), gen.rand));
+			auto terrainTypes = zone.getTerrainTypes();
+			if (terrainTypes.empty())
+			{
+				logGlobal->warn("No terrain types found, falling back to DIRT");
+				zone.setTerrainType(ETerrainId::DIRT);
+			}
+			else
+			{
+				zone.setTerrainType(*RandomGeneratorUtil::nextItem(terrainTypes, gen.rand));
+			}
 		}
 		
 		//Now, replace disallowed terrains on surface and in the underground