浏览代码

Redesign mod incompatibility message

nordsoft 2 年之前
父节点
当前提交
dce1ac1538

+ 3 - 3
Mods/vcmi/config/vcmi/chinese.json

@@ -30,9 +30,9 @@
 	"vcmi.capitalColors.6" : "褐色",
 	"vcmi.capitalColors.7" : "粉色",
 
-	"vcmi.server.errors.existingProcess"     : "一个VCMI进程已经在运行,启动新进程前请结束它。",
-	"vcmi.server.errors.modsIncompatibility" : "需要加载的MOD列表:",
-	"vcmi.server.confirmReconnect"           : "您想要重连上一个会话么?",
+	"vcmi.server.errors.existingProcess" : "一个VCMI进程已经在运行,启动新进程前请结束它。",
+	"vcmi.server.errors.modsToEnable"    : "{需要加载的MOD列表}",
+	"vcmi.server.confirmReconnect"       : "您想要重连上一个会话么?",
 
 	"vcmi.settingsMainWindow.generalTab.hover"   : "常规",
 	"vcmi.settingsMainWindow.generalTab.help"    : "切换到“常规”选项卡 - 设置游戏客户端呈现",

+ 3 - 3
Mods/vcmi/config/vcmi/czech.json

@@ -48,9 +48,9 @@
 	"vcmi.lobby.filename" : "Název souboru",
 	"vcmi.lobby.creationDate" : "Datum vytvoření",
 
-	"vcmi.server.errors.existingProcess"     : "Již běží jiný server VCMI. Prosím, ukončete ho před startem nové hry.",
-	"vcmi.server.errors.modsIncompatibility" : "Následující modifikace jsou nutné pro načtení hry:",
-	"vcmi.server.confirmReconnect"           : "Chcete se připojit k poslední relaci?",
+	"vcmi.server.errors.existingProcess" : "Již běží jiný server VCMI. Prosím, ukončete ho před startem nové hry.",
+	"vcmi.server.errors.modsToEnable"    : "{Následující modifikace jsou nutné pro načtení hry}",
+	"vcmi.server.confirmReconnect"       : "Chcete se připojit k poslední relaci?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Obecné",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Přepne na kartu obecných nastavení, která obsahuje nastavení související s obecným chováním klienta hry",

+ 4 - 3
Mods/vcmi/config/vcmi/english.json

@@ -53,9 +53,10 @@
 	"vcmi.lobby.filename" : "Filename",
 	"vcmi.lobby.creationDate" : "Creation date",
 
-	"vcmi.server.errors.existingProcess"     : "Another VCMI server process is running. Please terminate it before starting a new game.",
-	"vcmi.server.errors.modsIncompatibility" : "The following mods are required to load the game:",
-	"vcmi.server.confirmReconnect"           : "Do you want to reconnect to the last session?",
+	"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
+	"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.settingsMainWindow.generalTab.hover" : "General",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Switches to General Options tab, which contains settings related to general game client behavior.",

+ 3 - 3
Mods/vcmi/config/vcmi/french.json

@@ -38,9 +38,9 @@
 	"vcmi.mainMenu.joinTCP" : "Rejoindre TCP/IP jeu",
 	"vcmi.mainMenu.playerName" : "Joueur",
 
-	"vcmi.server.errors.existingProcess"     : "Un autre processus de serveur VCMI est en cours d'exécution. Veuillez l'arrêter' avant de démarrer un nouveau jeu.",
-	"vcmi.server.errors.modsIncompatibility" : "Les mods suivants sont nécessaires pour charger le jeu :",
-	"vcmi.server.confirmReconnect"           : "Voulez-vous vous reconnecter à la dernière session ?",
+	"vcmi.server.errors.existingProcess" : "Un autre processus de serveur VCMI est en cours d'exécution. Veuillez l'arrêter' avant de démarrer un nouveau jeu.",
+	"vcmi.server.errors.modsToEnable"    : "{Les mods suivants sont nécessaires pour charger le jeu}",
+	"vcmi.server.confirmReconnect"       : "Voulez-vous vous reconnecter à la dernière session ?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Général",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Passe à l'onglet Options générales, qui contient des paramètres liés au comportement général du client de jeu",

+ 3 - 3
Mods/vcmi/config/vcmi/german.json

@@ -52,9 +52,9 @@
 	"vcmi.lobby.filename" : "Dateiname",
 	"vcmi.lobby.creationDate" : "Erstellungsdatum",
 
-	"vcmi.server.errors.existingProcess"     : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
-	"vcmi.server.errors.modsIncompatibility" : "Erforderliche Mods um das Spiel zu laden:",
-	"vcmi.server.confirmReconnect"           : "Mit der letzten Sitzung verbinden?",
+	"vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
+	"vcmi.server.errors.modsToEnable"    : "{Erforderliche Mods um das Spiel zu laden}",
+	"vcmi.server.confirmReconnect"       : "Mit der letzten Sitzung verbinden?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Allgemein",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Wechselt zur Registerkarte Allgemeine Optionen, die Einstellungen zum allgemeinen Verhalten des Spielclients enthält.",

+ 3 - 3
Mods/vcmi/config/vcmi/polish.json

@@ -47,9 +47,9 @@
 	"vcmi.lobby.filename" : "Nazwa pliku",
 	"vcmi.lobby.creationDate" : "Data utworzenia",
 
-	"vcmi.server.errors.existingProcess"     : "Inny proces 'vcmiserver' został już uruchomiony, zakończ go nim przejdziesz dalej",
-	"vcmi.server.errors.modsIncompatibility" : "Następujące mody są wymagane do wczytania gry:",
-	"vcmi.server.confirmReconnect"           : "Połączyć ponownie z ostatnią sesją?",
+	"vcmi.server.errors.existingProcess" : "Inny proces 'vcmiserver' został już uruchomiony, zakończ go nim przejdziesz dalej",
+	"vcmi.server.errors.modsToEnable"    : "{Następujące mody są wymagane do wczytania gry}",
+	"vcmi.server.confirmReconnect"       : "Połączyć ponownie z ostatnią sesją?",
 
 	"vcmi.settingsMainWindow.generalTab.hover"   : "Ogólne",
 	"vcmi.settingsMainWindow.generalTab.help"    : "Przełącza do zakładki opcji ogólnych, która zawiera ustawienia związane z ogólnym działaniem gry",

+ 3 - 3
Mods/vcmi/config/vcmi/russian.json

@@ -21,9 +21,9 @@
 	"vcmi.adventureMap.moveCostDetails"        : "Очки движения - Стоимость: %TURNS ходов + %POINTS очков, Останется: %REMAINING очков",
 	"vcmi.adventureMap.moveCostDetailsNoTurns" : "Очки движения - Стоимость: %POINTS очков, Останется: %REMAINING очков",
 
-	"vcmi.server.errors.existingProcess"     : "Запущен другой процесс vcmiserver, сначала завершите его.",
-	"vcmi.server.errors.modsIncompatibility" : "Требуемые моды для загрузки игры:",
-	"vcmi.server.confirmReconnect"          : "Подключиться к предыдущей сессии?",
+	"vcmi.server.errors.existingProcess" : "Запущен другой процесс vcmiserver, сначала завершите его.",
+	"vcmi.server.errors.modsToEnable"    : "{Требуемые моды для загрузки игры}",
+	"vcmi.server.confirmReconnect"       : "Подключиться к предыдущей сессии?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Общее",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Переключиться на вкладку \"Общее\", содержащее общие настройки клиента игры",

+ 3 - 3
Mods/vcmi/config/vcmi/spanish.json

@@ -30,9 +30,9 @@
 	"vcmi.capitalColors.6" : "Turquesa",
 	"vcmi.capitalColors.7" : "Rosa",
 
-	"vcmi.server.errors.existingProcess"     : "Otro proceso de vcmiserver está en ejecución, por favor termínalo primero",
-	"vcmi.server.errors.modsIncompatibility" : "Mods necesarios para cargar el juego:",
-	"vcmi.server.confirmReconnect"           : "¿Conectar a la última sesión?",
+	"vcmi.server.errors.existingProcess" : "Otro proceso de vcmiserver está en ejecución, por favor termínalo primero",
+	"vcmi.server.errors.modsToEnable"    : "{Mods necesarios para cargar el juego}",
+	"vcmi.server.confirmReconnect"       : "¿Conectar a la última sesión?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "General",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Cambiar a la pestaña de opciones generales, que contiene ajustes relacionados con el comportamiento general del juego",

+ 3 - 3
Mods/vcmi/config/vcmi/ukrainian.json

@@ -48,9 +48,9 @@
 	"vcmi.lobby.filename" : "Назва файлу",
 	"vcmi.lobby.creationDate" : "Дата створення",
 
-	"vcmi.server.errors.existingProcess"     : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його",
-	"vcmi.server.errors.modsIncompatibility" : "Потрібні модифікації для завантаження гри:",
-	"vcmi.server.confirmReconnect"           : "Підключитися до минулої сесії?",
+	"vcmi.server.errors.existingProcess" : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його",
+	"vcmi.server.errors.modsToEnable"    : "{Потрібні модифікації для завантаження гри}",
+	"vcmi.server.confirmReconnect"       : "Підключитися до минулої сесії?",
 
 	"vcmi.settingsMainWindow.generalTab.hover" : "Загальні",
 	"vcmi.settingsMainWindow.generalTab.help"     : "Перемикає на вкладку загальних параметрів, яка містить налаштування, пов'язані із загальною поведінкою ігрового клієнта",

+ 11 - 4
client/CServerHandler.cpp

@@ -552,10 +552,17 @@ bool CServerHandler::validateGameStart(bool allowOnlyAI) const
 	catch(ModIncompatibility & e)
 	{
 		logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
-
-		auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';
-		errorMsg += e.what();
-
+		std::string errorMsg;
+		if(!e.whatMissing().empty())
+		{
+			errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToEnable") + '\n';
+			errorMsg += e.whatMissing();
+		}
+		if(!e.whatExcessive().empty())
+		{
+			errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToDisable") + '\n';
+			errorMsg += e.whatExcessive();
+		}
 		showServerError(errorMsg);
 		return false;
 	}

+ 0 - 1
client/lobby/CLobbyScreen.cpp

@@ -29,7 +29,6 @@
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/mapping/CMapInfo.h"
-#include "../../lib/modding/ModIncompatibility.h"
 #include "../../lib/rmg/CMapGenOptions.h"
 
 CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)

+ 2 - 2
lib/StartInfo.cpp

@@ -74,12 +74,12 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
 		throw std::domain_error(VLC->generaltexth->translate("core.genrltxt.529"));
 	
 	auto missingMods = CMapService::verifyMapHeaderMods(*mi->mapHeader);
-	ModIncompatibility::ModList modList;
+	ModIncompatibility::ModListWithVersion modList;
 	for(const auto & m : missingMods)
 		modList.push_back({m.second.name, m.second.version.toString()});
 	
 	if(!modList.empty())
-		throw ModIncompatibility(std::move(modList));
+		throw ModIncompatibility(modList);
 
 	//there must be at least one human player before game can be started
 	std::map<PlayerColor, PlayerSettings>::const_iterator i;

+ 17 - 5
lib/modding/CModHandler.cpp

@@ -481,6 +481,10 @@ void CModHandler::trySetActiveMods(const std::vector<std::pair<TModID, CModInfo:
 		return nullptr;
 	};
 	
+	std::vector<TModID> newActiveMods, missingMods, excessiveMods;
+	ModIncompatibility::ModListWithVersion missingModsResult;
+	ModIncompatibility::ModList excessiveModsResult;
+	
 	for(const auto & m : activeMods)
 	{
 		if(searchVerificationInfo(m))
@@ -488,11 +492,11 @@ void CModHandler::trySetActiveMods(const std::vector<std::pair<TModID, CModInfo:
 
 		//TODO: support actual disabling of these mods
 		if(getModInfo(m).checkModGameplayAffecting())
+		{
+			excessiveMods.push_back(m);
 			allMods[m].setEnabled(false);
+		}
 	}
-
-	std::vector<TModID> newActiveMods, missingMods;
-	ModIncompatibility::ModList missingModsResult;
 	
 	for(const auto & infoPair : modList)
 	{
@@ -539,9 +543,17 @@ void CModHandler::trySetActiveMods(const std::vector<std::pair<TModID, CModInfo:
 			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())
-		throw ModIncompatibility(std::move(missingModsResult));
+	if(!missingModsResult.empty() || !excessiveModsResult.empty())
+		throw ModIncompatibility(missingModsResult, excessiveModsResult);
 	
 	//TODO: support actual enabling of these mods
 	for(auto & m : newActiveMods)

+ 28 - 13
lib/modding/ModIncompatibility.h

@@ -14,29 +14,44 @@ VCMI_LIB_NAMESPACE_BEGIN
 class DLL_LINKAGE ModIncompatibility: public std::exception
 {
 public:
-	using StringPair = std::pair<const std::string, const std::string>;
-	using ModList = std::list<StringPair>;
+	using ModListWithVersion = std::vector<std::pair<const std::string, const std::string>>;
+	using ModList = std::vector<const std::string>;
 
-	ModIncompatibility(ModList && _missingMods):
-		missingMods(std::move(_missingMods))
+	ModIncompatibility(const ModListWithVersion & _missingMods)
 	{
 		std::ostringstream _ss;
-		for(const auto & m : missingMods)
+		for(const auto & m : _missingMods)
 			_ss << m.first << ' ' << m.second << std::endl;
-		message = _ss.str();
+		messageMissingMods = _ss.str();
 	}
-
+	
+	ModIncompatibility(const ModListWithVersion & _missingMods, ModList & _excessiveMods)
+		: ModIncompatibility(_missingMods)
+	{
+		std::ostringstream _ss;
+		for(const auto & m : _excessiveMods)
+			_ss << m << std::endl;
+		messageExcessiveMods = _ss.str();
+	}
+	
 	const char * what() const noexcept override
 	{
-		return message.c_str();
+		static const std::string w("Mod incompatibility exception");
+		return w.c_str();
+	}
+	
+	const std::string & whatMissing() const noexcept
+	{
+		return messageMissingMods;
+	}
+	
+	const std::string & whatExcessive() const noexcept
+	{
+		return messageExcessiveMods;
 	}
 
 private:
-	//list of mods required to load the game
-	// first: mod name
-	// second: mod version
-	const ModList missingMods;
-	std::string message;
+	std::string messageMissingMods, messageExcessiveMods;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 4 - 3
mapeditor/mainwindow.cpp

@@ -336,19 +336,20 @@ bool MainWindow::openMap(const QString & filenameSelect)
 		if(auto header = mapService.loadMapHeader(resId))
 		{
 			auto missingMods = CMapService::verifyMapHeaderMods(*header);
-			ModIncompatibility::ModList modList;
+			ModIncompatibility::ModListWithVersion modList;
 			for(const auto & m : missingMods)
 				modList.push_back({m.second.name, m.second.version.toString()});
 			
 			if(!modList.empty())
-				throw ModIncompatibility(std::move(modList));
+				throw ModIncompatibility(modList);
 			
 			controller.setMap(mapService.loadMap(resId));
 		}
 	}
 	catch(const ModIncompatibility & e)
 	{
-		QMessageBox::warning(this, "Mods requiered", e.what());
+		assert(e.whatExcessive().empty());
+		QMessageBox::warning(this, "Mods are requiered", QString::fromStdString(e.whatMissing()));
 		return false;
 	}
 	catch(const std::exception & e)

+ 11 - 2
server/CGameHandler.cpp

@@ -1765,8 +1765,17 @@ bool CGameHandler::load(const std::string & filename)
 	catch(const ModIncompatibility & e)
 	{
 		logGlobal->error("Failed to load game: %s", e.what());
-		auto errorMsg = VLC->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';
-		errorMsg += e.what();
+		std::string errorMsg;
+		if(!e.whatMissing().empty())
+		{
+			errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToEnable") + '\n';
+			errorMsg += e.whatMissing();
+		}
+		if(!e.whatExcessive().empty())
+		{
+			errorMsg += VLC->generaltexth->translate("vcmi.server.errors.modsToDisable") + '\n';
+			errorMsg += e.whatExcessive();
+		}
 		lobby->announceMessage(errorMsg);
 		return false;
 	}