Browse Source

Merge pull request #5685 from kdmcser/crash_fix4

Fix crash caused by conflicting instanceName after loading game
Ivan Savenko 5 months ago
parent
commit
544adfd545
4 changed files with 49 additions and 4 deletions
  1. 31 0
      lib/mapping/CMap.cpp
  2. 12 1
      lib/mapping/CMap.h
  3. 3 2
      lib/mapping/MapFormatJson.cpp
  4. 3 1
      lib/serializer/ESerializationVersion.h

+ 31 - 0
lib/mapping/CMap.cpp

@@ -936,4 +936,35 @@ ObjectInstanceID CMap::allocateUniqueInstanceID()
 	return ObjectInstanceID(objects.size() - 1);
 }
 
+void CMap::parseUidCounter()
+{
+	int max_index = -1;
+	for (const auto& entry : instanceNames) {
+		const std::string& key = entry.first;
+		const size_t pos = key.find_last_of('_');
+
+		// Validate underscore position
+		if (pos == std::string::npos || pos + 1 >= key.size()) {
+			logGlobal->error("Instance name '%s' is not valid.", key);
+			continue;
+		}
+
+		const std::string index_part = key.substr(pos + 1);
+		try {
+			const int current_index = std::stoi(index_part);
+			max_index = std::max(max_index, current_index);
+		}
+		catch (const std::invalid_argument& e) {
+			logGlobal->error("Instance name %s contains non-numeric index part: %s", key, index_part);
+		}
+		catch (const std::out_of_range&) {
+			logGlobal->error("Instance name %s index part is overflow.", key);
+		}
+	}
+
+	// Directly set uidCounter using simplified logic
+	uidCounter = max_index + 1;  // Automatically 0 when max_index = -1
+}
+
+
 VCMI_LIB_NAMESPACE_END

+ 12 - 1
lib/mapping/CMap.h

@@ -278,12 +278,14 @@ public:
 	const IGameSettings & getSettings() const;
 
 	void saveCompatibilityStoreAllocatedArtifactID();
+	void parseUidCounter();
 
 private:
+
 	/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
 	boost::multi_array<TerrainTile, 3> terrain;
 
-	si32 uidCounter; //TODO: initialize when loading an old map
+	si32 uidCounter; 
 
 public:
 	template <typename Handler>
@@ -346,6 +348,15 @@ public:
 
 		h & instanceNames;
 		h & *gameSettings;
+		if (!h.hasFeature(Handler::Version::STORE_UID_COUNTER_IN_CMAP))
+		{
+			if (!h.saving)
+				parseUidCounter();
+		}
+		else
+		{
+			h & uidCounter;
+		}
 	}
 };
 

+ 3 - 2
lib/mapping/MapFormatJson.cpp

@@ -1086,14 +1086,14 @@ void CMapLoaderJson::MapObjectLoader::configure()
 				spellID = 0;
 			artID = ArtifactID::SPELL_SCROLL;
 		}
-		else if(art->ID  == Obj::ARTIFACT)
+		else if (art->ID == Obj::ARTIFACT || (art->ID >= Obj::RANDOM_ART && art->ID <= Obj::RANDOM_RELIC_ART))
 		{
 			//specific artifact
 			artID = art->getArtifactType();
 		}
 
 		art->setArtifactInstance(owner->map->createArtifact(artID, spellID.getNum()));
-	}
+ 	}
 
 	if(auto hero = std::dynamic_pointer_cast<CGHeroInstance>(instance))
 	{
@@ -1132,6 +1132,7 @@ void CMapLoaderJson::readObjects()
 
 		debugHeroesOnMap.insert(hero->getHeroTypeID());
 	}
+	map->parseUidCounter();
 }
 
 void CMapLoaderJson::readTranslations()

+ 3 - 1
lib/serializer/ESerializationVersion.h

@@ -38,8 +38,10 @@ enum class ESerializationVersion : int32_t
 	NO_RAW_POINTERS_IN_SERIALIZER, // large rework that removed all non-owning pointers from serializer
 	STACK_INSTANCE_EXPERIENCE_FIX, // stack experience is stored as total, not as average
 	STACK_INSTANCE_ARMY_FIX, // remove serialization of army that owns stack instance
+	STORE_UID_COUNTER_IN_CMAP,  // fix crash caused by conflicting instanceName after loading game
+
 	
-	CURRENT = STACK_INSTANCE_ARMY_FIX,
+	CURRENT = STORE_UID_COUNTER_IN_CMAP,
 };
 
 static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");