From 369e925af8b82add87aade53065d48f42089761c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 14 Mar 2023 15:59:33 +0200 Subject: [PATCH] Loading of translation mods is now skipped on language mismatch --- client/CMT.cpp | 2 +- launcher/modManager/cmodlist.cpp | 5 ++ launcher/modManager/cmodlist.h | 2 + launcher/modManager/cmodlistview_moc.cpp | 2 +- lib/CGeneralTextHandler.cpp | 5 +- lib/CModHandler.cpp | 82 +++++++++++------------- lib/CModHandler.h | 20 +++--- lib/VCMI_Lib.cpp | 16 +++-- lib/VCMI_Lib.h | 3 +- mapeditor/mainwindow.cpp | 1 - server/CVCMIServer.cpp | 1 - 11 files changed, 73 insertions(+), 66 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index 9e95e2966..13b31c89f 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -258,7 +258,7 @@ int main(int argc, char * argv[]) // Init filesystem and settings preinitDLL(::console); - settings.init(); + Settings session = settings.write["session"]; auto setSettingBool = [](std::string key, std::string arg) { Settings s = settings.write(vstd::split(key, "/")); diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index cfbddf2bd..cc52a8bb6 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -139,6 +139,11 @@ bool CModEntry::isValid() const return !localData.isEmpty() || !repository.isEmpty(); } +bool CModEntry::isTranslation() const +{ + return getBaseValue("modType").toString().toLower() == "translation"; +} + int CModEntry::getModStatus() const { int status = 0; diff --git a/launcher/modManager/cmodlist.h b/launcher/modManager/cmodlist.h index 1330ebfaf..b8a9276ae 100644 --- a/launcher/modManager/cmodlist.h +++ b/launcher/modManager/cmodlist.h @@ -62,6 +62,8 @@ public: bool isCompatible() const; // returns if has any data bool isValid() const; + // installed and enabled + bool isTranslation() const; // see ModStatus enum int getModStatus() const; diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index eef3c249b..679a8a5e8 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -847,7 +847,7 @@ QString CModListView::getTranslationModName(const QString & language) { auto mod = modModel->getMod(modName); - if (mod.getBaseValue("modType").toString().toLower() != "translation") + if (!mod.isTranslation()) continue; if (mod.getBaseValue("language").toString() != language) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index bc1b55f69..4ab579874 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -387,8 +387,6 @@ CGeneralTextHandler::CGeneralTextHandler(): znpc00 (*this, "vcmi.znpc00" ), // technically - wog qeModCommands (*this, "vcmi.quickExchange" ) { - detectInstallParameters(); - readToVector("core.vcdesc", "DATA/VCDESC.TXT" ); readToVector("core.lcdesc", "DATA/LCDESC.TXT" ); readToVector("core.tcommand", "DATA/TCOMMAND.TXT" ); @@ -605,16 +603,19 @@ std::string CGeneralTextHandler::getModLanguage(const std::string & modContext) std::string CGeneralTextHandler::getPreferredLanguage() { + assert(!settings["general"]["language"].String().empty()); return settings["general"]["language"].String(); } std::string CGeneralTextHandler::getInstalledLanguage() { + assert(!settings["session"]["language"].String().empty()); return settings["session"]["language"].String(); } std::string CGeneralTextHandler::getInstalledEncoding() { + assert(!settings["session"]["encoding"].String().empty()); return settings["session"]["encoding"].String(); } diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index be7af936c..7516bfc84 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -616,7 +616,8 @@ bool CModInfo::Version::isNull() const CModInfo::CModInfo(): checksum(0), - enabled(false), + explicitlyEnabled(false), + implicitlyEnabled(true), validation(PENDING) { @@ -629,7 +630,8 @@ CModInfo::CModInfo(std::string identifier,const JsonNode & local, const JsonNode dependencies(config["depends"].convertTo >()), conflicts(config["conflicts"].convertTo >()), checksum(0), - enabled(false), + explicitlyEnabled(false), + implicitlyEnabled(true), validation(PENDING), config(addMeta(config, identifier)) { @@ -654,7 +656,7 @@ JsonNode CModInfo::saveLocalData() const stream << std::noshowbase << std::hex << std::setw(8) << std::setfill('0') << checksum; JsonNode conf; - conf["active"].Bool() = enabled; + conf["active"].Bool() = explicitlyEnabled; conf["validated"].Bool() = validation != FAILED; conf["checksum"].String() = stream.str(); return conf; @@ -683,33 +685,52 @@ void CModInfo::updateChecksum(ui32 newChecksum) void CModInfo::loadLocalData(const JsonNode & data) { bool validated = false; - enabled = true; + implicitlyEnabled = true; + explicitlyEnabled = true; checksum = 0; if (data.getType() == JsonNode::JsonType::DATA_BOOL) { - enabled = data.Bool(); + explicitlyEnabled = data.Bool(); } if (data.getType() == JsonNode::JsonType::DATA_STRUCT) { - enabled = data["active"].Bool(); + explicitlyEnabled = data["active"].Bool(); validated = data["validated"].Bool(); checksum = strtol(data["checksum"].String().c_str(), nullptr, 16); } //check compatibility - bool wasEnabled = enabled; - enabled = enabled && (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin)); - enabled = enabled && (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion())); + implicitlyEnabled &= (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin)); + implicitlyEnabled &= (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion())); - if(wasEnabled && !enabled) + if(!implicitlyEnabled) logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name); - if (enabled) + if (boost::iequals(config["modType"].String(), "translation")) // compatibility code - mods use "Translation" type at the moment + { + if (baseLanguage != VLC->generaltexth->getPreferredLanguage()) + { + logGlobal->warn("Translation mod %s was not loaded: language mismatch!", name); + implicitlyEnabled = false; + } + } + + if (isEnabled()) validation = validated ? PASSED : PENDING; else validation = validated ? PASSED : FAILED; } +bool CModInfo::isEnabled() const +{ + return implicitlyEnabled && explicitlyEnabled; +} + +void CModInfo::setEnabled(bool on) +{ + explicitlyEnabled = on; +} + CModHandler::CModHandler() : content(std::make_shared()) { modules.COMMANDERS = false; @@ -820,36 +841,6 @@ bool CModHandler::hasCircularDependency(TModID modID, std::set currentL return false; } -bool CModHandler::checkDependencies(const std::vector & input) const -{ - for(const TModID & id : input) - { - const CModInfo & mod = allMods.at(id); - - for(const TModID & dep : mod.dependencies) - { - if(!vstd::contains(input, dep)) - { - logMod->error("Error: Mod %s requires missing %s!", mod.name, dep); - return false; - } - } - - for(const TModID & conflicting : mod.conflicts) - { - if(vstd::contains(input, conflicting)) - { - logMod->error("Error: Mod %s conflicts with %s!", mod.name, allMods.at(conflicting).name); - return false; - } - } - - if(hasCircularDependency(id)) - return false; - } - return true; -} - // Returned vector affects the resource loaders call order (see CFilesystemList::load). // The loaders call order matters when dependent mod overrides resources in its dependencies. std::vector CModHandler::validateAndSortDependencies(std::vector modsToResolve) const @@ -995,10 +986,10 @@ void CModHandler::loadOneMod(std::string modName, std::string parent, const Json mod.dependencies.insert(parent); allMods[modFullName] = mod; - if (mod.enabled && enableMods) + if (mod.isEnabled() && enableMods) activeMods.push_back(modFullName); - loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.enabled); + loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.isEnabled()); } } @@ -1087,6 +1078,8 @@ static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoade void CModHandler::loadModFilesystems() { + CGeneralTextHandler::detectInstallParameters(); + activeMods = validateAndSortDependencies(activeMods); coreMod.updateChecksum(calculateModChecksum(CModHandler::scopeBuiltin(), CResourceHandler::get(CModHandler::scopeBuiltin()))); @@ -1109,6 +1102,9 @@ TModID CModHandler::findResourceOrigin(const ResourceID & name) if(CResourceHandler::get("core")->existsResource(name)) return "core"; + if(CResourceHandler::get("mapEditor")->existsResource(name)) + return "core"; // Workaround for loading maps via map editor + assert(0); return ""; } diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 27dfd3836..a3953e7be 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -236,9 +236,6 @@ public: /// CRC-32 checksum of the mod ui32 checksum; - /// true if mod is enabled - bool enabled; - EValidationStatus validation; JsonNode config; @@ -249,10 +246,19 @@ public: JsonNode saveLocalData() const; void updateChecksum(ui32 newChecksum); + bool isEnabled() const; + void setEnabled(bool on); + static std::string getModDir(std::string name); static std::string getModFile(std::string name); private: + /// true if mod is enabled by user, e.g. in Launcher UI + bool explicitlyEnabled; + + /// true if mod can be loaded - compatible and has no missing deps + bool implicitlyEnabled; + void loadLocalData(const JsonNode & data); }; @@ -266,12 +272,6 @@ class DLL_LINKAGE CModHandler bool hasCircularDependency(TModID mod, std::set currentList = std::set ()) const; - //returns false if mod list is incorrect and prints error to console. Possible errors are: - // - missing dependency mod - // - conflicting mod in load order - // - circular dependencies - bool checkDependencies(const std::vector & input) const; - /** * 1. Set apart mods with resolved dependencies from mods which have unresolved dependencies * 2. Sort resolved mods using topological algorithm @@ -449,7 +449,7 @@ public: h & mver; if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver))) - allMods[m].enabled = true; + allMods[m].setEnabled(true); else missingMods.emplace_back(m, mver.toString()); } diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index 647ddce44..c7d2d85f2 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -18,6 +18,7 @@ #include "CHeroHandler.h" #include "mapObjects/CObjectHandler.h" #include "CTownHandler.h" +#include "CConfigHandler.h" #include "RoadHandler.h" #include "RiverHandler.h" #include "TerrainHandler.h" @@ -48,7 +49,9 @@ DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential, bool VLC = new LibClasses(); try { - VLC->loadFilesystem(onlyEssential, extractArchives); + VLC->loadFilesystem(extractArchives); + settings.init(); + VLC->loadModFilesystem(onlyEssential); } catch(...) { @@ -160,9 +163,8 @@ void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & } } -void LibClasses::loadFilesystem(bool onlyEssential, bool extractArchives) +void LibClasses::loadFilesystem(bool extractArchives) { - CStopWatch totalTime; CStopWatch loadTime; CResourceHandler::initialize(); @@ -170,15 +172,17 @@ void LibClasses::loadFilesystem(bool onlyEssential, bool extractArchives) CResourceHandler::load("config/filesystem.json", extractArchives); logGlobal->info("\tData loading: %d ms", loadTime.getDiff()); +} +void LibClasses::loadModFilesystem(bool onlyEssential) +{ + CStopWatch loadTime; modh = new CModHandler(); + modh->loadMods(onlyEssential); logGlobal->info("\tMod handler: %d ms", loadTime.getDiff()); - modh->loadMods(onlyEssential); modh->loadModFilesystems(); logGlobal->info("\tMod filesystems: %d ms", loadTime.getDiff()); - - logGlobal->info("Basic initialization: %d ms", totalTime.getDiff()); } static void logHandlerLoaded(const std::string & name, CStopWatch & timer) diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index 55e58712b..16a52e0eb 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -107,7 +107,8 @@ public: void clear(); //deletes all handlers and its data // basic initialization. should be called before init(). Can also extract original H3 archives - void loadFilesystem(bool onlyEssential, bool extractArchives = false); + void loadFilesystem(bool extractArchives); + void loadModFilesystem(bool onlyEssential); #if SCRIPTING_ENABLED void scriptsLoaded(); diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index bb05305a3..ce3150731 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -170,7 +170,6 @@ MainWindow::MainWindow(QWidget* parent) : //init preinitDLL(::console, false, extractionOptions.extractArchives); - settings.init(); // Initialize logging based on settings logConfig->configure(); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 3ce05505e..ce24bba5e 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -1117,7 +1117,6 @@ int main(int argc, const char * argv[]) boost::program_options::variables_map opts; handleCommandOptions(argc, argv, opts); preinitDLL(console); - settings.init(); logConfig.configure(); loadDLLClasses();