diff options
author | Petr Mrázek <peterix@gmail.com> | 2016-08-14 02:33:31 +0200 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2016-08-14 23:22:54 +0200 |
commit | 042f3ef55c0b469f438542152c4eb02b0789ea3c (patch) | |
tree | 03e0c15b200786558babd0fe58edac88ed1bfd1e /api/logic/minecraft/onesix | |
parent | 2f0441b3c1cd9fc3bcb176d2852da8f92a6e6777 (diff) | |
download | MultiMC-042f3ef55c0b469f438542152c4eb02b0789ea3c.tar MultiMC-042f3ef55c0b469f438542152c4eb02b0789ea3c.tar.gz MultiMC-042f3ef55c0b469f438542152c4eb02b0789ea3c.tar.lz MultiMC-042f3ef55c0b469f438542152c4eb02b0789ea3c.tar.xz MultiMC-042f3ef55c0b469f438542152c4eb02b0789ea3c.zip |
GH-352 Make OneSix instance update downloads cancellable
Diffstat (limited to 'api/logic/minecraft/onesix')
-rw-r--r-- | api/logic/minecraft/onesix/OneSixInstance.cpp | 4 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/OneSixInstance.h | 2 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/OneSixUpdate.cpp | 346 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/OneSixUpdate.h | 36 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/AssetUpdateTask.cpp | 99 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/AssetUpdateTask.h | 25 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp | 132 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/FMLLibrariesTask.h | 27 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/FoldersTask.cpp | 20 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/FoldersTask.h | 14 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/LibrariesTask.cpp | 89 | ||||
-rw-r--r-- | api/logic/minecraft/onesix/update/LibrariesTask.h | 24 |
12 files changed, 520 insertions, 298 deletions
diff --git a/api/logic/minecraft/onesix/OneSixInstance.cpp b/api/logic/minecraft/onesix/OneSixInstance.cpp index 8b5491e2..1bf75bdb 100644 --- a/api/logic/minecraft/onesix/OneSixInstance.cpp +++ b/api/logic/minecraft/onesix/OneSixInstance.cpp @@ -60,9 +60,9 @@ QSet<QString> OneSixInstance::traits() } } -std::shared_ptr<Task> OneSixInstance::createUpdateTask() +shared_qobject_ptr<Task> OneSixInstance::createUpdateTask() { - return std::shared_ptr<Task>(new OneSixUpdate(this)); + return shared_qobject_ptr<Task>(new OneSixUpdate(this)); } QString replaceTokensIn(QString text, QMap<QString, QString> with) diff --git a/api/logic/minecraft/onesix/OneSixInstance.h b/api/logic/minecraft/onesix/OneSixInstance.h index 1a917e79..c857075f 100644 --- a/api/logic/minecraft/onesix/OneSixInstance.h +++ b/api/logic/minecraft/onesix/OneSixInstance.h @@ -52,7 +52,7 @@ public: QString worldDir() const; virtual QString instanceConfigFolder() const override; - virtual std::shared_ptr<Task> createUpdateTask() override; + virtual shared_qobject_ptr<Task> createUpdateTask() override; virtual std::shared_ptr<Task> createJarModdingTask() override; virtual QString createLaunchScript(AuthSessionPtr session) override; QStringList verboseDescription(AuthSessionPtr session) override; diff --git a/api/logic/minecraft/onesix/OneSixUpdate.cpp b/api/logic/minecraft/onesix/OneSixUpdate.cpp index d3cd197d..02a6f561 100644 --- a/api/logic/minecraft/onesix/OneSixUpdate.cpp +++ b/api/logic/minecraft/onesix/OneSixUpdate.cpp @@ -18,332 +18,138 @@ #include "OneSixUpdate.h" #include "OneSixInstance.h" -#include <QtNetwork> - #include <QFile> #include <QFileInfo> #include <QTextStream> #include <QDataStream> -#include <JlCompress.h> #include "BaseInstance.h" #include "minecraft/MinecraftVersionList.h" #include "minecraft/MinecraftProfile.h" #include "minecraft/Library.h" #include "net/URLConstants.h" -#include "net/ChecksumValidator.h" -#include "minecraft/AssetsUtils.h" -#include "Exception.h" -#include "MMCZip.h" #include <FileSystem.h> -OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst) -{ -} +#include "update/FoldersTask.h" +#include "update/LibrariesTask.h" +#include "update/FMLLibrariesTask.h" +#include "update/AssetUpdateTask.h" -void OneSixUpdate::executeTask() +OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst) { - // Make directories - QDir mcDir(m_inst->minecraftRoot()); - if (!mcDir.exists() && !mcDir.mkpath(".")) + // create folders { - emitFailed(tr("Failed to create folder for minecraft binaries.")); - return; + m_tasks.append(std::make_shared<FoldersTask>(m_inst)); } - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", m_inst->intendedVersionId())); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitFailed(tr("The specified Minecraft version is invalid. Choose a different one.")); - return; - } - if (m_inst->providesVersionFile() || !targetVersion->needsUpdate()) + // add a version update task, if necessary { - qDebug() << "Instance either provides a version file or doesn't need an update."; - jarlibStart(); - return; + auto list = std::dynamic_pointer_cast<MinecraftVersionList>(ENV.getVersionList("net.minecraft")); + auto version = std::dynamic_pointer_cast<MinecraftVersion>(list->findVersion(m_inst->intendedVersionId())); + if (version == nullptr) + { + // don't do anything if it was invalid + m_preFailure = tr("The specified Minecraft version is invalid. Choose a different one."); + } + else if (m_inst->providesVersionFile() || !version->needsUpdate()) + { + qDebug() << "Instance either provides a version file or doesn't need an update."; + } + else + { + auto versionUpdateTask = list->createUpdateTask(m_inst->intendedVersionId()); + if (!versionUpdateTask) + { + qDebug() << "Didn't spawn an update task."; + } + else + { + m_tasks.append(versionUpdateTask); + } + } } - versionUpdateTask = std::dynamic_pointer_cast<MinecraftVersionList>(ENV.getVersionList("net.minecraft"))->createUpdateTask(m_inst->intendedVersionId()); - if (!versionUpdateTask) + + // libraries download { - qDebug() << "Didn't spawn an update task."; - jarlibStart(); - return; + m_tasks.append(std::make_shared<LibrariesTask>(m_inst)); } - connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart())); - connect(versionUpdateTask.get(), &NetJob::failed, this, &OneSixUpdate::versionUpdateFailed); - connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - setStatus(tr("Getting the version files from Mojang...")); - versionUpdateTask->start(); -} -void OneSixUpdate::versionUpdateFailed(QString reason) -{ - emitFailed(reason); -} - -void OneSixUpdate::assetIndexStart() -{ - setStatus(tr("Updating assets index...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto profile = inst->getMinecraftProfile(); - auto assets = profile->getMinecraftAssets(); - QUrl indexUrl = assets->url; - QString localPath = assets->id + ".json"; - auto job = new NetJob(tr("Asset index for %1").arg(inst->name())); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("asset_indexes", localPath); - entry->setStale(true); - auto hexSha1 = assets->sha1.toLatin1(); - qDebug() << "Asset index SHA1:" << hexSha1; - auto dl = Net::Download::makeCached(indexUrl, entry); - auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); - job->addNetAction(dl); - - jarlibDownloadJob.reset(job); - - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetIndexFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetIndexFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - - qDebug() << m_inst->name() << ": Starting asset index download"; - jarlibDownloadJob->start(); -} - -void OneSixUpdate::assetIndexFinished() -{ - AssetsIndex index; - qDebug() << m_inst->name() << ": Finished asset index download"; - - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto profile = inst->getMinecraftProfile(); - auto assets = profile->getMinecraftAssets(); - - QString asset_fname = "assets/indexes/" + assets->id + ".json"; - // FIXME: this looks like a job for a generic validator based on json schema? - if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index)) + // FML libraries download and copy into the instance { - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); - metacache->evictEntry(entry); - emitFailed(tr("Failed to read the assets index!")); + m_tasks.append(std::make_shared<FMLLibrariesTask>(m_inst)); } - auto job = index.getDownloadJob(); - if(job) + // assets update { - setStatus(tr("Getting the assets files from Mojang...")); - jarlibDownloadJob = job; - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::assetsFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - jarlibDownloadJob->start(); - return; + m_tasks.append(std::make_shared<AssetUpdateTask>(m_inst)); } - assetsFinished(); -} - -void OneSixUpdate::assetIndexFailed(QString reason) -{ - qDebug() << m_inst->name() << ": Failed asset index download"; - emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); -} - -void OneSixUpdate::assetsFinished() -{ - emitSucceeded(); -} - -void OneSixUpdate::assetsFailed(QString reason) -{ - emitFailed(tr("Failed to download assets:\n%1").arg(reason)); } -void OneSixUpdate::jarlibStart() +void OneSixUpdate::executeTask() { - setStatus(tr("Getting the library files from Mojang...")); - qDebug() << m_inst->name() << ": downloading libraries"; - OneSixInstance *inst = (OneSixInstance *)m_inst; - inst->reloadProfile(); - if(inst->flags() & BaseInstance::VersionBrokenFlag) + if(!m_preFailure.isEmpty()) { - emitFailed(tr("Failed to load the version description files - check the instance for errors.")); + emitFailed(m_preFailure); return; } + next(); +} - // Build a list of URLs that will need to be downloaded. - std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); - // minecraft.jar for this version +void OneSixUpdate::next() +{ + if(m_abort) { - QString version_id = profile->getMinecraftVersion(); - QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = profile->getMainJarUrl(); - - auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name())); - - auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - job->addNetAction(Net::Download::makeCached(QUrl(urlstr), entry)); - jarlibDownloadJob.reset(job); + emitFailed(tr("Aborted by user.")); + return; } - - auto libs = profile->getLibraries(); - - auto metacache = ENV.metacache(); - QList<LibraryPtr> brokenLocalLibs; - - QStringList failedFiles; - for (auto lib : libs) + m_currentTask ++; + if(m_currentTask > 0) { - auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles); - for(auto dl : dls) - { - jarlibDownloadJob->addNetAction(dl); - } + auto task = m_tasks[m_currentTask - 1]; + disconnect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); + disconnect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); + disconnect(task.get(), &Task::progress, this, &OneSixUpdate::progress); + disconnect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); } - if (!brokenLocalLibs.empty()) + if(m_currentTask == m_tasks.size()) { - jarlibDownloadJob.reset(); - - QString failed_all = failedFiles.join("\n"); - emitFailed(tr("Some libraries marked as 'local' are missing their jar " - "files:\n%1\n\nYou'll have to correct this problem manually. If this is " - "an externally tracked instance, make sure to run it at least once " - "outside of MultiMC.").arg(failed_all)); + emitSucceeded(); return; } - - connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::jarlibFailed); - connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - - jarlibDownloadJob->start(); + auto task = m_tasks[m_currentTask]; + connect(task.get(), &Task::succeeded, this, &OneSixUpdate::subtaskSucceeded); + connect(task.get(), &Task::failed, this, &OneSixUpdate::subtaskFailed); + connect(task.get(), &Task::progress, this, &OneSixUpdate::progress); + connect(task.get(), &Task::status, this, &OneSixUpdate::setStatus); + task->start(); } -void OneSixUpdate::jarlibFinished() +void OneSixUpdate::subtaskSucceeded() { - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); - - if (profile->hasTrait("legacyFML")) - { - fmllibsStart(); - } - else - { - assetIndexStart(); - } + next(); } -void OneSixUpdate::jarlibFailed(QString reason) +void OneSixUpdate::subtaskFailed(QString error) { - QStringList failed = jarlibDownloadJob->getFailedFiles(); - QString failed_all = failed.join("\n"); - emitFailed( - tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason)); + emitFailed(error); } -void OneSixUpdate::fmllibsStart() -{ - // Get the mod list - OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); - bool forge_present = false; - - QString version = inst->intendedVersionId(); - auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; - if (!fmlLibsMapping.contains(version)) - { - assetIndexStart(); - return; - } - - auto &libList = fmlLibsMapping[version]; - - // determine if we need some libs for FML or forge - setStatus(tr("Checking for FML libraries...")); - forge_present = (profile->versionPatch("net.minecraftforge") != nullptr); - // we don't... - if (!forge_present) - { - assetIndexStart(); - return; - } - - // now check the lib folder inside the instance for files. - for (auto &lib : libList) - { - QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename)); - if (libInfo.exists()) - continue; - fmlLibsToProcess.append(lib); - } - // if everything is in place, there's nothing to do here... - if (fmlLibsToProcess.isEmpty()) - { - assetIndexStart(); - return; - } - - // download missing libs to our place - setStatus(tr("Dowloading FML libraries...")); - auto dljob = new NetJob("FML libraries"); - auto metacache = ENV.metacache(); - for (auto &lib : fmlLibsToProcess) - { - auto entry = metacache->resolveEntry("fmllibs", lib.filename); - QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename - : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename; - dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry)); - } - - connect(dljob, SIGNAL(succeeded()), SLOT(fmllibsFinished())); - connect(dljob, &NetJob::failed, this, &OneSixUpdate::fmllibsFailed); - connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); - legacyDownloadJob.reset(dljob); - legacyDownloadJob->start(); -} - -void OneSixUpdate::fmllibsFinished() +bool OneSixUpdate::abort() { - legacyDownloadJob.reset(); - if (!fmlLibsToProcess.isEmpty()) + if(!m_abort) { - setStatus(tr("Copying FML libraries into the instance...")); - OneSixInstance *inst = (OneSixInstance *)m_inst; - auto metacache = ENV.metacache(); - int index = 0; - for (auto &lib : fmlLibsToProcess) + m_abort = true; + auto task = m_tasks[m_currentTask]; + if(task->canAbort()) { - progress(index, fmlLibsToProcess.size()); - auto entry = metacache->resolveEntry("fmllibs", lib.filename); - auto path = FS::PathCombine(inst->libDir(), lib.filename); - if (!FS::ensureFilePathExists(path)) - { - emitFailed(tr("Failed creating FML library folder inside the instance.")); - return; - } - if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) - { - emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); - return; - } - index++; + return task->abort(); } - progress(index, fmlLibsToProcess.size()); } - assetIndexStart(); + return true; } -void OneSixUpdate::fmllibsFailed(QString reason) +bool OneSixUpdate::canAbort() const { - emitFailed(tr("Game update failed: it was impossible to fetch the required FML libraries.\nReason:\n%1").arg(reason)); - return; + return true; } - diff --git a/api/logic/minecraft/onesix/OneSixUpdate.h b/api/logic/minecraft/onesix/OneSixUpdate.h index b5195364..3780ef2e 100644 --- a/api/logic/minecraft/onesix/OneSixUpdate.h +++ b/api/logic/minecraft/onesix/OneSixUpdate.h @@ -32,36 +32,22 @@ class OneSixUpdate : public Task Q_OBJECT public: explicit OneSixUpdate(OneSixInstance *inst, QObject *parent = 0); - virtual void executeTask(); + void executeTask() override; + bool canAbort() const override; private slots: - void versionUpdateFailed(QString reason); - - void jarlibStart(); - void jarlibFinished(); - void jarlibFailed(QString reason); - - void fmllibsStart(); - void fmllibsFinished(); - void fmllibsFailed(QString reason); - - void assetIndexStart(); - void assetIndexFinished(); - void assetIndexFailed(QString reason); - - void assetsFinished(); - void assetsFailed(QString reason); + bool abort() override; + void subtaskSucceeded(); + void subtaskFailed(QString error); private: - NetJobPtr jarlibDownloadJob; - NetJobPtr legacyDownloadJob; - - /// target version, determined during this task - std::shared_ptr<MinecraftVersion> targetVersion; - /// the task that is spawned for version updates - std::shared_ptr<Task> versionUpdateTask; + void next(); +private: OneSixInstance *m_inst = nullptr; - QList<FMLlib> fmlLibsToProcess; + QList<std::shared_ptr<Task>> m_tasks; + QString m_preFailure; + int m_currentTask = -1; + bool m_abort = false; }; diff --git a/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp b/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp new file mode 100644 index 00000000..21600ff0 --- /dev/null +++ b/api/logic/minecraft/onesix/update/AssetUpdateTask.cpp @@ -0,0 +1,99 @@ +#include "Env.h" +#include "AssetUpdateTask.h" +#include "minecraft/onesix/OneSixInstance.h" +#include "net/ChecksumValidator.h" +#include "minecraft/AssetsUtils.h" + +AssetUpdateTask::AssetUpdateTask(OneSixInstance * inst) +{ + m_inst = inst; +} +void AssetUpdateTask::executeTask() +{ + setStatus(tr("Updating assets index...")); + auto profile = m_inst->getMinecraftProfile(); + auto assets = profile->getMinecraftAssets(); + QUrl indexUrl = assets->url; + QString localPath = assets->id + ".json"; + auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name())); + + auto metacache = ENV.metacache(); + auto entry = metacache->resolveEntry("asset_indexes", localPath); + entry->setStale(true); + auto hexSha1 = assets->sha1.toLatin1(); + qDebug() << "Asset index SHA1:" << hexSha1; + auto dl = Net::Download::makeCached(indexUrl, entry); + auto rawSha1 = QByteArray::fromHex(assets->sha1.toLatin1()); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + job->addNetAction(dl); + + downloadJob.reset(job); + + connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::assetIndexFinished); + connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); + connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); + + qDebug() << m_inst->name() << ": Starting asset index download"; + downloadJob->start(); +} + +bool AssetUpdateTask::canAbort() const +{ + return true; +} + +void AssetUpdateTask::assetIndexFinished() +{ + AssetsIndex index; + qDebug() << m_inst->name() << ": Finished asset index download"; + + auto profile = m_inst->getMinecraftProfile(); + auto assets = profile->getMinecraftAssets(); + + QString asset_fname = "assets/indexes/" + assets->id + ".json"; + // FIXME: this looks like a job for a generic validator based on json schema? + if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, &index)) + { + auto metacache = ENV.metacache(); + auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json"); + metacache->evictEntry(entry); + emitFailed(tr("Failed to read the assets index!")); + } + + auto job = index.getDownloadJob(); + if(job) + { + setStatus(tr("Getting the assets files from Mojang...")); + downloadJob = job; + connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded); + connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); + connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); + downloadJob->start(); + return; + } + emitSucceeded(); +} + +void AssetUpdateTask::assetIndexFailed(QString reason) +{ + qDebug() << m_inst->name() << ": Failed asset index download"; + emitFailed(tr("Failed to download the assets index:\n%1").arg(reason)); +} + +void AssetUpdateTask::assetsFailed(QString reason) +{ + emitFailed(tr("Failed to download assets:\n%1").arg(reason)); +} + +bool AssetUpdateTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted FMLLibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/onesix/update/AssetUpdateTask.h b/api/logic/minecraft/onesix/update/AssetUpdateTask.h new file mode 100644 index 00000000..dff72571 --- /dev/null +++ b/api/logic/minecraft/onesix/update/AssetUpdateTask.h @@ -0,0 +1,25 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class OneSixInstance; + +class AssetUpdateTask : public Task +{ +public: + AssetUpdateTask(OneSixInstance * inst); + void executeTask() override; + + bool canAbort() const override; + +private slots: + void assetIndexFinished(); + void assetIndexFailed(QString reason); + void assetsFailed(QString reason); + +public slots: + bool abort() override; + +private: + OneSixInstance *m_inst; + NetJobPtr downloadJob; +}; diff --git a/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp b/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp new file mode 100644 index 00000000..1cbee95e --- /dev/null +++ b/api/logic/minecraft/onesix/update/FMLLibrariesTask.cpp @@ -0,0 +1,132 @@ +#include "Env.h" +#include <FileSystem.h> +#include <minecraft/VersionFilterData.h> +#include "FMLLibrariesTask.h" +#include "minecraft/onesix/OneSixInstance.h" + + +FMLLibrariesTask::FMLLibrariesTask(OneSixInstance * inst) +{ + m_inst = inst; +} +void FMLLibrariesTask::executeTask() +{ + // Get the mod list + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); + bool forge_present = false; + + if (!profile->hasTrait("legacyFML")) + { + emitSucceeded(); + } + + QString version = inst->intendedVersionId(); + auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; + if (!fmlLibsMapping.contains(version)) + { + emitSucceeded(); + return; + } + + auto &libList = fmlLibsMapping[version]; + + // determine if we need some libs for FML or forge + setStatus(tr("Checking for FML libraries...")); + forge_present = (profile->versionPatch("net.minecraftforge") != nullptr); + // we don't... + if (!forge_present) + { + emitSucceeded(); + return; + } + + // now check the lib folder inside the instance for files. + for (auto &lib : libList) + { + QFileInfo libInfo(FS::PathCombine(inst->libDir(), lib.filename)); + if (libInfo.exists()) + continue; + fmlLibsToProcess.append(lib); + } + + // if everything is in place, there's nothing to do here... + if (fmlLibsToProcess.isEmpty()) + { + emitSucceeded(); + return; + } + + // download missing libs to our place + setStatus(tr("Dowloading FML libraries...")); + auto dljob = new NetJob("FML libraries"); + auto metacache = ENV.metacache(); + for (auto &lib : fmlLibsToProcess) + { + auto entry = metacache->resolveEntry("fmllibs", lib.filename); + QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename + : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename; + dljob->addNetAction(Net::Download::makeCached(QUrl(urlString), entry)); + } + + connect(dljob, &NetJob::succeeded, this, &FMLLibrariesTask::fmllibsFinished); + connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); + connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress); + downloadJob.reset(dljob); + downloadJob->start(); +} + +bool FMLLibrariesTask::canAbort() const +{ + return true; +} + +void FMLLibrariesTask::fmllibsFinished() +{ + downloadJob.reset(); + if (!fmlLibsToProcess.isEmpty()) + { + setStatus(tr("Copying FML libraries into the instance...")); + OneSixInstance *inst = (OneSixInstance *)m_inst; + auto metacache = ENV.metacache(); + int index = 0; + for (auto &lib : fmlLibsToProcess) + { + progress(index, fmlLibsToProcess.size()); + auto entry = metacache->resolveEntry("fmllibs", lib.filename); + auto path = FS::PathCombine(inst->libDir(), lib.filename); + if (!FS::ensureFilePathExists(path)) + { + emitFailed(tr("Failed creating FML library folder inside the instance.")); + return; + } + if (!QFile::copy(entry->getFullPath(), FS::PathCombine(inst->libDir(), lib.filename))) + { + emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename)); + return; + } + index++; + } + progress(index, fmlLibsToProcess.size()); + } + emitSucceeded(); +} +void FMLLibrariesTask::fmllibsFailed(QString reason) +{ + QStringList failed = downloadJob->getFailedFiles(); + QString failed_all = failed.join("\n"); + emitFailed(tr("Failed to download the following files:\n%1\n\nReason:%2\nPlease try again.").arg(failed_all, reason)); +} + +bool FMLLibrariesTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted FMLLibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/onesix/update/FMLLibrariesTask.h b/api/logic/minecraft/onesix/update/FMLLibrariesTask.h new file mode 100644 index 00000000..d1c250e4 --- /dev/null +++ b/api/logic/minecraft/onesix/update/FMLLibrariesTask.h @@ -0,0 +1,27 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class OneSixInstance; + +class FMLLibrariesTask : public Task +{ +public: + FMLLibrariesTask(OneSixInstance * inst); + + void executeTask() override; + + bool canAbort() const override; + +private slots: + void fmllibsFinished(); + void fmllibsFailed(QString reason); + +public slots: + bool abort() override; + +private: + OneSixInstance *m_inst; + NetJobPtr downloadJob; + QList<FMLlib> fmlLibsToProcess; +}; + diff --git a/api/logic/minecraft/onesix/update/FoldersTask.cpp b/api/logic/minecraft/onesix/update/FoldersTask.cpp new file mode 100644 index 00000000..239a2675 --- /dev/null +++ b/api/logic/minecraft/onesix/update/FoldersTask.cpp @@ -0,0 +1,20 @@ +#include "FoldersTask.h" +#include "minecraft/onesix/OneSixInstance.h" +#include <QDir> + +FoldersTask::FoldersTask(OneSixInstance * inst) +{ + m_inst = inst; +} + +void FoldersTask::executeTask() +{ + // Make directories + QDir mcDir(m_inst->minecraftRoot()); + if (!mcDir.exists() && !mcDir.mkpath(".")) + { + emitFailed(tr("Failed to create folder for minecraft binaries.")); + return; + } + emitSucceeded(); +} diff --git a/api/logic/minecraft/onesix/update/FoldersTask.h b/api/logic/minecraft/onesix/update/FoldersTask.h new file mode 100644 index 00000000..552d3098 --- /dev/null +++ b/api/logic/minecraft/onesix/update/FoldersTask.h @@ -0,0 +1,14 @@ +#pragma once + +#include "tasks/Task.h" + +class OneSixInstance; +class FoldersTask : public Task +{ +public: + FoldersTask(OneSixInstance * inst); + void executeTask() override; +private: + OneSixInstance *m_inst; +}; + diff --git a/api/logic/minecraft/onesix/update/LibrariesTask.cpp b/api/logic/minecraft/onesix/update/LibrariesTask.cpp new file mode 100644 index 00000000..123b14a3 --- /dev/null +++ b/api/logic/minecraft/onesix/update/LibrariesTask.cpp @@ -0,0 +1,89 @@ +#include "Env.h" +#include "LibrariesTask.h" +#include "minecraft/onesix/OneSixInstance.h" + +LibrariesTask::LibrariesTask(OneSixInstance * inst) +{ + m_inst = inst; +} + +void LibrariesTask::executeTask() +{ + setStatus(tr("Getting the library files from Mojang...")); + qDebug() << m_inst->name() << ": downloading libraries"; + OneSixInstance *inst = (OneSixInstance *)m_inst; + inst->reloadProfile(); + if(inst->flags() & BaseInstance::VersionBrokenFlag) + { + emitFailed(tr("Failed to load the version description files - check the instance for errors.")); + return; + } + + // Build a list of URLs that will need to be downloaded. + std::shared_ptr<MinecraftProfile> profile = inst->getMinecraftProfile(); + // minecraft.jar for this version + { + QString version_id = profile->getMinecraftVersion(); + QString localPath = version_id + "/" + version_id + ".jar"; + QString urlstr = profile->getMainJarUrl(); + + auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name())); + + auto metacache = ENV.metacache(); + auto entry = metacache->resolveEntry("versions", localPath); + job->addNetAction(Net::Download::makeCached(QUrl(urlstr), entry)); + downloadJob.reset(job); + } + + auto libs = profile->getLibraries(); + + auto metacache = ENV.metacache(); + QList<LibraryPtr> brokenLocalLibs; + QStringList failedFiles; + for (auto lib : libs) + { + auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles); + for(auto dl : dls) + { + downloadJob->addNetAction(dl); + } + } + // FIXME: this is never filled!!!! + if (!brokenLocalLibs.empty()) + { + downloadJob.reset(); + QString failed_all = failedFiles.join("\n"); + emitFailed(tr("Some libraries marked as 'local' are missing their jar " + "files:\n%1\n\nYou'll have to correct this problem manually. If this is " + "an externally tracked instance, make sure to run it at least once " + "outside of MultiMC.").arg(failed_all)); + return; + } + connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded); + connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); + connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); + downloadJob->start(); +} + +bool LibrariesTask::canAbort() const +{ + return true; +} + +void LibrariesTask::jarlibFailed(QString reason) +{ + emitFailed(tr("Game update failed: it was impossible to fetch the required libraries.\nReason:\n%1").arg(reason)); +} + +bool LibrariesTask::abort() +{ + if(downloadJob) + { + return downloadJob->abort(); + } + else + { + qWarning() << "Prematurely aborted LibrariesTask"; + } + return true; +} diff --git a/api/logic/minecraft/onesix/update/LibrariesTask.h b/api/logic/minecraft/onesix/update/LibrariesTask.h new file mode 100644 index 00000000..80cf0d2a --- /dev/null +++ b/api/logic/minecraft/onesix/update/LibrariesTask.h @@ -0,0 +1,24 @@ +#pragma once +#include "tasks/Task.h" +#include "net/NetJob.h" +class OneSixInstance; + +class LibrariesTask : public Task +{ +public: + LibrariesTask(OneSixInstance * inst); + + void executeTask() override; + + bool canAbort() const override; + +private slots: + void jarlibFailed(QString reason); + +public slots: + bool abort() override; + +private: + OneSixInstance *m_inst; + NetJobPtr downloadJob; +}; |