Warn about known conflicting modloaders

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2024-06-27 19:25:38 -07:00
parent 4ed92a95c2
commit c4cc1cfe4f
No known key found for this signature in database
GPG Key ID: E10E321EB160949B
4 changed files with 137 additions and 102 deletions

View File

@ -225,6 +225,22 @@ bool Component::isVersionChangeable()
return false;
}
bool Component::isKnownModloader()
{
auto iter = KNOWN_MODLOADERS.find(m_uid);
return iter != KNOWN_MODLOADERS.cend();
}
QStringList Component::knownConfictingComponents()
{
auto iter = KNOWN_MODLOADERS.find(m_uid);
if (iter != KNOWN_MODLOADERS.cend()) {
return (*iter).knownConfictingComponents;
} else {
return {};
}
}
void Component::setImportant(bool state)
{
if (m_important != state) {

View File

@ -9,6 +9,7 @@
#include "ProblemProvider.h"
#include "QObjectPtr.h"
#include "meta/JsonFormat.h"
#include "modplatform/ModIndex.h"
class PackProfile;
class LaunchProfile;
@ -43,6 +44,18 @@ using UpdateAction = std::variant<UpdateActionNone,
UpdateActionRemove,
UpdateActionImportantChanged>;
struct ModloaderMapEntry {
ModPlatform::ModLoaderType type;
QStringList knownConfictingComponents;
};
static const QMap<QString, ModloaderMapEntry> KNOWN_MODLOADERS{
{ "net.neoforged", { ModPlatform::NeoForge, { "net.minecraftforge", "net.fabricmc.fabric-loader", "net.fabricmc.fabric-loader" } } },
{ "net.minecraftforge", { ModPlatform::Forge, { "net.neoforged", "net.fabricmc.fabric-loader", "net.fabricmc.fabric-loader" } } },
{ "net.fabricmc.fabric-loader", { ModPlatform::Fabric, { "net.minecraftforge", "net.neoforged", "org.quiltmc.quilt-loader" } } },
{ "org.quiltmc.quilt-loader", { ModPlatform::Quilt, { "net.minecraftforge", "net.neoforged", "net.fabricmc.fabric-loader" } } },
{ "com.mumfrey.liteloader", { ModPlatform::LiteLoader, {} } }
};
class Component : public QObject, public ProblemProvider {
Q_OBJECT
public:
@ -65,6 +78,8 @@ class Component : public QObject, public ProblemProvider {
bool isRemovable();
bool isCustom();
bool isVersionChangeable();
bool isKnownModloader();
QStringList knownConfictingComponents();
// DEPRECATED: explicit numeric order values, used for loading old non-component config. TODO: refactor and move to migration code
void setOrder(int order);

View File

@ -181,8 +181,8 @@ void ComponentUpdateTask::loadComponents()
}
result = composeLoadResult(result, singleResult);
if (loadTask) {
qCDebug(instanceProfileResolveC) << d->m_profile->d->m_instance->name() << "|"
<< "Remote loading is being run for" << component->getName();
qCDebug(instanceProfileResolveC) << d->m_profile->d->m_instance->name() << "|" << "Remote loading is being run for"
<< component->getName();
connect(loadTask.get(), &Task::succeeded, this, [this, taskIndex]() { remoteLoadSucceeded(taskIndex); });
connect(loadTask.get(), &Task::failed, this, [this, taskIndex](const QString& error) { remoteLoadFailed(taskIndex, error); });
connect(loadTask.get(), &Task::aborted, this, [this, taskIndex]() { remoteLoadFailed(taskIndex, tr("Aborted")); });
@ -392,8 +392,8 @@ ComponentContainer ComponentUpdateTask::collectTreeLinked(const QString& uid)
auto& componentIndex = d->m_profile->d->componentIndex;
auto& instance = d->m_profile->d->m_instance;
for (auto comp : components) {
qCDebug(instanceProfileResolveC) << instance->name() << "|"
<< "scanning" << comp->getID() << ":" << comp->getVersion() << "for tree link";
qCDebug(instanceProfileResolveC) << instance->name() << "|" << "scanning" << comp->getID() << ":" << comp->getVersion()
<< "for tree link";
auto dep = std::find_if(comp->m_cachedRequires.cbegin(), comp->m_cachedRequires.cend(),
[uid](const Meta::Require& req) -> bool { return req.uid == uid; });
if (dep != comp->m_cachedRequires.cend()) {
@ -559,98 +559,94 @@ void ComponentUpdateTask::performUpdateActions()
continue;
}
auto action = component->getUpdateAction();
auto visitor =
overload{ [](const UpdateActionNone&) {
// noop
},
[&component, &instance](const UpdateActionChangeVersion& cv) {
qCDebug(instanceProfileResolveC) << instance->name() << "|"
<< "UpdateActionChangeVersion" << component->getID() << ":"
<< component->getVersion() << "change to" << cv.targetVersion;
component->setVersion(cv.targetVersion);
component->waitLoadMeta();
},
[&component, &instance](const UpdateActionLatestRecommendedCompatable lrc) {
qCDebug(instanceProfileResolveC)
<< instance->name() << "|"
<< "UpdateActionLatestRecommendedCompatable" << component->getID() << ":" << component->getVersion()
<< "updating to latest recommend or compatible with" << lrc.parentUid << lrc.version;
auto versionList = APPLICATION->metadataIndex()->get(component->getID());
versionList->waitToLoad();
if (versionList) {
auto recommended = versionList->getRecommendedForParent(lrc.parentUid, lrc.version);
if (recommended) {
component->setVersion(recommended->version());
component->waitLoadMeta();
return;
}
auto visitor = overload{
[](const UpdateActionNone&) {
// noop
},
[&component, &instance](const UpdateActionChangeVersion& cv) {
qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionChangeVersion" << component->getID() << ":"
<< component->getVersion() << "change to" << cv.targetVersion;
component->setVersion(cv.targetVersion);
component->waitLoadMeta();
},
[&component, &instance](const UpdateActionLatestRecommendedCompatable lrc) {
qCDebug(instanceProfileResolveC)
<< instance->name() << "|" << "UpdateActionLatestRecommendedCompatable" << component->getID() << ":"
<< component->getVersion() << "updating to latest recommend or compatible with" << lrc.parentUid << lrc.version;
auto versionList = APPLICATION->metadataIndex()->get(component->getID());
versionList->waitToLoad();
if (versionList) {
auto recommended = versionList->getRecommendedForParent(lrc.parentUid, lrc.version);
if (recommended) {
component->setVersion(recommended->version());
component->waitLoadMeta();
return;
}
auto latest = versionList->getLatestForParent(lrc.parentUid, lrc.version);
if (latest) {
component->setVersion(latest->version());
component->waitLoadMeta();
} else {
component->addComponentProblem(ProblemSeverity::Error,
QObject::tr("No compatible version of %1 found for %2 %3")
.arg(component->getName(), lrc.parentName, lrc.version));
}
} else {
component->addComponentProblem(
ProblemSeverity::Error,
QObject::tr("No version list in metadata index for %1").arg(component->getID()));
}
},
[&component, &instance, &toRemove](const UpdateActionRemove&) {
qCDebug(instanceProfileResolveC)
<< instance->name() << "|"
<< "UpdateActionRemove" << component->getID() << ":" << component->getVersion() << "removing";
toRemove.append(component->getID());
},
[this, &component, &instance, &addedActions, &componentIndex](const UpdateActionImportantChanged& ic) {
qCDebug(instanceProfileResolveC)
<< instance->name() << "|"
<< "UpdateImportantChanged" << component->getID() << ":" << component->getVersion() << "was changed from"
<< ic.oldVersion << "updating linked components";
auto oldVersion = APPLICATION->metadataIndex()->getLoadedVersion(component->getID(), ic.oldVersion);
for (auto oldReq : oldVersion->requiredSet()) {
auto currentlyRequired = component->m_cachedRequires.find(oldReq);
if (currentlyRequired == component->m_cachedRequires.cend()) {
auto oldReqComp = componentIndex.find(oldReq.uid);
if (oldReqComp != componentIndex.cend()) {
(*oldReqComp)->setUpdateAction(UpdateAction{ UpdateActionRemove{} });
addedActions = true;
}
}
}
auto linked = collectTreeLinked(component->getID());
for (auto comp : linked) {
if (comp->isCustom()) {
continue;
}
auto compUid = comp->getID();
auto parentReq = std::find_if(component->m_cachedRequires.begin(), component->m_cachedRequires.end(),
[compUid](const Meta::Require& req) { return req.uid == compUid; });
if (parentReq != component->m_cachedRequires.end()) {
auto newVersion = parentReq->equalsVersion.isEmpty() ? parentReq->suggests : parentReq->equalsVersion;
if (!newVersion.isEmpty()) {
comp->setUpdateAction(UpdateAction{ UpdateActionChangeVersion{ newVersion } });
} else {
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatable{
component->getID(),
component->getName(),
component->getVersion(),
} });
}
} else {
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatable{
component->getID(),
component->getName(),
component->getVersion(),
} });
}
addedActions = true;
}
} };
auto latest = versionList->getLatestForParent(lrc.parentUid, lrc.version);
if (latest) {
component->setVersion(latest->version());
component->waitLoadMeta();
} else {
component->addComponentProblem(ProblemSeverity::Error,
QObject::tr("No compatible version of %1 found for %2 %3")
.arg(component->getName(), lrc.parentName, lrc.version));
}
} else {
component->addComponentProblem(ProblemSeverity::Error,
QObject::tr("No version list in metadata index for %1").arg(component->getID()));
}
},
[&component, &instance, &toRemove](const UpdateActionRemove&) {
qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionRemove" << component->getID() << ":"
<< component->getVersion() << "removing";
toRemove.append(component->getID());
},
[this, &component, &instance, &addedActions, &componentIndex](const UpdateActionImportantChanged& ic) {
qCDebug(instanceProfileResolveC)
<< instance->name() << "|" << "UpdateImportantChanged" << component->getID() << ":" << component->getVersion()
<< "was changed from" << ic.oldVersion << "updating linked components";
auto oldVersion = APPLICATION->metadataIndex()->getLoadedVersion(component->getID(), ic.oldVersion);
for (auto oldReq : oldVersion->requiredSet()) {
auto currentlyRequired = component->m_cachedRequires.find(oldReq);
if (currentlyRequired == component->m_cachedRequires.cend()) {
auto oldReqComp = componentIndex.find(oldReq.uid);
if (oldReqComp != componentIndex.cend()) {
(*oldReqComp)->setUpdateAction(UpdateAction{ UpdateActionRemove{} });
addedActions = true;
}
}
}
auto linked = collectTreeLinked(component->getID());
for (auto comp : linked) {
if (comp->isCustom()) {
continue;
}
auto compUid = comp->getID();
auto parentReq = std::find_if(component->m_cachedRequires.begin(), component->m_cachedRequires.end(),
[compUid](const Meta::Require& req) { return req.uid == compUid; });
if (parentReq != component->m_cachedRequires.end()) {
auto newVersion = parentReq->equalsVersion.isEmpty() ? parentReq->suggests : parentReq->equalsVersion;
if (!newVersion.isEmpty()) {
comp->setUpdateAction(UpdateAction{ UpdateActionChangeVersion{ newVersion } });
} else {
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatable{
component->getID(),
component->getName(),
component->getVersion(),
} });
}
} else {
comp->setUpdateAction(UpdateAction{ UpdateActionLatestRecommendedCompatable{
component->getID(),
component->getName(),
component->getVersion(),
} });
}
addedActions = true;
}
}
};
std::visit(visitor, action);
component->clearUpdateAction();
for (auto uid : toRemove) {
@ -690,6 +686,19 @@ void ComponentUpdateTask::finalizeComponents()
}
}
}
for (auto conflict : component->knownConfictingComponents()) {
auto found = componentIndex.find(conflict);
if (found != componentIndex.cend()) {
auto foundComp = *found;
if (foundComp ->isCustom()) {
continue;
}
component->addComponentProblem(
ProblemSeverity::Warning,
QObject::tr("%1 and %2 are known to not work together. It is recommended to remove one of them.")
.arg(component->getName(), foundComp->getName()));
}
}
}
}

View File

@ -71,11 +71,6 @@
#include "ui/dialogs/CustomMessageBox.h"
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
{ "net.minecraftforge", ModPlatform::Forge },
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
{
@ -1040,12 +1035,12 @@ std::optional<ModPlatform::ModLoaderTypes> PackProfile::getModLoaders()
ModPlatform::ModLoaderTypes result;
bool has_any_loader = false;
QMapIterator<QString, ModPlatform::ModLoaderType> i(modloaderMapping);
QMapIterator<QString, ModloaderMapEntry> i(KNOWN_MODLOADERS);
while (i.hasNext()) {
i.next();
if (auto c = getComponent(i.key()); c != nullptr && c->isEnabled()) {
result |= i.value();
result |= i.value().type;
has_any_loader = true;
}
}