Ver Fonte

Verifying mods before starting map

nordsoft há 2 anos atrás
pai
commit
954a2abb71

+ 9 - 0
client/lobby/CLobbyScreen.cpp

@@ -131,6 +131,15 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
 		CSH->sendStartGame(allowOnlyAI);
 		buttonStart->block(true);
 	}
+	catch(CModHandler::Incompatibility & e)
+	{
+		logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
+		
+		auto errorMsg = VLC->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';
+		errorMsg += e.what();
+		
+		CInfoWindow::showInfoDialog(errorMsg, CInfoWindow::TCompsInfo(), PlayerColor(1));
+	}
 	catch(std::exception & e)
 	{
 		logGlobal->error("Exception during startScenario: %s", e.what());

+ 5 - 0
lib/CModHandler.cpp

@@ -967,6 +967,11 @@ std::vector<std::string> CModHandler::getActiveMods()
 	return activeMods;
 }
 
+const CModInfo & CModHandler::getModInfo(const TModID & modId) const
+{
+	return allMods.at(modId);
+}
+
 static JsonNode genDefaultFS()
 {
 	// default FS config for mods: directory "Content" that acts as H3 root directory

+ 3 - 1
lib/CModHandler.h

@@ -174,7 +174,7 @@ public:
 	const ContentTypeHandler & operator[] (const std::string & name) const;
 };
 
-typedef std::string TModID;
+using TModID = std::string;
 
 class DLL_LINKAGE CModInfo
 {
@@ -347,6 +347,8 @@ public:
 	/// returns list of all (active) mods
 	std::vector<std::string> getAllMods();
 	std::vector<std::string> getActiveMods();
+	
+	const CModInfo & getModInfo(const TModID & modId) const;
 
 	/// load content from all available mods
 	void load();

+ 10 - 1
lib/StartInfo.cpp

@@ -15,6 +15,7 @@
 #include "mapping/CMapInfo.h"
 #include "mapping/CCampaignHandler.h"
 #include "mapping/CMap.h"
+#include "mapping/CMapService.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -67,8 +68,16 @@ std::string StartInfo::getCampaignName() const
 
 void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
 {
-	if(!mi)
+	if(!mi || !mi->mapHeader)
 		throw std::domain_error("ExceptionMapMissing");
+	
+	auto missingMods = CMapService::verifyMapHeaderMods(*mi->mapHeader);
+	CModHandler::Incompatibility::ModList modList;
+	for(const auto & m : missingMods)
+		modList.push_back({m.first, m.second.toString()});
+	
+	if(!modList.empty())
+		throw CModHandler::Incompatibility(std::move(modList));
 
 	//there must be at least one human player before game can be started
 	std::map<PlayerColor, PlayerSettings>::const_iterator i;

+ 5 - 1
lib/mapping/CMap.h

@@ -275,6 +275,10 @@ enum EMapFormat: ui8
 };
 }
 
+// Inherit from container to enable forward declaration
+class ModCompatibilityInfo: public std::map<TModID, CModInfo::Version>
+{};
+
 /// The map header holds information about loss/victory condition,map format, version, players, height, width,...
 class DLL_LINKAGE CMapHeader
 {
@@ -295,7 +299,7 @@ public:
 	ui8 levels() const;
 
 	EMapFormat::EMapFormat version; /// The default value is EMapFormat::SOD.
-	std::map<TModID, CModInfo::Version> mods; /// set of mods required to play a map
+	ModCompatibilityInfo mods; /// set of mods required to play a map
 	
 	si32 height; /// The default value is 72.
 	si32 width; /// The default value is 72.

+ 18 - 0
lib/mapping/CMapService.cpp

@@ -86,6 +86,24 @@ void CMapService::saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::
 	}
 }
 
+ModCompatibilityInfo CMapService::verifyMapHeaderMods(const CMapHeader & map)
+{
+	ModCompatibilityInfo modCompatibilityInfo;
+	const auto & activeMods = VLC->modh->getActiveMods();
+	for(const auto & mapMod : map.mods)
+	{
+		if(vstd::contains(activeMods, mapMod.first))
+		{
+			const auto & modInfo = VLC->modh->getModInfo(mapMod.first);
+			if(modInfo.version.compatible(mapMod.second))
+				continue;
+		}
+		
+		modCompatibilityInfo[mapMod.first] = mapMod.second;
+	}	
+	return modCompatibilityInfo;
+}
+
 std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const ResourceID & name)
 {
 	return CResourceHandler::get()->load(name);

+ 10 - 0
lib/mapping/CMapService.h

@@ -21,6 +21,8 @@ class CInputStream;
 class IMapLoader;
 class IMapPatcher;
 
+class ModCompatibilityInfo;
+
 /**
  * The map service provides loading and saving of VCMI/H3 map files.
  */
@@ -74,7 +76,15 @@ public:
 	 */
 	std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const;
 	
+	/**
+	 * Tests if mods used in the map are currently loaded
+	 * @param map const reference to map header
+	 * @return data structure representing missing or incompatible mods (those which are needed from map but not loaded)
+	 */
+	static ModCompatibilityInfo verifyMapHeaderMods(const CMapHeader & map);
+	
 	void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath) const;
+	
 private:
 	/**
 	 * Gets a map input stream object specified by a map name.