浏览代码

Merge pull request #3193 from IvanSavenko/warn_mod_dependencies

Show message about mods that failed to load on opening main menu
Ivan Savenko 1 年之前
父节点
当前提交
de02507fff

+ 2 - 0
Mods/vcmi/config/vcmi/english.json

@@ -73,6 +73,8 @@
 	"vcmi.server.errors.modsToEnable"    : "{Following mods are required}",
 	"vcmi.server.errors.modsToDisable"   : "{Following mods must be disabled}",
 	"vcmi.server.confirmReconnect"       : "Do you want to reconnect to the last session?",
+	"vcmi.server.errors.modNoDependency" : "Failed to load mod {'%s'}!\n It depends on mod {'%s'} which is not active!\n",
+	"vcmi.server.errors.modConflict" : "Failed to load mod {'%s'}!\n Conflicts with active mod {'%s'}!\n",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "General",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Switches to General Options tab, which contains settings related to general game client behavior.",

+ 2 - 0
Mods/vcmi/config/vcmi/ukrainian.json

@@ -73,6 +73,8 @@
 	"vcmi.server.errors.modsToEnable"    : "{Потрібні модифікації для завантаження гри}",
 	"vcmi.server.errors.modsToDisable"   : "{Модифікації що мають бути вимкнені}",
 	"vcmi.server.confirmReconnect"       : "Підключитися до минулої сесії?",
+	"vcmi.server.errors.modNoDependency" : "Не вдалося увімкнути мод {'%s'}!\n Модифікація потребує мод {'%s'} який зараз не активний!\n",
+	"vcmi.server.errors.modConflict" : "Не вдалося увімкнути мод {'%s'}!\n Конфліктує з активним модом {'%s'}!\n",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Загальні",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Перемикає на вкладку загальних параметрів, яка містить налаштування, пов'язані із загальною поведінкою ігрового клієнта",

+ 12 - 0
client/mainmenu/CMainMenu.cpp

@@ -49,6 +49,7 @@
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/CCompressedStream.h"
 #include "../../lib/mapping/CMapInfo.h"
+#include "../../lib/modding/CModHandler.h"
 #include "../../lib/VCMIDirs.h"
 #include "../../lib/CStopWatch.h"
 #include "../../lib/CThreadHelper.h"
@@ -339,6 +340,17 @@ void CMainMenu::update()
 		menu->switchToTab(menu->getActiveTab());
 	}
 
+	static bool warnedAboutModDependencies = false;
+
+	if (!warnedAboutModDependencies)
+	{
+		warnedAboutModDependencies = true;
+		auto errorMessages = CGI->modh->getModLoadErrors();
+
+		if (!errorMessages.empty())
+			CInfoWindow::showInfoDialog(errorMessages, std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
+	}
+
 	// Handles mouse and key input
 	GH.handleEvents();
 	GH.windows().simpleRedraw();

+ 30 - 6
lib/modding/CModHandler.cpp

@@ -21,6 +21,7 @@
 #include "../CStopWatch.h"
 #include "../GameSettings.h"
 #include "../Languages.h"
+#include "../MetaString.h"
 #include "../ScriptHandler.h"
 #include "../constants/StringConstants.h"
 #include "../filesystem/Filesystem.h"
@@ -133,6 +134,24 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI
 		break;
 	}
 
+	modLoadErrors = std::make_unique<MetaString>();
+
+	auto addErrorMessage = [this](const std::string & textID, const std::string & brokenModID, const std::string & missingModID)
+	{
+		modLoadErrors->appendTextID(textID);
+
+		if (allMods.count(brokenModID))
+			modLoadErrors->replaceRawString(allMods.at(brokenModID).getVerificationInfo().name);
+		else
+			modLoadErrors->replaceRawString(brokenModID);
+
+		if (allMods.count(missingModID))
+			modLoadErrors->replaceRawString(allMods.at(missingModID).getVerificationInfo().name);
+		else
+			modLoadErrors->replaceRawString(missingModID);
+
+	};
+
 	// Left mods have unresolved dependencies, output all to log.
 	for(const auto & brokenModID : modsToResolve)
 	{
@@ -140,17 +159,17 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI
 		for(const TModID & dependency : brokenMod.dependencies)
 		{
 			if(!vstd::contains(resolvedModIDs, dependency) && brokenMod.config["modType"].String() != "Compatibility")
-				logMod->error("Mod '%s' has been disabled: dependency '%s' is missing.", brokenMod.getVerificationInfo().name, dependency);
+				addErrorMessage("vcmi.server.errors.modNoDependency", brokenModID, dependency);
 		}
 		for(const TModID & conflict : brokenMod.conflicts)
 		{
 			if(vstd::contains(resolvedModIDs, conflict))
-				logMod->error("Mod '%s' has been disabled: conflicts with enabled mod '%s'.", brokenMod.getVerificationInfo().name, conflict);
+				addErrorMessage("vcmi.server.errors.modConflict", brokenModID, conflict);
 		}
 		for(const TModID & reverseConflict : resolvedModIDs)
 		{
 			if (vstd::contains(allMods.at(reverseConflict).conflicts, brokenModID))
-				logMod->error("Mod '%s' has been disabled: conflicts with enabled mod '%s'.", brokenMod.getVerificationInfo().name, reverseConflict);
+				addErrorMessage("vcmi.server.errors.modConflict", brokenModID, reverseConflict);
 		}
 	}
 	return sortedValidMods;
@@ -235,7 +254,7 @@ void CModHandler::loadMods(bool onlyEssential)
 	coreMod = std::make_unique<CModInfo>(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json")));
 }
 
-std::vector<std::string> CModHandler::getAllMods()
+std::vector<std::string> CModHandler::getAllMods() const
 {
 	std::vector<std::string> modlist;
 	modlist.reserve(allMods.size());
@@ -244,11 +263,16 @@ std::vector<std::string> CModHandler::getAllMods()
 	return modlist;
 }
 
-std::vector<std::string> CModHandler::getActiveMods()
+std::vector<std::string> CModHandler::getActiveMods() const
 {
 	return activeMods;
 }
 
+std::string CModHandler::getModLoadErrors() const
+{
+	return modLoadErrors->toString();
+}
+
 const CModInfo & CModHandler::getModInfo(const TModID & modId) const
 {
 	return allMods.at(modId);
@@ -320,7 +344,7 @@ void CModHandler::loadModFilesystems()
 	}
 }
 
-TModID CModHandler::findResourceOrigin(const ResourcePath & name)
+TModID CModHandler::findResourceOrigin(const ResourcePath & name) const
 {
 	for(const auto & modID : boost::adaptors::reverse(activeMods))
 	{

+ 8 - 3
lib/modding/CModHandler.h

@@ -21,6 +21,7 @@ class CIdentifierStorage;
 class CContentHandler;
 struct ModVerificationInfo;
 class ResourcePath;
+class MetaString;
 
 using TModID = std::string;
 
@@ -29,6 +30,7 @@ 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
 	std::unique_ptr<CModInfo> coreMod;
+	mutable std::unique_ptr<MetaString> modLoadErrors;
 
 	bool hasCircularDependency(const TModID & mod, std::set<TModID> currentList = std::set<TModID>()) const;
 
@@ -60,15 +62,18 @@ public:
 	void loadModFilesystems();
 
 	/// returns ID of mod that provides selected file resource
-	TModID findResourceOrigin(const ResourcePath & name);
+	TModID findResourceOrigin(const ResourcePath & name) const;
 
 	std::string getModLanguage(const TModID & modId) const;
 
 	std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
 
 	/// returns list of all (active) mods
-	std::vector<std::string> getAllMods();
-	std::vector<std::string> getActiveMods();
+	std::vector<std::string> getAllMods() const;
+	std::vector<std::string> getActiveMods() const;
+
+	/// Returns human-readable string that describes erros encounter during mod loading, such as missing dependencies
+	std::string getModLoadErrors() const;
 	
 	const CModInfo & getModInfo(const TModID & modId) const;