1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-27 22:49:25 +02:00

Allow mod operations such as install when possible

This commit is contained in:
Ivan Savenko
2024-11-18 17:37:45 +00:00
parent 879eb2184f
commit 00f97fb8cd
5 changed files with 94 additions and 61 deletions

View File

@@ -266,41 +266,34 @@ QStringList CModListView::getModNames(QString queryingModID, QStringList input)
for(const auto & modID : input) for(const auto & modID : input)
{ {
// Missing mod - use mod ID if (modStateModel->isModExists(modID) && modStateModel->getMod(modID).isHidden())
// TODO: for submods show parent ID instead continue;
if (!modStateModel->isModExists(modID))
QString parentModID = modStateModel->getTopParent(modID);
QString displayName;
if (modStateModel->isSubmod(modID) && queryingMod.getParentID() != parentModID )
{ {
result += modID; // show in form "parent mod (submod)"
continue;
QString parentDisplayName = parentModID;
QString submodDisplayName = modID;
if (modStateModel->isModExists(parentModID))
parentDisplayName = modStateModel->getMod(parentModID).getName();
if (modStateModel->isModExists(modID))
submodDisplayName = modStateModel->getMod(modID).getName();
displayName = QString("%1 (%2)").arg(submodDisplayName, parentDisplayName);
} }
else
auto mod = modStateModel->getMod(modID);
if (queryingMod.getParentID() == modID)
continue;
if (mod.isHidden())
continue;
QString displayName = mod.getName();
if (displayName.isEmpty())
displayName = modID.toLower();
if (mod.isSubmod() && queryingMod.getParentID() != mod.getParentID() ) // FIXME: recheck this block
{ {
auto parentModID = mod.getTopParentID(); // show simply as mod name
auto parentMod = modStateModel->getMod(parentModID); displayName = modID;
QString parentDisplayName = parentMod.getName(); if (modStateModel->isModExists(modID))
if (parentDisplayName.isEmpty()) displayName = modStateModel->getMod(modID).getName();
parentDisplayName = parentModID.toLower();
if (modStateModel->isModInstalled(modID))
displayName = QString("%1 (%2)").arg(displayName, parentDisplayName);
else
displayName = parentDisplayName;
} }
// TODO: show active mods in bold?
result += displayName; result += displayName;
} }
return result; return result;
@@ -457,7 +450,6 @@ void CModListView::selectMod(const QModelIndex & index)
//FIXME: this function should be recursive //FIXME: this function should be recursive
//FIXME: ensure that this is also reflected correctly in "Notes" section of mod description //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 hasDependentMods = !findDependentMods(modName, true).empty();
ui->disableButton->setVisible(modStateModel->isModEnabled(mod.getID())); ui->disableButton->setVisible(modStateModel->isModEnabled(mod.getID()));
ui->enableButton->setVisible(!modStateModel->isModEnabled(mod.getID())); ui->enableButton->setVisible(!modStateModel->isModEnabled(mod.getID()));
@@ -466,13 +458,11 @@ void CModListView::selectMod(const QModelIndex & index)
ui->updateButton->setVisible(mod.isUpdateAvailable()); ui->updateButton->setVisible(mod.isUpdateAvailable());
// 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
// to resolve all conflicts manually.
ui->disableButton->setEnabled(true); ui->disableButton->setEnabled(true);
ui->enableButton->setEnabled(!hasInvalidDeps); ui->enableButton->setEnabled(!hasInvalidDeps);
ui->installButton->setEnabled(!hasInvalidDeps); ui->installButton->setEnabled(!hasInvalidDeps);
ui->uninstallButton->setEnabled(true); ui->uninstallButton->setEnabled(true);
ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods); ui->updateButton->setEnabled(!hasInvalidDeps);
loadScreenshots(); loadScreenshots();
} }
@@ -514,27 +504,18 @@ QStringList CModListView::findInvalidDependencies(QString mod)
QStringList ret; QStringList ret;
for(QString requirement : modStateModel->getMod(mod).getDependencies()) for(QString requirement : modStateModel->getMod(mod).getDependencies())
{ {
if(!modStateModel->isModExists(requirement)) if(modStateModel->isModExists(requirement))
ret += requirement;
}
return ret;
}
QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled)
{
QStringList ret;
for(QString modName : modStateModel->getAllMods())
{
auto current = modStateModel->getMod(modName);
if(!current.isInstalled() || !current.isVisible())
continue; continue;
if(current.getDependencies().contains(mod, Qt::CaseInsensitive)) if(modStateModel->isSubmod(requirement))
{ {
if(!(modStateModel->isModEnabled(modName) && excludeDisabled)) QString parentModID = modStateModel->getTopParent(requirement);
ret += modName;
if (modStateModel->isModExists(parentModID) && !modStateModel->isModInstalled(parentModID))
continue;
} }
ret += requirement;
} }
return ret; return ret;
} }
@@ -569,13 +550,47 @@ void CModListView::disableModByName(QString modName)
modModel->reloadRepositories(); modModel->reloadRepositories();
} }
QStringList CModListView::getModsToInstall(QString mod)
{
QStringList result;
QStringList candidates;
QStringList processed;
candidates.push_back(mod);
while (!candidates.empty())
{
QString potentialToInstall = candidates.back();
candidates.pop_back();
processed.push_back(potentialToInstall);
if (modStateModel->isSubmod(potentialToInstall))
potentialToInstall = modStateModel->getTopParent(potentialToInstall);
if (!modStateModel->isModExists(potentialToInstall))
throw std::runtime_error("Attempt to install non-existing mod! Mod name:" + potentialToInstall.toStdString());
if (modStateModel->isModInstalled(potentialToInstall))
continue;
result.push_back(potentialToInstall);
QStringList dependencies = modStateModel->getMod(potentialToInstall).getDependencies();
for (const auto & dependency : dependencies)
{
if (!processed.contains(dependency))
candidates.push_back(dependency);
}
}
return result;
}
void CModListView::on_updateButton_clicked() void CModListView::on_updateButton_clicked()
{ {
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
assert(findInvalidDependencies(modName).empty()); assert(findInvalidDependencies(modName).empty());
for(const auto & name : modStateModel->getMod(modName).getDependencies()) for(const auto & name : getModsToInstall(modName))
{ {
auto mod = modStateModel->getMod(name); auto mod = modStateModel->getMod(name);
// update required mod, install missing (can be new dependency) // update required mod, install missing (can be new dependency)
@@ -604,8 +619,8 @@ void CModListView::on_installButton_clicked()
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
assert(findInvalidDependencies(modName).empty()); assert(findInvalidDependencies(modName).empty());
for(const auto & name : modStateModel->getMod(modName).getDependencies()) for(const auto & name : getModsToInstall(modName))
{ {
auto mod = modStateModel->getMod(name); auto mod = modStateModel->getMod(name);
if(mod.isAvailable()) if(mod.isAvailable())
@@ -1058,7 +1073,6 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
auto mod = modStateModel->getMod(modName); auto mod = modStateModel->getMod(modName);
bool hasInvalidDeps = !findInvalidDependencies(modName).empty(); bool hasInvalidDeps = !findInvalidDependencies(modName).empty();
bool hasDependentMods = !findDependentMods(modName, true).empty();
if(!hasInvalidDeps && mod.isAvailable() && !mod.isSubmod()) if(!hasInvalidDeps && mod.isAvailable() && !mod.isSubmod())
{ {
@@ -1066,7 +1080,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
return; return;
} }
if(!hasInvalidDeps && !hasDependentMods && mod.isUpdateAvailable() && index.column() == ModFields::STATUS_UPDATE) if(!hasInvalidDeps && mod.isUpdateAvailable() && index.column() == ModFields::STATUS_UPDATE)
{ {
on_updateButton_clicked(); on_updateButton_clicked();
return; return;
@@ -1088,7 +1102,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
return; return;
} }
if(!hasDependentMods && !mod.isVisible() && modStateModel->isModEnabled(modName)) if(modStateModel->isModEnabled(modName))
{ {
on_disableButton_clicked(); on_disableButton_clicked();
return; return;

View File

@@ -46,10 +46,11 @@ class CModListView : public QWidget
/// replace mod ID's with proper human-readable mod names /// replace mod ID's with proper human-readable mod names
QStringList getModNames(QString queryingMod, QStringList input); QStringList getModNames(QString queryingMod, QStringList input);
/// returns list of mods that are needed for install of this mod (potentially including this mod itself)
QStringList getModsToInstall(QString mod);
// find mods unknown to mod list (not present in repo and not installed) // find mods unknown to mod list (not present in repo and not installed)
QStringList findInvalidDependencies(QString mod); QStringList findInvalidDependencies(QString mod);
// find mods that depend on this one
QStringList findDependentMods(QString mod, bool excludeDisabled);
void manualInstallFile(QString filePath); void manualInstallFile(QString filePath);
void downloadFile(QString file, QString url, QString description, qint64 size = 0); void downloadFile(QString file, QString url, QString description, qint64 size = 0);

View File

@@ -104,3 +104,17 @@ void ModStateModel::doDisableMod(QString modname)
{ {
modManager->tryDisableMod(modname.toStdString()); modManager->tryDisableMod(modname.toStdString());
} }
bool ModStateModel::isSubmod(QString modname)
{
return modname.contains('.');
}
QString ModStateModel::getTopParent(QString modname) const
{
QStringList components = modname.split('.');
if (components.size() > 1)
return components.front();
else
return "";
}

View File

@@ -45,4 +45,7 @@ public:
void doEnableMod(QString modname); void doEnableMod(QString modname);
void doDisableMod(QString modname); void doDisableMod(QString modname);
bool isSubmod(QString modname);
QString getTopParent(QString modname) const;
}; };

View File

@@ -93,7 +93,8 @@ void CModHandler::loadModFilesystems()
modFilesystems[modName] = genModFilesystem(modName, getModInfo(modName).getFilesystemConfig()); modFilesystems[modName] = genModFilesystem(modName, getModInfo(modName).getFilesystemConfig());
for(const TModID & modName : activeMods) for(const TModID & modName : activeMods)
CResourceHandler::addFilesystem("data", modName, modFilesystems[modName]); if (modName != "core") // FIXME: remove
CResourceHandler::addFilesystem("data", modName, modFilesystems[modName]);
if (settings["mods"]["validation"].String() == "full") if (settings["mods"]["validation"].String() == "full")
checkModFilesystemsConflicts(modFilesystems); checkModFilesystemsConflicts(modFilesystems);