mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-14 10:12:59 +02:00
Implemented enabling and disabling of mods with dependencies resolving
This commit is contained in:
parent
37e975036c
commit
2fcda48c65
@ -446,8 +446,10 @@ void CModListView::selectMod(const QModelIndex & index)
|
|||||||
Helper::enableScrollBySwiping(ui->modInfoBrowser);
|
Helper::enableScrollBySwiping(ui->modInfoBrowser);
|
||||||
Helper::enableScrollBySwiping(ui->changelogBrowser);
|
Helper::enableScrollBySwiping(ui->changelogBrowser);
|
||||||
|
|
||||||
|
//FIXME: this function should be recursive
|
||||||
|
//FIXME: ensure that this is also reflected correctly in "Notes" section of mod description
|
||||||
bool hasInvalidDeps = !findInvalidDependencies(modName).empty();
|
bool hasInvalidDeps = !findInvalidDependencies(modName).empty();
|
||||||
bool hasBlockingMods = !findBlockingMods(modName).empty();
|
//bool hasBlockingMods = !findBlockingMods(modName).empty();
|
||||||
bool hasDependentMods = !findDependentMods(modName, true).empty();
|
bool hasDependentMods = !findDependentMods(modName, true).empty();
|
||||||
|
|
||||||
ui->disableButton->setVisible(modStateModel->isModEnabled(mod.getID()));
|
ui->disableButton->setVisible(modStateModel->isModEnabled(mod.getID()));
|
||||||
@ -459,8 +461,8 @@ void CModListView::selectMod(const QModelIndex & index)
|
|||||||
// Block buttons if action is not allowed at this time
|
// Block buttons if action is not allowed at this time
|
||||||
// TODO: automate handling of some of these cases instead of forcing player
|
// TODO: automate handling of some of these cases instead of forcing player
|
||||||
// to resolve all conflicts manually.
|
// to resolve all conflicts manually.
|
||||||
ui->disableButton->setEnabled(!hasDependentMods && !mod.isHidden());
|
ui->disableButton->setEnabled(true);
|
||||||
ui->enableButton->setEnabled(!hasBlockingMods && !hasInvalidDeps);
|
ui->enableButton->setEnabled(!hasInvalidDeps);
|
||||||
ui->installButton->setEnabled(!hasInvalidDeps);
|
ui->installButton->setEnabled(!hasInvalidDeps);
|
||||||
ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isHidden());
|
ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isHidden());
|
||||||
ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods);
|
ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods);
|
||||||
@ -564,16 +566,8 @@ void CModListView::on_enableButton_clicked()
|
|||||||
|
|
||||||
void CModListView::enableModByName(QString modName)
|
void CModListView::enableModByName(QString modName)
|
||||||
{
|
{
|
||||||
assert(findBlockingMods(modName).empty());
|
manager->enableMod(modName);
|
||||||
assert(findInvalidDependencies(modName).empty());
|
modModel->reloadRepositories();
|
||||||
|
|
||||||
auto mod = modStateModel->getMod(modName);
|
|
||||||
|
|
||||||
for(const auto & name : mod.getDependencies())
|
|
||||||
{
|
|
||||||
if(!modStateModel->isModEnabled(name))
|
|
||||||
manager->enableMod(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModListView::on_disableButton_clicked()
|
void CModListView::on_disableButton_clicked()
|
||||||
@ -587,8 +581,8 @@ void CModListView::on_disableButton_clicked()
|
|||||||
|
|
||||||
void CModListView::disableModByName(QString modName)
|
void CModListView::disableModByName(QString modName)
|
||||||
{
|
{
|
||||||
if(modStateModel->isModExists(modName) && modStateModel->isModEnabled(modName))
|
manager->disableMod(modName);
|
||||||
manager->disableMod(modName);
|
modModel->reloadRepositories();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModListView::on_updateButton_clicked()
|
void CModListView::on_updateButton_clicked()
|
||||||
@ -943,30 +937,9 @@ void CModListView::installMods(QStringList archives)
|
|||||||
manager->installMod(modNames[i], archives[i]);
|
manager->installMod(modNames[i], archives[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void(QString)> enableMod;
|
|
||||||
|
|
||||||
enableMod = [&](QString modName)
|
|
||||||
{
|
|
||||||
auto mod = modStateModel->getMod(modName);
|
|
||||||
if(mod.isInstalled() && !mod.isKeptDisabled())
|
|
||||||
{
|
|
||||||
for(const auto & dependencyName : mod.getDependencies())
|
|
||||||
{
|
|
||||||
if(!modStateModel->isModEnabled(dependencyName))
|
|
||||||
manager->enableMod(dependencyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!modStateModel->isModEnabled(modName) && manager->enableMod(modName))
|
|
||||||
{
|
|
||||||
for(QString child : modStateModel->getSubmods(modName))
|
|
||||||
enableMod(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for(QString mod : modsToEnable)
|
for(QString mod : modsToEnable)
|
||||||
{
|
{
|
||||||
enableMod(mod);
|
manager->enableMod(mod); // TODO: make it as a single action, so if mod 1 depends on mod 2 it would still activate
|
||||||
}
|
}
|
||||||
|
|
||||||
checkManagerErrors();
|
checkManagerErrors();
|
||||||
|
@ -47,7 +47,7 @@ QString ModState::getParentID() const
|
|||||||
|
|
||||||
QString ModState::getTopParentID() const
|
QString ModState::getTopParentID() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(impl.getParentID());
|
return QString::fromStdString(impl.getParentID()); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
|
@ -152,28 +152,8 @@ bool ModStateController::canEnableMod(QString modname)
|
|||||||
{
|
{
|
||||||
if(!modList->isModExists(modEntry)) // required mod is not available
|
if(!modList->isModExists(modEntry)) // required mod is not available
|
||||||
return addError(modname, tr("Required mod %1 is missing").arg(modEntry));
|
return addError(modname, tr("Required mod %1 is missing").arg(modEntry));
|
||||||
|
|
||||||
ModState modData = modList->getMod(modEntry);
|
|
||||||
|
|
||||||
if(!modData.isCompatibility() && !modList->isModEnabled(modEntry))
|
|
||||||
return addError(modname, tr("Required mod %1 is not enabled").arg(modEntry));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(QString modEntry : modList->getAllMods())
|
|
||||||
{
|
|
||||||
auto otherMod = modList->getMod(modEntry);
|
|
||||||
|
|
||||||
// "reverse conflict" - enabled mod has this one as conflict
|
|
||||||
if(modList->isModEnabled(modname) && otherMod.getConflicts().contains(modname))
|
|
||||||
return addError(modname, tr("This mod conflicts with %1").arg(modEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const auto & modEntry : mod.getConflicts())
|
|
||||||
{
|
|
||||||
// check if conflicting mod installed and enabled
|
|
||||||
if(modList->isModExists(modEntry) && modList->isModEnabled(modEntry))
|
|
||||||
return addError(modname, tr("This mod conflicts with %1").arg(modEntry));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,20 +167,16 @@ bool ModStateController::canDisableMod(QString modname)
|
|||||||
if(!mod.isInstalled())
|
if(!mod.isInstalled())
|
||||||
return addError(modname, tr("Mod must be installed first"));
|
return addError(modname, tr("Mod must be installed first"));
|
||||||
|
|
||||||
for(QString modEntry : modList->getAllMods())
|
|
||||||
{
|
|
||||||
auto current = modList->getMod(modEntry);
|
|
||||||
|
|
||||||
if(current.getDependencies().contains(modname) && modList->isModEnabled(modEntry))
|
|
||||||
return addError(modname, tr("This mod is needed to run %1").arg(modEntry));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModStateController::doEnableMod(QString mod, bool on)
|
bool ModStateController::doEnableMod(QString mod, bool on)
|
||||||
{
|
{
|
||||||
//modSettings->setModActive(mod, on);
|
if (on)
|
||||||
//modList->modChanged(mod);
|
modList->doEnableMod(mod);
|
||||||
|
else
|
||||||
|
modList->doDisableMod(mod);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ enum EModRoles
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModStateItemModel : public QAbstractItemModel
|
class ModStateItemModel final : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
friend class CModFilterModel;
|
friend class CModFilterModel;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -87,7 +87,7 @@ public:
|
|||||||
Qt::ItemFlags flags(const QModelIndex & index) const override;
|
Qt::ItemFlags flags(const QModelIndex & index) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CModFilterModel : public QSortFilterProxyModel
|
class CModFilterModel final : public QSortFilterProxyModel
|
||||||
{
|
{
|
||||||
ModStateItemModel * base;
|
ModStateItemModel * base;
|
||||||
ModFilterMask filterMask;
|
ModFilterMask filterMask;
|
||||||
|
@ -53,11 +53,6 @@ QStringList ModStateModel::getAllMods() const
|
|||||||
return stringListStdToQt(modManager->getAllMods());
|
return stringListStdToQt(modManager->getAllMods());
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ModStateModel::getSubmods(QString modName) const
|
|
||||||
{
|
|
||||||
return {}; //TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModStateModel::isModExists(QString modName) const
|
bool ModStateModel::isModExists(QString modName) const
|
||||||
{
|
{
|
||||||
return vstd::contains(modManager->getAllMods(), modName.toStdString());
|
return vstd::contains(modManager->getAllMods(), modName.toStdString());
|
||||||
@ -92,3 +87,13 @@ double ModStateModel::getInstalledModSizeMegabytes(QString modName) const
|
|||||||
{
|
{
|
||||||
return modManager->getInstalledModSizeMegabytes(modName.toStdString());
|
return modManager->getInstalledModSizeMegabytes(modName.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModStateModel::doEnableMod(QString modname)
|
||||||
|
{
|
||||||
|
modManager->tryEnableMod(modname.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModStateModel::doDisableMod(QString modname)
|
||||||
|
{
|
||||||
|
modManager->tryDisableMod(modname.toStdString());
|
||||||
|
}
|
||||||
|
@ -32,7 +32,6 @@ public:
|
|||||||
|
|
||||||
ModState getMod(QString modName) const;
|
ModState getMod(QString modName) const;
|
||||||
QStringList getAllMods() const;
|
QStringList getAllMods() const;
|
||||||
QStringList getSubmods(QString modName) const;
|
|
||||||
|
|
||||||
QString getInstalledModSizeFormatted(QString modName) const;
|
QString getInstalledModSizeFormatted(QString modName) const;
|
||||||
double getInstalledModSizeMegabytes(QString modName) const;
|
double getInstalledModSizeMegabytes(QString modName) const;
|
||||||
@ -42,4 +41,7 @@ public:
|
|||||||
bool isModEnabled(QString modName) const;
|
bool isModEnabled(QString modName) const;
|
||||||
bool isModUpdateAvailable(QString modName) const;
|
bool isModUpdateAvailable(QString modName) const;
|
||||||
bool isModVisible(QString modName) const;
|
bool isModVisible(QString modName) const;
|
||||||
|
|
||||||
|
void doEnableMod(QString modname);
|
||||||
|
void doDisableMod(QString modname);
|
||||||
};
|
};
|
||||||
|
@ -172,8 +172,6 @@ ModsPresetState::ModsPresetState()
|
|||||||
createInitialPreset(); // new install
|
createInitialPreset(); // new install
|
||||||
else
|
else
|
||||||
importInitialPreset(); // 1.5 format import
|
importInitialPreset(); // 1.5 format import
|
||||||
|
|
||||||
saveConfigurationState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +227,35 @@ std::optional<uint32_t> ModsPresetState::getValidatedChecksum(const TModID & mod
|
|||||||
return node.Integer();
|
return node.Integer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModsPresetState::setSettingActiveInPreset(const TModID & modName, const TModID & settingName, bool isActive)
|
void ModsPresetState::setModActive(const TModID & modID, bool isActive)
|
||||||
|
{
|
||||||
|
size_t dotPos = modID.find('.');
|
||||||
|
|
||||||
|
if(dotPos != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string rootMod = modID.substr(0, dotPos);
|
||||||
|
std::string settingID = modID.substr(dotPos + 1);
|
||||||
|
setSettingActive(rootMod, settingID, isActive);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isActive)
|
||||||
|
addRootMod(modID);
|
||||||
|
else
|
||||||
|
eraseRootMod(modID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModsPresetState::addRootMod(const TModID & modName)
|
||||||
|
{
|
||||||
|
const std::string & currentPresetName = modConfig["activePreset"].String();
|
||||||
|
JsonNode & currentPreset = modConfig["presets"][currentPresetName];
|
||||||
|
|
||||||
|
if (!vstd::contains(currentPreset["mods"].Vector(), JsonNode(modName)))
|
||||||
|
currentPreset["mods"].Vector().emplace_back(modName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModsPresetState::setSettingActive(const TModID & modName, const TModID & settingName, bool isActive)
|
||||||
{
|
{
|
||||||
const std::string & currentPresetName = modConfig["activePreset"].String();
|
const std::string & currentPresetName = modConfig["activePreset"].String();
|
||||||
JsonNode & currentPreset = modConfig["presets"][currentPresetName];
|
JsonNode & currentPreset = modConfig["presets"][currentPresetName];
|
||||||
@ -237,16 +263,18 @@ void ModsPresetState::setSettingActiveInPreset(const TModID & modName, const TMo
|
|||||||
currentPreset["settings"][modName][settingName].Bool() = isActive;
|
currentPreset["settings"][modName][settingName].Bool() = isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModsPresetState::eraseModInAllPresets(const TModID & modName)
|
void ModsPresetState::eraseRootMod(const TModID & modName)
|
||||||
{
|
{
|
||||||
for (auto & preset : modConfig["presets"].Struct())
|
const std::string & currentPresetName = modConfig["activePreset"].String();
|
||||||
vstd::erase(preset.second["mods"].Vector(), JsonNode(modName));
|
JsonNode & currentPreset = modConfig["presets"][currentPresetName];
|
||||||
|
vstd::erase(currentPreset["mods"].Vector(), JsonNode(modName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModsPresetState::eraseModSettingInAllPresets(const TModID & modName, const TModID & settingName)
|
void ModsPresetState::eraseModSetting(const TModID & modName, const TModID & settingName)
|
||||||
{
|
{
|
||||||
for (auto & preset : modConfig["presets"].Struct())
|
const std::string & currentPresetName = modConfig["activePreset"].String();
|
||||||
preset.second["settings"][modName].Struct().erase(modName);
|
JsonNode & currentPreset = modConfig["presets"][currentPresetName];
|
||||||
|
currentPreset["settings"][modName].Struct().erase(modName);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TModID> ModsPresetState::getActiveMods() const
|
std::vector<TModID> ModsPresetState::getActiveMods() const
|
||||||
@ -344,7 +372,7 @@ ModManager::ModManager(const JsonNode & repositoryList)
|
|||||||
addNewModsToPreset();
|
addNewModsToPreset();
|
||||||
|
|
||||||
std::vector<TModID> desiredModList = modsPreset->getActiveMods();
|
std::vector<TModID> desiredModList = modsPreset->getActiveMods();
|
||||||
generateLoadOrder(desiredModList);
|
depedencyResolver = std::make_unique<ModDependenciesResolver>(desiredModList, *modsStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModManager::~ModManager() = default;
|
ModManager::~ModManager() = default;
|
||||||
@ -357,12 +385,12 @@ const ModDescription & ModManager::getModDescription(const TModID & modID) const
|
|||||||
|
|
||||||
bool ModManager::isModActive(const TModID & modID) const
|
bool ModManager::isModActive(const TModID & modID) const
|
||||||
{
|
{
|
||||||
return vstd::contains(activeMods, modID);
|
return vstd::contains(getActiveMods(), modID);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TModList & ModManager::getActiveMods() const
|
const TModList & ModManager::getActiveMods() const
|
||||||
{
|
{
|
||||||
return activeMods;
|
return depedencyResolver->getActiveMods();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ModManager::computeChecksum(const TModID & modName) const
|
uint32_t ModManager::computeChecksum(const TModID & modName) const
|
||||||
@ -404,7 +432,7 @@ void ModManager::eraseMissingModsFromPreset()
|
|||||||
{
|
{
|
||||||
if(!vstd::contains(installedMods, rootMod))
|
if(!vstd::contains(installedMods, rootMod))
|
||||||
{
|
{
|
||||||
modsPreset->eraseModInAllPresets(rootMod);
|
modsPreset->eraseRootMod(rootMod);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +443,7 @@ void ModManager::eraseMissingModsFromPreset()
|
|||||||
TModID fullModID = rootMod + '.' + modSetting.first;
|
TModID fullModID = rootMod + '.' + modSetting.first;
|
||||||
if(!vstd::contains(installedMods, fullModID))
|
if(!vstd::contains(installedMods, fullModID))
|
||||||
{
|
{
|
||||||
modsPreset->eraseModSettingInAllPresets(rootMod, modSetting.first);
|
modsPreset->eraseModSetting(rootMod, modSetting.first);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -439,17 +467,110 @@ void ModManager::addNewModsToPreset()
|
|||||||
const auto & modSettings = modsPreset->getModSettings(rootMod);
|
const auto & modSettings = modsPreset->getModSettings(rootMod);
|
||||||
|
|
||||||
if (!modSettings.count(settingID))
|
if (!modSettings.count(settingID))
|
||||||
modsPreset->setSettingActiveInPreset(rootMod, settingID, modsStorage->getMod(modID).keepDisabled());
|
modsPreset->setSettingActive(rootMod, settingID, modsStorage->getMod(modID).keepDisabled());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
|
TModList ModManager::collectDependenciesRecursive(const TModID & modID) const
|
||||||
|
{
|
||||||
|
TModList result;
|
||||||
|
TModList toTest;
|
||||||
|
|
||||||
|
toTest.push_back(modID);
|
||||||
|
while (!toTest.empty())
|
||||||
|
{
|
||||||
|
TModID currentMod = toTest.back();
|
||||||
|
toTest.pop_back();
|
||||||
|
result.push_back(currentMod);
|
||||||
|
|
||||||
|
for (const auto & dependency : getModDescription(currentMod).getDependencies())
|
||||||
|
{
|
||||||
|
if (!vstd::contains(result, dependency))
|
||||||
|
toTest.push_back(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModManager::tryEnableMod(const TModID & modName)
|
||||||
|
{
|
||||||
|
auto requiredActiveMods = collectDependenciesRecursive(modName);
|
||||||
|
auto additionalActiveMods = getActiveMods();
|
||||||
|
|
||||||
|
assert(!vstd::contains(additionalActiveMods, modName));
|
||||||
|
|
||||||
|
ModDependenciesResolver testResolver(requiredActiveMods, *modsStorage);
|
||||||
|
assert(testResolver.getBrokenMods().empty());
|
||||||
|
assert(vstd::contains(testResolver.getActiveMods(), modName));
|
||||||
|
|
||||||
|
testResolver.tryAddMods(additionalActiveMods, *modsStorage);
|
||||||
|
|
||||||
|
if (!vstd::contains(testResolver.getActiveMods(), modName))
|
||||||
|
{
|
||||||
|
// FIXME: report?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePreset(testResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModManager::tryDisableMod(const TModID & modName)
|
||||||
|
{
|
||||||
|
auto desiredActiveMods = getActiveMods();
|
||||||
|
assert(vstd::contains(desiredActiveMods, modName));
|
||||||
|
|
||||||
|
vstd::erase(desiredActiveMods, modName);
|
||||||
|
|
||||||
|
ModDependenciesResolver testResolver(desiredActiveMods, *modsStorage);
|
||||||
|
|
||||||
|
if (vstd::contains(testResolver.getActiveMods(), modName))
|
||||||
|
{
|
||||||
|
// FIXME: report?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePreset(testResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModManager::updatePreset(const ModDependenciesResolver & testResolver)
|
||||||
|
{
|
||||||
|
const auto & newActiveMods = testResolver.getActiveMods();
|
||||||
|
const auto & newBrokenMods = testResolver.getBrokenMods();
|
||||||
|
|
||||||
|
for (const auto & modID : newActiveMods)
|
||||||
|
modsPreset->setModActive(modID, true);
|
||||||
|
|
||||||
|
for (const auto & modID : newBrokenMods)
|
||||||
|
modsPreset->setModActive(modID, false);
|
||||||
|
|
||||||
|
std::vector<TModID> desiredModList = modsPreset->getActiveMods();
|
||||||
|
depedencyResolver = std::make_unique<ModDependenciesResolver>(desiredModList, *modsStorage);
|
||||||
|
|
||||||
|
modsPreset->saveConfigurationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModDependenciesResolver::ModDependenciesResolver(const TModList & modsToResolve, const ModsStorage & storage)
|
||||||
|
{
|
||||||
|
tryAddMods(modsToResolve, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TModList & ModDependenciesResolver::getActiveMods() const
|
||||||
|
{
|
||||||
|
return activeMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TModList & ModDependenciesResolver::getBrokenMods() const
|
||||||
|
{
|
||||||
|
return brokenMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModDependenciesResolver::tryAddMods(TModList modsToResolve, const ModsStorage & storage)
|
||||||
{
|
{
|
||||||
// Topological sort algorithm.
|
// Topological sort algorithm.
|
||||||
boost::range::sort(modsToResolve); // Sort mods per name
|
boost::range::sort(modsToResolve); // Sort mods per name
|
||||||
std::vector<TModID> sortedValidMods; // Vector keeps order of elements (LIFO)
|
std::vector<TModID> sortedValidMods(activeMods.begin(), activeMods.end()); // Vector keeps order of elements (LIFO)
|
||||||
sortedValidMods.reserve(modsToResolve.size()); // push_back calls won't cause memory reallocation
|
std::set<TModID> resolvedModIDs(activeMods.begin(), activeMods.end()); // Use a set for validation for performance reason, but set does not keep order of elements
|
||||||
std::set<TModID> resolvedModIDs; // Use a set for validation for performance reason, but set does not keep order of elements
|
|
||||||
std::set<TModID> notResolvedModIDs(modsToResolve.begin(), modsToResolve.end()); // Use a set for validation for performance reason
|
std::set<TModID> notResolvedModIDs(modsToResolve.begin(), modsToResolve.end()); // Use a set for validation for performance reason
|
||||||
|
|
||||||
// Mod is resolved if it has no dependencies or all its dependencies are already resolved
|
// Mod is resolved if it has no dependencies or all its dependencies are already resolved
|
||||||
@ -474,7 +595,7 @@ void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
for(const TModID & reverseConflict : resolvedModIDs)
|
for(const TModID & reverseConflict : resolvedModIDs)
|
||||||
if(vstd::contains(modsStorage->getMod(reverseConflict).getConflicts(), mod.getID()))
|
if(vstd::contains(storage.getMod(reverseConflict).getConflicts(), mod.getID()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -485,7 +606,7 @@ void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
|
|||||||
std::set<TModID> resolvedOnCurrentTreeLevel;
|
std::set<TModID> resolvedOnCurrentTreeLevel;
|
||||||
for(auto it = modsToResolve.begin(); it != modsToResolve.end();) // One iteration - one level of mods tree
|
for(auto it = modsToResolve.begin(); it != modsToResolve.end();) // One iteration - one level of mods tree
|
||||||
{
|
{
|
||||||
if(isResolved(modsStorage->getMod(*it)))
|
if(isResolved(storage.getMod(*it)))
|
||||||
{
|
{
|
||||||
resolvedOnCurrentTreeLevel.insert(*it); // Not to the resolvedModIDs, so current node children will be resolved on the next iteration
|
resolvedOnCurrentTreeLevel.insert(*it); // Not to the resolvedModIDs, so current node children will be resolved on the next iteration
|
||||||
sortedValidMods.push_back(*it);
|
sortedValidMods.push_back(*it);
|
||||||
@ -507,7 +628,7 @@ void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
|
|||||||
|
|
||||||
assert(!sortedValidMods.empty());
|
assert(!sortedValidMods.empty());
|
||||||
activeMods = sortedValidMods;
|
activeMods = sortedValidMods;
|
||||||
brokenMods = modsToResolve;
|
brokenMods.insert(brokenMods.end(), modsToResolve.begin(), modsToResolve.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -50,9 +50,13 @@ class ModsPresetState : boost::noncopyable
|
|||||||
public:
|
public:
|
||||||
ModsPresetState();
|
ModsPresetState();
|
||||||
|
|
||||||
void setSettingActiveInPreset(const TModID & modName, const TModID & settingName, bool isActive);
|
void setModActive(const TModID & modName, bool isActive);
|
||||||
void eraseModInAllPresets(const TModID & modName);
|
|
||||||
void eraseModSettingInAllPresets(const TModID & modName, const TModID & settingName);
|
void addRootMod(const TModID & modName);
|
||||||
|
void eraseRootMod(const TModID & modName);
|
||||||
|
|
||||||
|
void setSettingActive(const TModID & modName, const TModID & settingName, bool isActive);
|
||||||
|
void eraseModSetting(const TModID & modName, const TModID & settingName);
|
||||||
|
|
||||||
/// Returns list of all mods active in current preset. Mod order is unspecified
|
/// Returns list of all mods active in current preset. Mod order is unspecified
|
||||||
TModList getActiveMods() const;
|
TModList getActiveMods() const;
|
||||||
@ -81,8 +85,7 @@ public:
|
|||||||
TModList getAllMods() const;
|
TModList getAllMods() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Provides public interface to access mod state
|
class ModDependenciesResolver : boost::noncopyable
|
||||||
class DLL_LINKAGE ModManager : boost::noncopyable
|
|
||||||
{
|
{
|
||||||
/// all currently active mods, in their load order
|
/// all currently active mods, in their load order
|
||||||
TModList activeMods;
|
TModList activeMods;
|
||||||
@ -90,13 +93,29 @@ class DLL_LINKAGE ModManager : boost::noncopyable
|
|||||||
/// Mods from current preset that failed to load due to invalid dependencies
|
/// Mods from current preset that failed to load due to invalid dependencies
|
||||||
TModList brokenMods;
|
TModList brokenMods;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ModDependenciesResolver(const TModList & modsToResolve, const ModsStorage & storage);
|
||||||
|
|
||||||
|
void tryAddMods(TModList modsToResolve, const ModsStorage & storage);
|
||||||
|
|
||||||
|
const TModList & getActiveMods() const;
|
||||||
|
const TModList & getBrokenMods() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provides public interface to access mod state
|
||||||
|
class DLL_LINKAGE ModManager : boost::noncopyable
|
||||||
|
{
|
||||||
std::unique_ptr<ModsState> modsState;
|
std::unique_ptr<ModsState> modsState;
|
||||||
std::unique_ptr<ModsPresetState> modsPreset;
|
std::unique_ptr<ModsPresetState> modsPreset;
|
||||||
std::unique_ptr<ModsStorage> modsStorage;
|
std::unique_ptr<ModsStorage> modsStorage;
|
||||||
|
std::unique_ptr<ModDependenciesResolver> depedencyResolver;
|
||||||
|
|
||||||
void generateLoadOrder(TModList desiredModList);
|
void generateLoadOrder(TModList desiredModList);
|
||||||
void eraseMissingModsFromPreset();
|
void eraseMissingModsFromPreset();
|
||||||
void addNewModsToPreset();
|
void addNewModsToPreset();
|
||||||
|
void updatePreset(const ModDependenciesResolver & newData);
|
||||||
|
|
||||||
|
TModList collectDependenciesRecursive(const TModID & modID) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModManager(const JsonNode & repositoryList);
|
ModManager(const JsonNode & repositoryList);
|
||||||
@ -113,6 +132,9 @@ public:
|
|||||||
void setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value);
|
void setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value);
|
||||||
void saveConfigurationState() const;
|
void saveConfigurationState() const;
|
||||||
double getInstalledModSizeMegabytes(const TModID & modName) const;
|
double getInstalledModSizeMegabytes(const TModID & modName) const;
|
||||||
|
|
||||||
|
void tryEnableMod(const TModID & modName);
|
||||||
|
void tryDisableMod(const TModID & modName);
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
Loading…
Reference in New Issue
Block a user