diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index 67f26a02e..00d3f2e42 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -45,6 +45,7 @@ #include "OneSixVersionFormat.h" #include "VersionFile.h" #include "meta/Version.h" +#include "minecraft/Component.h" #include "minecraft/PackProfile.h" #include @@ -438,14 +439,14 @@ void Component::setUpdateAction(UpdateAction action) m_updateAction = action; } -std::optional Component::getUpdateAction() +UpdateAction Component::getUpdateAction() { return m_updateAction; } void Component::clearUpdateAction() { - m_updateAction.reset(); + m_updateAction = UpdateAction{ UpdateActionNone{} }; } QDebug operator<<(QDebug d, const Component& comp) diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index 50c4e02db..94a964182 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "ProblemProvider.h" #include "QObjectPtr.h" #include "meta/JsonFormat.h" @@ -17,7 +18,7 @@ class VersionList; } // namespace Meta class VersionFile; -struct UpdateActionChangeVerison { +struct UpdateActionChangeVersion { /// version to change to QString targetVersion; }; @@ -34,8 +35,13 @@ struct UpdateActionImportantChanged { QString oldVersion; }; -using UpdateAction = - std::variant; +using UpdateActionNone = std::monostate; + +using UpdateAction = std::variant; class Component : public QObject, public ProblemProvider { Q_OBJECT @@ -92,8 +98,7 @@ class Component : public QObject, public ProblemProvider { void setUpdateAction(UpdateAction action); void clearUpdateAction(); - std::optional getUpdateAction(); - std::optional takeUpdateAction(); + UpdateAction getUpdateAction(); signals: void dataChanged(); @@ -136,7 +141,7 @@ class Component : public QObject, public ProblemProvider { private: QList m_componentProblems; ProblemSeverity m_componentProblemSeverity = ProblemSeverity::None; - std::optional m_updateAction = std::nullopt; + UpdateAction m_updateAction = UpdateAction{ UpdateActionNone{} }; }; using ComponentPtr = shared_qobject_ptr; diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 8f19f20c8..8001627e4 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -153,6 +153,7 @@ void ComponentUpdateTask::loadComponents() Task::Ptr loadTask; LoadResult singleResult; RemoteLoadStatus::Type loadType; + component->resetComponentProblems(); // FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, // ignore all that... #if 0 @@ -445,6 +446,7 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) allRequires.clear(); toRemove.clear(); if (!gatherRequirementsFromComponents(components, allRequires)) { + finalizeComponents(); emitFailed(tr("Conflicting requirements detected during dependency checking!")); return; } @@ -461,10 +463,12 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly) RequireExSet toChange; bool succeeded = getTrivialComponentChanges(componentIndex, allRequires, toAdd, toChange); if (!succeeded) { + finalizeComponents(); emitFailed(tr("Instance has conflicting dependencies.")); return; } if (checkOnly) { + finalizeComponents(); if (toAdd.size() || toChange.size()) { emitFailed(tr("Instance has unresolved dependencies while loading/checking for launch.")); } else { @@ -542,77 +546,115 @@ overload(Ts...) -> overload; void ComponentUpdateTask::performUpdateActions() { - auto& components = d->m_profile->d->components; - + auto& instance = d->m_profile->d->m_instance; bool addedActions; + QStringList toRemove; do { addedActions = false; + toRemove.clear(); + auto& components = d->m_profile->d->components; + auto& componentIndex = d->m_profile->d->componentIndex; for (auto component : components) { - if (auto action = component->getUpdateAction()) { - auto visitor = overload{ - [&component](const UpdateActionChangeVerison& cv) { - component->setVersion(cv.targetVersion); - component->waitLoadMeta(); - }, - [&component](const UpdateActionLatestRecommendedCompatable lrc) { - 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; - } + if (!component) { + 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 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())); - } - }, - [this, &component](const UpdateActionRemove&) { d->m_profile->remove(component->getID()); }, - [this, &component, &addedActions](const UpdateActionImportantChanged&) { - 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{ UpdateActionChangeVerison{ 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(); + 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) { + d->m_profile->remove(uid); } } } while (addedActions); @@ -637,6 +679,15 @@ void ComponentUpdateTask::finalizeComponents() reqComp->getProblemSeverity(), QObject::tr("%1, a dependency of this component, has reported issues").arg(reqComp->getName())); } + if (!req.equalsVersion.isEmpty() && req.equalsVersion != reqComp->getVersion()) { + component->addComponentProblem(ProblemSeverity::Error, + QObject::tr("%1, a dependency of this component, is not the required version %2") + .arg(reqComp->getName(), req.equalsVersion)); + } else if (!req.suggests.isEmpty() && req.suggests != reqComp->getVersion()) { + component->addComponentProblem(ProblemSeverity::Warning, + QObject::tr("%1, a dependency of this component, is not the suggested version %2") + .arg(reqComp->getName(), req.suggests)); + } } } }