added auto updating mods on launch functionality

Signed-off-by: dyredhead <danielyentin@gmail.com>
This commit is contained in:
dyredhead 2026-05-31 16:59:14 -04:00
parent b8d996530c
commit f0d4bba7eb
4 changed files with 162 additions and 0 deletions

View file

@ -255,6 +255,8 @@ set(MINECRAFT_SOURCES
minecraft/update/FoldersTask.h
minecraft/update/LibrariesTask.cpp
minecraft/update/LibrariesTask.h
minecraft/update/ModUpdateTask.cpp
minecraft/update/ModUpdateTask.h
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h

View file

@ -73,6 +73,7 @@
#include "minecraft/update/FoldersTask.h"
#include "minecraft/update/LegacyFMLLibrariesTask.h"
#include "minecraft/update/LibrariesTask.h"
#include "minecraft/update/ModUpdateTask.h"
#include "java/JavaUtils.h"
@ -1208,6 +1209,13 @@ LaunchTask* MinecraftInstance::createLaunchTask(AuthSessionPtr session, Minecraf
process->appendStep(makeShared<ScanModFolders>(pptr));
}
// Update mods if "AutomaticallyUpdateMods" is true.
// Must come after ScanModFolders to ensure mods are loaded.
if (settings()->get("AutomaticallyUpdateMods").toBool()) {
process->appendStep(
makeShared<TaskStepWrapper>(pptr, makeShared<ModUpdateTask>(this, settings()->get("AutomaticallyUpdateModsEnabled").toBool())));
}
// make sure we have enough RAM, warn the user if we don't
{
process->appendStep(makeShared<EnsureAvailableMemory>(pptr, this));

View file

@ -0,0 +1,125 @@
#include "ModUpdateTask.h"
#include <ui/dialogs/ProgressDialog.h>
#include <ui/dialogs/ResourceUpdateDialog.h>
#include "Application.h"
#include "BuildConfig.h"
#include "launch/LaunchStep.h"
#include "minecraft/AssetsUtils.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/mod/Mod.h"
#include "minecraft/mod/ModDetails.h"
#include "minecraft/mod/ModFolderModel.h"
#include "net/ApiDownload.h"
#include "net/ChecksumValidator.h"
#include "ui/dialogs/CustomMessageBox.h"
ModUpdateTask::ModUpdateTask(MinecraftInstance* inst, bool enabledModsOnly)
{
m_instance = inst;
m_enabledModsOnly = enabledModsOnly;
}
void ModUpdateTask::executeTask()
{
setStatus(tr("Updating mods..."));
qDebug() << "Updating mods...";
if (m_instance->typeName() != "Minecraft") {
return; // this is a null instance or a legacy instance
}
auto* profile = static_cast<MinecraftInstance*>(m_instance)->getPackProfile();
if (!profile->getModLoaders().has_value()) {
emitFailed(tr("Mod updates are unavailable when mod loader is missing!"));
return;
}
if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) {
emitFailed(tr("Mod updates are unavailable when metadata is disabled!"));
return;
}
// ResourceUpdateDialog only accepts Resource,
// so we convert the Mod* list to a Resource* list with a simple static_cast
auto model = m_instance->loaderModList();
auto _modsList = model->allMods();
QList<Resource*> modsList;
modsList.reserve(_modsList.size());
for (auto mod : _modsList) {
modsList.append(static_cast<Resource*>(mod));
}
// Filter out disabled mods if "AutomaticallyUpdateModsEnabled" is true
if (m_enabledModsOnly) {
modsList.erase(std::remove_if(modsList.begin(), modsList.end(), [](Resource* resource) { return !resource->enabled(); }),
modsList.end());
}
// Spawn ResourceUpdateDialog to handle mod updates
// 99% copied from ModFolderPage.cpp
auto parent = QApplication::activeWindow();
ResourceUpdateDialog updateDialog =
ResourceUpdateDialog(parent, m_instance, model, modsList, m_includeDeps, profile->getModLoadersList());
updateDialog.checkCandidates();
if (updateDialog.aborted()) {
CustomMessageBox::selectable(parent, tr("Aborted"), tr("The mod updater was aborted!"), QMessageBox::Warning)->show();
emitAborted();
return;
}
if (updateDialog.noUpdates()) {
QString message{ tr("'%1' is up-to-date! :)").arg(modsList.front()->name()) };
if (modsList.size() > 1) {
if (!m_enabledModsOnly) {
message = tr("All mods are up-to-date! :)");
} else {
message = tr("All enabled mods are up-to-date! :)");
}
}
CustomMessageBox::selectable(parent, tr("Update checker"), message)->exec();
emitSucceeded();
return;
}
if (updateDialog.exec() != 0) {
auto* tasks = new ConcurrentTask("Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt());
connect(tasks, &Task::failed, [this, parent, tasks](const QString& reason) {
CustomMessageBox::selectable(parent, tr("Error"), reason, QMessageBox::Critical)->show();
tasks->deleteLater();
});
connect(tasks, &Task::aborted, [this, parent, tasks]() {
CustomMessageBox::selectable(parent, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show();
tasks->deleteLater();
});
connect(tasks, &Task::succeeded, [this, parent, tasks]() {
QStringList warnings = tasks->warnings();
if (warnings.count()) {
CustomMessageBox::selectable(parent, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show();
}
tasks->deleteLater();
});
for (const auto& task : updateDialog.getTasks()) {
tasks->addTask(task);
}
ProgressDialog loadDialog(parent);
loadDialog.setSkipButton(true, tr("Abort"));
loadDialog.execWithTask(tasks);
}
model->update();
qDebug() << "Finished updating mods...";
emitSucceeded();
}
bool ModUpdateTask::canAbort() const
{
return true;
}
bool ModUpdateTask::abort()
{
return true;
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "net/NetJob.h"
#include "tasks/Task.h"
class MinecraftInstance;
class ModUpdateTask : public Task {
Q_OBJECT
public:
ModUpdateTask(MinecraftInstance* inst, bool enabled);
virtual ~ModUpdateTask() = default;
void executeTask() override;
bool canAbort() const override;
public:
static QString resourceUrl();
public slots:
bool abort() override;
private:
MinecraftInstance* m_instance;
bool m_enabledModsOnly;
bool m_includeDeps = true;
};