Quellcode durchsuchen

Mod compatibility check is now in a separate class and not part of
ModHandler

Ivan Savenko vor 2 Jahren
Ursprung
Commit
eb167d94a6

+ 4 - 0
client/mapView/mapHandler.cpp

@@ -74,6 +74,10 @@ std::string CMapHandler::getTerrainDescr(const int3 & pos, bool rightClick) cons
 
 bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b)
 {
+	//FIXME: Optimize
+	// this method is called A LOT on game start and some parts, e.g. for loops are too slow for that
+
+	assert(a && b);
 	if(!a)
 		return true;
 	if(!b)

+ 3 - 0
cmake_modules/VCMI_lib.cmake

@@ -116,6 +116,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/MapFormatJson.cpp
 		${MAIN_LIB_DIR}/mapping/ObstacleProxy.cpp
 
+		${MAIN_LIB_DIR}/modding/ActiveModsInSaveList.cpp
 		${MAIN_LIB_DIR}/modding/CModHandler.cpp
 		${MAIN_LIB_DIR}/modding/CModInfo.cpp
 		${MAIN_LIB_DIR}/modding/CModVersion.cpp
@@ -466,6 +467,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/MapFormatJson.h
 		${MAIN_LIB_DIR}/mapping/ObstacleProxy.h
 
+		${MAIN_LIB_DIR}/modding/ActiveModsInSaveList.h
 		${MAIN_LIB_DIR}/modding/CModHandler.h
 		${MAIN_LIB_DIR}/modding/CModInfo.h
 		${MAIN_LIB_DIR}/modding/CModVersion.h
@@ -474,6 +476,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/modding/ModIncompatibility.h
 		${MAIN_LIB_DIR}/modding/ModScope.h
 		${MAIN_LIB_DIR}/modding/ModUtility.h
+		${MAIN_LIB_DIR}/modding/ModVerificationInfo.h
 
 		${MAIN_LIB_DIR}/networkPacks/ArtifactLocation.h
 		${MAIN_LIB_DIR}/networkPacks/BattleChanges.h

+ 9 - 0
lib/IGameCallback.cpp

@@ -40,6 +40,7 @@
 #include "modding/CModInfo.h"
 #include "modding/IdentifierStorage.h"
 #include "modding/CModVersion.h"
+#include "modding/ActiveModsInSaveList.h"
 #include "CPlayerState.h"
 #include "GameSettings.h"
 #include "ScriptHandler.h"
@@ -183,6 +184,7 @@ void CPrivilegedInfoCallback::loadCommonState(Loader & in)
 
 	CMapHeader dum;
 	StartInfo * si = nullptr;
+	ActiveModsInSaveList activeMods;
 
 	logGlobal->info("\tReading header");
 	in.serializer & dum;
@@ -190,6 +192,9 @@ void CPrivilegedInfoCallback::loadCommonState(Loader & in)
 	logGlobal->info("\tReading options");
 	in.serializer & si;
 
+	logGlobal->info("\tReading mod list");
+	in.serializer & activeMods;
+
 	logGlobal->info("\tReading gamestate");
 	in.serializer & gs;
 }
@@ -197,12 +202,16 @@ void CPrivilegedInfoCallback::loadCommonState(Loader & in)
 template<typename Saver>
 void CPrivilegedInfoCallback::saveCommonState(Saver & out) const
 {
+	ActiveModsInSaveList activeMods;
+
 	logGlobal->info("Saving lib part of game...");
 	out.putMagicBytes(SAVEGAME_MAGIC);
 	logGlobal->info("\tSaving header");
 	out.serializer & static_cast<CMapHeader&>(*gs->map);
 	logGlobal->info("\tSaving options");
 	out.serializer & gs->scenarioOps;
+	logGlobal->info("\tSaving mod list");
+	out.serializer & activeMods;
 	logGlobal->info("\tSaving gamestate");
 	out.serializer & gs;
 }

+ 1 - 1
lib/mapping/CMapHeader.h

@@ -20,7 +20,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class CGObjectInstance;
 enum class EMapFormat : uint8_t;
-using ModCompatibilityInfo = std::map<std::string, CModInfo::VerificationInfo>;
+using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
 
 /// The hero name struct consists of the hero id and the hero name.
 struct DLL_LINKAGE SHeroName

+ 1 - 1
lib/mapping/CMapService.h

@@ -23,7 +23,7 @@ class CInputStream;
 class IMapLoader;
 class IMapPatcher;
 
-using ModCompatibilityInfo = std::map<std::string, CModInfo::VerificationInfo>;
+using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
 
 /**
  * The map service provides loading of VCMI/H3 map files. It can

+ 1 - 1
lib/mapping/MapFormatJson.cpp

@@ -966,7 +966,7 @@ void CMapLoaderJson::readHeader(const bool complete)
 	{
 		for(auto & mod : header["mods"].Vector())
 		{
-			CModInfo::VerificationInfo info;
+			ModVerificationInfo info;
 			info.version = CModVersion::fromString(mod["version"].String());
 			info.checksum = mod["checksum"].Integer();
 			info.name = mod["name"].String();

+ 112 - 0
lib/modding/ActiveModsInSaveList.cpp

@@ -0,0 +1,112 @@
+/*
+ * ActiveModsInSaveList.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ActiveModsInSaveList.h"
+
+#include "../VCMI_Lib.h"
+#include "CModInfo.h"
+#include "CModHandler.h"
+#include "ModIncompatibility.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+std::vector<TModID> ActiveModsInSaveList::getActiveMods()
+{
+	return VLC->modh->getActiveMods();
+}
+
+const ModVerificationInfo & ActiveModsInSaveList::getVerificationInfo(TModID mod)
+{
+	return VLC->modh->getModInfo(mod).getVerificationInfo();
+}
+
+void ActiveModsInSaveList::verifyActiveMods(const std::vector<std::pair<TModID, ModVerificationInfo>> & modList)
+{
+	auto searchVerificationInfo = [&modList](const TModID & m) -> const ModVerificationInfo*
+	{
+		for(auto & i : modList)
+			if(i.first == m)
+				return &i.second;
+		return nullptr;
+	};
+
+	std::vector<TModID> missingMods, excessiveMods;
+	ModIncompatibility::ModListWithVersion missingModsResult;
+	ModIncompatibility::ModList excessiveModsResult;
+
+	for(const auto & m : VLC->modh->getActiveMods())
+	{
+		if(searchVerificationInfo(m))
+			continue;
+
+		//TODO: support actual disabling of these mods
+		if(VLC->modh->getModInfo(m).checkModGameplayAffecting())
+			excessiveMods.push_back(m);
+	}
+
+	for(const auto & infoPair : modList)
+	{
+		auto & remoteModId = infoPair.first;
+		auto & remoteModInfo = infoPair.second;
+
+		bool modAffectsGameplay = remoteModInfo.impactsGameplay;
+		//parent mod affects gameplay if child affects too
+		for(const auto & subInfoPair : modList)
+			modAffectsGameplay |= (subInfoPair.second.impactsGameplay && subInfoPair.second.parent == remoteModId);
+
+		if(!vstd::contains(VLC->modh->getAllMods(), remoteModId))
+		{
+			if(modAffectsGameplay)
+				missingMods.push_back(remoteModId); //mod is not installed
+			continue;
+		}
+
+		auto & localModInfo = VLC->modh->getModInfo(remoteModId).getVerificationInfo();
+		modAffectsGameplay |= VLC->modh->getModInfo(remoteModId).checkModGameplayAffecting();
+		bool modVersionCompatible = localModInfo.version.isNull()
+			|| remoteModInfo.version.isNull()
+			|| localModInfo.version.compatible(remoteModInfo.version);
+		bool modLocalyEnabled = vstd::contains(VLC->modh->getActiveMods(), remoteModId);
+
+		if(modVersionCompatible && modAffectsGameplay && modLocalyEnabled)
+			continue;
+
+		if(modAffectsGameplay)
+			missingMods.push_back(remoteModId); //incompatible mod impacts gameplay
+	}
+
+	//filter mods
+	for(auto & m : missingMods)
+	{
+		if(auto * vInfo = searchVerificationInfo(m))
+		{
+			assert(vInfo->parent != m);
+			if(!vInfo->parent.empty() && vstd::contains(missingMods, vInfo->parent))
+				continue;
+			missingModsResult.push_back({vInfo->name, vInfo->version.toString()});
+		}
+	}
+	for(auto & m : excessiveMods)
+	{
+		auto & vInfo = VLC->modh->getModInfo(m).getVerificationInfo();
+		assert(vInfo.parent != m);
+		if(!vInfo.parent.empty() && vstd::contains(excessiveMods, vInfo.parent))
+			continue;
+		excessiveModsResult.push_back(vInfo.name);
+	}
+
+	if(!missingModsResult.empty() || !excessiveModsResult.empty())
+		throw ModIncompatibility(missingModsResult, excessiveModsResult);
+
+	//TODO: support actual enabling of required mods
+}
+
+
+VCMI_LIB_NAMESPACE_END

+ 51 - 0
lib/modding/ActiveModsInSaveList.h

@@ -0,0 +1,51 @@
+/*
+ * ActiveModsInSaveList.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include "ModVerificationInfo.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ActiveModsInSaveList
+{
+	std::vector<TModID> getActiveMods();
+	const ModVerificationInfo & getVerificationInfo(TModID mod);
+
+	/// Checks whether provided mod list is compatible with current VLC and throws on failure
+	void verifyActiveMods(const std::vector<std::pair<TModID, ModVerificationInfo>> & modList);
+public:
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		if(h.saving)
+		{
+			std::vector<TModID> activeMods = getActiveMods();
+			h & activeMods;
+			for(const auto & m : activeMods)
+				h & getVerificationInfo(m);
+		}
+		else
+		{
+			std::vector<TModID> saveActiveMods;
+			h & saveActiveMods;
+
+			std::vector<std::pair<TModID, ModVerificationInfo>> saveModInfos(saveActiveMods.size());
+			for(int i = 0; i < saveActiveMods.size(); ++i)
+			{
+				saveModInfos[i].first = saveActiveMods[i];
+				h & saveModInfos[i].second;
+			}
+
+			verifyActiveMods(saveModInfos);
+		}
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 0 - 82
lib/modding/CModHandler.cpp

@@ -491,88 +491,6 @@ void CModHandler::afterLoad(bool onlyEssential)
 		std::fstream file(CResourceHandler::get()->getResourceName(ResourcePath("config/modSettings.json"))->c_str(), std::ofstream::out | std::ofstream::trunc);
 		file << modSettings.toJson();
 	}
-
-}
-
-void CModHandler::trySetActiveMods(const std::vector<std::pair<TModID, CModInfo::VerificationInfo>> & modList)
-{
-	auto searchVerificationInfo = [&modList](const TModID & m) -> const CModInfo::VerificationInfo*
-	{
-		for(auto & i : modList)
-			if(i.first == m)
-				return &i.second;
-		return nullptr;
-	};
-	
-	std::vector<TModID> missingMods, excessiveMods;
-	ModIncompatibility::ModListWithVersion missingModsResult;
-	ModIncompatibility::ModList excessiveModsResult;
-	
-	for(const auto & m : activeMods)
-	{
-		if(searchVerificationInfo(m))
-			continue;
-
-		//TODO: support actual disabling of these mods
-		if(getModInfo(m).checkModGameplayAffecting())
-			excessiveMods.push_back(m);
-	}
-	
-	for(const auto & infoPair : modList)
-	{
-		auto & remoteModId = infoPair.first;
-		auto & remoteModInfo = infoPair.second;
-		
-		bool modAffectsGameplay = remoteModInfo.impactsGameplay;
-		//parent mod affects gameplay if child affects too
-		for(const auto & subInfoPair : modList)
-			modAffectsGameplay |= (subInfoPair.second.impactsGameplay && subInfoPair.second.parent == remoteModId);
-		
-		if(!allMods.count(remoteModId))
-		{
-			if(modAffectsGameplay)
-				missingMods.push_back(remoteModId); //mod is not installed
-			continue;
-		}
-		
-		auto & localModInfo = getModInfo(remoteModId).getVerificationInfo();
-		modAffectsGameplay |= getModInfo(remoteModId).checkModGameplayAffecting();
-		bool modVersionCompatible = localModInfo.version.isNull()
-			|| remoteModInfo.version.isNull()
-			|| localModInfo.version.compatible(remoteModInfo.version);
-		bool modLocalyEnabled = vstd::contains(activeMods, remoteModId);
-		
-		if(modVersionCompatible && modAffectsGameplay && modLocalyEnabled)
-			continue;
-		
-		if(modAffectsGameplay)
-			missingMods.push_back(remoteModId); //incompatible mod impacts gameplay
-	}
-	
-	//filter mods
-	for(auto & m : missingMods)
-	{
-		if(auto * vInfo = searchVerificationInfo(m))
-		{
-			assert(vInfo->parent != m);
-			if(!vInfo->parent.empty() && vstd::contains(missingMods, vInfo->parent))
-				continue;
-			missingModsResult.push_back({vInfo->name, vInfo->version.toString()});
-		}
-	}
-	for(auto & m : excessiveMods)
-	{
-		auto & vInfo = getModInfo(m).getVerificationInfo();
-		assert(vInfo.parent != m);
-		if(!vInfo.parent.empty() && vstd::contains(excessiveMods, vInfo.parent))
-			continue;
-		excessiveModsResult.push_back(vInfo.name);
-	}
-	
-	if(!missingModsResult.empty() || !excessiveModsResult.empty())
-		throw ModIncompatibility(missingModsResult, excessiveModsResult);
-	
-	//TODO: support actual enabling of required mods
 }
 
 VCMI_LIB_NAMESPACE_END

+ 5 - 32
lib/modding/CModHandler.h

@@ -9,21 +9,22 @@
  */
 #pragma once
 
-#include "CModInfo.h"
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CModHandler;
 class CModIndentifier;
+class CModInfo;
+struct CModVersion;
 class JsonNode;
 class IHandlerBase;
 class CIdentifierStorage;
 class CContentHandler;
+struct ModVerificationInfo;
 class ResourcePath;
 
 using TModID = std::string;
 
-class DLL_LINKAGE CModHandler : boost::noncopyable
+class DLL_LINKAGE CModHandler final : boost::noncopyable
 {
 	std::map <TModID, CModInfo> allMods;
 	std::vector <TModID> activeMods;//active mods, in order in which they were loaded
@@ -50,9 +51,6 @@ class DLL_LINKAGE CModHandler : boost::noncopyable
 
 	CModVersion getModVersion(TModID modName) const;
 
-	/// Attempt to set active mods according to provided list of mods from save, throws on failure
-	void trySetActiveMods(const std::vector<std::pair<TModID, CModInfo::VerificationInfo>> & modList);
-
 public:
 	std::shared_ptr<CContentHandler> content; //(!)Do not serialize FIXME: make private
 
@@ -79,32 +77,7 @@ public:
 	void afterLoad(bool onlyEssential);
 
 	CModHandler();
-	virtual ~CModHandler();
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		if(h.saving)
-		{
-			h & activeMods;
-			for(const auto & m : activeMods)
-				h & getModInfo(m).getVerificationInfo();
-		}
-		else
-		{
-			loadMods();
-			std::vector<TModID> saveActiveMods;
-			h & saveActiveMods;
-			
-			std::vector<std::pair<TModID, CModInfo::VerificationInfo>> saveModInfos(saveActiveMods.size());
-			for(int i = 0; i < saveActiveMods.size(); ++i)
-			{
-				saveModInfos[i].first = saveActiveMods[i];
-				h & saveModInfos[i].second;
-			}
-
-			trySetActiveMods(saveModInfos);
-		}
-	}
+	~CModHandler();
 };
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/modding/CModInfo.cpp

@@ -177,7 +177,7 @@ bool CModInfo::checkModGameplayAffecting() const
 	return *modGameplayAffecting;
 }
 
-const CModInfo::VerificationInfo & CModInfo::getVerificationInfo() const
+const ModVerificationInfo & CModInfo::getVerificationInfo() const
 {
 	return verificationInfo;
 }

+ 3 - 33
lib/modding/CModInfo.h

@@ -10,12 +10,10 @@
 #pragma once
 
 #include "../JsonNode.h"
-#include "CModVersion.h"
+#include "ModVerificationInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-using TModID = std::string;
-
 class DLL_LINKAGE CModInfo
 {
 	/// cached result of checkModGameplayAffecting() call
@@ -30,34 +28,6 @@ public:
 		PASSED
 	};
 	
-	struct VerificationInfo
-	{
-		/// human-readable mod name
-		std::string name;
-		
-		/// version of the mod
-		CModVersion version;
-		
-		/// CRC-32 checksum of the mod
-		ui32 checksum = 0;
-		
-		/// parent mod ID, empty if root-level mod
-		TModID parent;
-		
-		/// for serialization purposes
-		bool impactsGameplay = true;
-		
-		template <typename Handler>
-		void serialize(Handler & h, const int v)
-		{
-			h & name;
-			h & version;
-			h & checksum;
-			h & parent;
-			h & impactsGameplay;
-		}
-	};
-
 	/// identifier, identical to name of folder with mod
 	std::string identifier;
 
@@ -94,7 +64,7 @@ public:
 	/// return true if this mod can affect gameplay, e.g. adds or modifies any game objects
 	bool checkModGameplayAffecting() const;
 	
-	const VerificationInfo & getVerificationInfo() const;
+	const ModVerificationInfo & getVerificationInfo() const;
 
 private:
 	/// true if mod is enabled by user, e.g. in Launcher UI
@@ -103,7 +73,7 @@ private:
 	/// true if mod can be loaded - compatible and has no missing deps
 	bool implicitlyEnabled;
 	
-	VerificationInfo verificationInfo;
+	ModVerificationInfo verificationInfo;
 
 	void loadLocalData(const JsonNode & data);
 };

+ 2 - 0
lib/modding/CModVersion.h

@@ -18,6 +18,8 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+using TModID = std::string;
+
 struct DLL_LINKAGE CModVersion
 {
 	static const int Any = -1;

+ 44 - 0
lib/modding/ModVerificationInfo.h

@@ -0,0 +1,44 @@
+/*
+ * ModVerificationInfo.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "CModVersion.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+struct ModVerificationInfo
+{
+	/// human-readable mod name
+	std::string name;
+
+	/// version of the mod
+	CModVersion version;
+
+	/// CRC-32 checksum of the mod
+	ui32 checksum = 0;
+
+	/// parent mod ID, empty if root-level mod
+	TModID parent;
+
+	/// for serialization purposes
+	bool impactsGameplay = true;
+
+	template <typename Handler>
+	void serialize(Handler & h, const int v)
+	{
+		h & name;
+		h & version;
+		h & checksum;
+		h & parent;
+		h & impactsGameplay;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 1
mapeditor/mapcontroller.h

@@ -16,7 +16,7 @@
 #include "../lib/modding/CModInfo.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
-using ModCompatibilityInfo = std::map<std::string, CModInfo::VerificationInfo>;
+using ModCompatibilityInfo = std::map<std::string, ModVerificationInfo>;
 class EditorObstaclePlacer;
 VCMI_LIB_NAMESPACE_END