1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-28 23:06:24 +02:00

Fixed mod dependencies check on save loading to prevent crashes:

- mods that don't affect game objects can be ignored when loading save
- gameplay-affecting mods that were present in save must be active
- gameplay-affecting mods that were not in save must not be active
This commit is contained in:
Ivan Savenko 2023-08-14 23:37:16 +03:00
parent 5dc735494c
commit ef0cd0ba6e
2 changed files with 82 additions and 6 deletions

View File

@ -649,6 +649,48 @@ void CModInfo::updateChecksum(ui32 newChecksum)
}
}
bool CModInfo::checkModGameplayAffecting() const
{
if (modGameplayAffecting.has_value())
return *modGameplayAffecting;
static const std::vector<std::string> keysToTest = {
"heroClasses",
"artifacts",
"creatures",
"factions",
"objects",
"heroes",
"spells",
"skills",
"templates",
"scripts",
"battlefields",
"terrains",
"rivers",
"roads",
"obstacles"
};
ResourceID modFileResource(CModInfo::getModFile(identifier));
if(CResourceHandler::get("initial")->existsResource(modFileResource))
{
const JsonNode modConfig(modFileResource);
for (auto const & key : keysToTest)
{
if (!modConfig[key].isNull())
{
modGameplayAffecting = true;
return *modGameplayAffecting;
}
}
}
modGameplayAffecting = false;
return *modGameplayAffecting;
}
void CModInfo::loadLocalData(const JsonNode & data)
{
bool validated = false;

View File

@ -179,6 +179,10 @@ using TModID = std::string;
class DLL_LINKAGE CModInfo
{
/// cached result of checkModGameplayAffecting() call
/// Do not serialize - depends on local mod version, not server/save mod version
mutable std::optional<bool> modGameplayAffecting;
public:
enum EValidationStatus
{
@ -223,6 +227,9 @@ public:
JsonNode saveLocalData() const;
void updateChecksum(ui32 newChecksum);
/// return true if this mod can affect gameplay, e.g. adds or modifies any game objects
bool checkModGameplayAffecting() const;
bool isEnabled() const;
void setEnabled(bool on);
@ -351,19 +358,46 @@ public:
else
{
loadMods();
std::vector<TModID> saveActiveMods;
std::vector<TModID> newActiveMods;
h & newActiveMods;
h & saveActiveMods;
Incompatibility::ModList missingMods;
for(const auto & m : newActiveMods)
for(const auto & m : activeMods)
{
if (vstd::contains(saveActiveMods, m))
continue;
auto & modInfo = allMods.at(m);
if(modInfo.checkModGameplayAffecting())
missingMods.emplace_back(m, modInfo.version.toString());
}
for(const auto & m : saveActiveMods)
{
CModVersion mver;
h & mver;
if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
allMods[m].setEnabled(true);
else
if (allMods.count(m) == 0)
{
missingMods.emplace_back(m, mver.toString());
continue;
}
auto & modInfo = allMods.at(m);
bool modAffectsGameplay = modInfo.checkModGameplayAffecting();
bool modVersionCompatible = modInfo.version.isNull() || mver.isNull() || modInfo.version.compatible(mver);
bool modEnabledLocally = vstd::contains(activeMods, m);
bool modCanBeEnabled = modEnabledLocally && modVersionCompatible;
allMods[m].setEnabled(modCanBeEnabled);
if (modCanBeEnabled)
newActiveMods.push_back(m);
if (!modCanBeEnabled && modAffectsGameplay)
missingMods.emplace_back(m, mver.toString());
}