From 13a7f8d3b7b7d92387099141fad81ca74adedf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 9 Feb 2021 05:04:23 +0100 Subject: [PATCH] NOISSUE fix multiple issues in ATLauncher integration --- api/logic/InstanceImportTask.cpp | 2 +- api/logic/InstanceImportTask.h | 6 +- api/logic/MMCZip.cpp | 47 ++++- api/logic/MMCZip.h | 7 +- api/logic/minecraft/World.cpp | 2 +- .../atlauncher/ATLPackInstallTask.cpp | 178 ++++++++++-------- .../atlauncher/ATLPackInstallTask.h | 22 ++- .../modplatform/legacy_ftb/PackInstallTask.h | 6 +- .../technic/SingleZipPackInstallTask.cpp | 2 +- .../technic/SingleZipPackInstallTask.h | 6 +- .../technic/SolderPackInstallTask.cpp | 2 +- 11 files changed, 175 insertions(+), 105 deletions(-) diff --git a/api/logic/InstanceImportTask.cpp b/api/logic/InstanceImportTask.cpp index bd98f9d4..fe2cdd75 100644 --- a/api/logic/InstanceImportTask.cpp +++ b/api/logic/InstanceImportTask.cpp @@ -138,7 +138,7 @@ void InstanceImportTask::processZipPack() void InstanceImportTask::extractFinished() { m_packZip.reset(); - if (m_extractFuture.result().isEmpty()) + if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; diff --git a/api/logic/InstanceImportTask.h b/api/logic/InstanceImportTask.h index db658808..7291324d 100644 --- a/api/logic/InstanceImportTask.h +++ b/api/logic/InstanceImportTask.h @@ -24,6 +24,8 @@ #include "settings/SettingsObject.h" #include "QObjectPtr.h" +#include + class QuaZip; namespace Flame { @@ -60,8 +62,8 @@ private: /* data */ QString m_archivePath; bool m_downloadRequired = false; std::unique_ptr m_packZip; - QFuture m_extractFuture; - QFutureWatcher m_extractFutureWatcher; + QFuture> m_extractFuture; + QFutureWatcher> m_extractFutureWatcher; enum class ModpackType{ Unknown, MultiMC, diff --git a/api/logic/MMCZip.cpp b/api/logic/MMCZip.cpp index 50b95c8e..b25c61e7 100644 --- a/api/logic/MMCZip.cpp +++ b/api/logic/MMCZip.cpp @@ -208,16 +208,27 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re // ours -QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) +nonstd::optional MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target) { QDir directory(target); QStringList extracted; + qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target; - if (!zip->goToFirstFile()) + auto numEntries = zip->getEntriesCount(); + if(numEntries < 0) { + qWarning() << "Failed to enumerate files in archive"; + return nonstd::nullopt; + } + else if(numEntries == 0) { + qDebug() << "Extracting empty archives seems odd..."; + return extracted; + } + else if (!zip->goToFirstFile()) { qWarning() << "Failed to seek to first file in zip"; - return QStringList(); + return nonstd::nullopt; } + do { QString name = zip->getCurrentFileName(); @@ -235,7 +246,7 @@ QStringList MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QSt { qWarning() << "Failed to extract file" << name << "to" << absFilePath; JlCompress::removeFile(extracted); - return QStringList(); + return nonstd::nullopt; } extracted.append(absFilePath); qDebug() << "Extracted file" << name; @@ -250,23 +261,35 @@ bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &tar } // ours -QStringList MMCZip::extractDir(QString fileCompressed, QString dir) +nonstd::optional MMCZip::extractDir(QString fileCompressed, QString dir) { QuaZip zip(fileCompressed); if (!zip.open(QuaZip::mdUnzip)) { - return {}; + // check if this is a minimum size empty zip file... + QFileInfo fileInfo(fileCompressed); + if(fileInfo.size() == 22) { + return QStringList(); + } + qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; + return nonstd::nullopt; } return MMCZip::extractSubDir(&zip, "", dir); } // ours -QStringList MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) +nonstd::optional MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir) { QuaZip zip(fileCompressed); if (!zip.open(QuaZip::mdUnzip)) { - return {}; + // check if this is a minimum size empty zip file... + QFileInfo fileInfo(fileCompressed); + if(fileInfo.size() == 22) { + return QStringList(); + } + qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();; + return nonstd::nullopt; } return MMCZip::extractSubDir(&zip, subdir, dir); } @@ -277,7 +300,13 @@ bool MMCZip::extractFile(QString fileCompressed, QString file, QString target) QuaZip zip(fileCompressed); if (!zip.open(QuaZip::mdUnzip)) { - return {}; + // check if this is a minimum size empty zip file... + QFileInfo fileInfo(fileCompressed); + if(fileInfo.size() == 22) { + return true; + } + qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError(); + return false; } return MMCZip::extractRelFile(&zip, file, target); } diff --git a/api/logic/MMCZip.h b/api/logic/MMCZip.h index beff2e4d..98d9cd5b 100644 --- a/api/logic/MMCZip.h +++ b/api/logic/MMCZip.h @@ -24,6 +24,7 @@ #include "multimc_logic_export.h" #include +#include namespace MMCZip { @@ -57,7 +58,7 @@ namespace MMCZip /** * Extract a subdirectory from an archive */ - QStringList MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); + nonstd::optional MULTIMC_LOGIC_EXPORT extractSubDir(QuaZip *zip, const QString & subdir, const QString &target); bool MULTIMC_LOGIC_EXPORT extractRelFile(QuaZip *zip, const QString & file, const QString &target); @@ -68,7 +69,7 @@ namespace MMCZip * \param dir The directory to extract to, the current directory if left empty. * \return The list of the full paths of the files extracted, empty on failure. */ - QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir); + nonstd::optional MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString dir); /** * Extract a subdirectory from an archive @@ -78,7 +79,7 @@ namespace MMCZip * \param dir The directory to extract to, the current directory if left empty. * \return The list of the full paths of the files extracted, empty on failure. */ - QStringList MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString subdir, QString dir); + nonstd::optional MULTIMC_LOGIC_EXPORT extractDir(QString fileCompressed, QString subdir, QString dir); /** * Extract a single file from an archive into a directory diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp index ddeaa3b6..a2b4dac7 100644 --- a/api/logic/minecraft/World.cpp +++ b/api/logic/minecraft/World.cpp @@ -289,7 +289,7 @@ bool World::install(const QString &to, const QString &name) { return false; } - ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath).isEmpty(); + ok = !MMCZip::extractSubDir(&zip, m_containerOffsetPath, finalPath); } else if(m_containerFile.isDir()) { diff --git a/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp b/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp index 9719cef3..faa05e84 100644 --- a/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/api/logic/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -31,6 +31,7 @@ bool PackInstallTask::abort() void PackInstallTask::executeTask() { + qDebug() << "PackInstallTask::executeTask: " << QThread::currentThreadId(); auto *netJob = new NetJob("ATLauncher::VersionFetch"); auto searchUrl = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.json") .arg(m_pack).arg(m_version_name); @@ -44,6 +45,7 @@ void PackInstallTask::executeTask() void PackInstallTask::onDownloadSucceeded() { + qDebug() << "PackInstallTask::onDownloadSucceeded: " << QThread::currentThreadId(); jobPtr.reset(); QJsonParseError parse_error; @@ -84,7 +86,7 @@ void PackInstallTask::onDownloadSucceeded() minecraftVersion = ver; if(m_version.noConfigs) { - installMods(); + downloadMods(); } else { installConfigs(); @@ -93,6 +95,7 @@ void PackInstallTask::onDownloadSucceeded() void PackInstallTask::onDownloadFailed(QString reason) { + qDebug() << "PackInstallTask::onDownloadFailed: " << QThread::currentThreadId(); jobPtr.reset(); emitFailed(reason); } @@ -360,6 +363,7 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr< void PackInstallTask::installConfigs() { + qDebug() << "PackInstallTask::installConfigs: " << QThread::currentThreadId(); setStatus(tr("Downloading configs...")); jobPtr.reset(new NetJob(tr("Config download"))); @@ -392,6 +396,7 @@ void PackInstallTask::installConfigs() void PackInstallTask::extractConfigs() { + qDebug() << "PackInstallTask::extractConfigs: " << QThread::currentThreadId(); setStatus(tr("Extracting configs...")); QDir extractDir(m_stagingPath); @@ -406,7 +411,7 @@ void PackInstallTask::extractConfigs() m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft"); connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, [&]() { - installMods(); + downloadMods(); }); connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, [&]() { @@ -415,8 +420,9 @@ void PackInstallTask::extractConfigs() m_extractFutureWatcher.setFuture(m_extractFuture); } -void PackInstallTask::installMods() +void PackInstallTask::downloadMods() { + qDebug() << "PackInstallTask::installMods: " << QThread::currentThreadId(); setStatus(tr("Downloading mods...")); jarmods.clear(); @@ -464,12 +470,17 @@ void PackInstallTask::installMods() else { auto relpath = getDirForModType(mod.type, mod.type_raw); if(relpath == Q_NULLPTR) continue; - auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); - qDebug() << "Will download" << url << "to" << path; - auto dl = Net::Download::makeFile(url, path); + auto entry = ENV.metacache()->resolveEntry("ATLauncherPacks", cacheName); + entry->setStale(true); + + auto dl = Net::Download::makeCached(url, entry); jobPtr->addNetAction(dl); + auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file); + qDebug() << "Will download" << url << "to" << path; + modsToCopy[entry->getFullPath()] = path; + if(mod.type == ModType::Forge) { auto vlist = ENV.metadataIndex()->get("net.minecraftforge"); if(vlist) @@ -493,11 +504,7 @@ void PackInstallTask::installMods() } } - connect(jobPtr.get(), &NetJob::succeeded, this, [&]() - { - jobPtr.reset(); - extractMods(); - }); + connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded); connect(jobPtr.get(), &NetJob::failed, [&](QString reason) { jobPtr.reset(); @@ -511,88 +518,103 @@ void PackInstallTask::installMods() jobPtr->start(); } -void PackInstallTask::extractMods() -{ - setStatus(tr("Extracting mods...")); +void PackInstallTask::onModsDownloaded() { + qDebug() << "PackInstallTask::onModsDownloaded: " << QThread::currentThreadId(); + jobPtr.reset(); - if(modsToExtract.isEmpty()) { - decompMods(); - return; + if(modsToExtract.size() || modsToDecomp.size() || modsToCopy.size()) { + m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy); + connect(&m_modExtractFutureWatcher, &QFutureWatcher::finished, this, &PackInstallTask::onModsExtracted); + connect(&m_modExtractFutureWatcher, &QFutureWatcher::canceled, this, [&]() + { + emitAborted(); + }); + m_modExtractFutureWatcher.setFuture(m_modExtractFuture); } - - auto modPath = modsToExtract.firstKey(); - auto mod = modsToExtract.value(modPath); - - QString extractToDir; - if(mod.type == ModType::Extract) { - extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw); + else { + install(); } - else if(mod.type == ModType::TexturePackExtract) { - extractToDir = FS::PathCombine("texturepacks", "extracted"); - } - else if(mod.type == ModType::ResourcePackExtract) { - extractToDir = FS::PathCombine("resourcepacks", "extracted"); - } - - qDebug() << "Extracting " + mod.file + " to " + extractToDir; - - QDir extractDir(m_stagingPath); - auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir); - - QString folderToExtract = ""; - if(mod.type == ModType::Extract) { - folderToExtract = mod.extractFolder; - } - - m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, modPath, folderToExtract, extractToPath); - connect(&m_extractFutureWatcher, &QFutureWatcher::finished, this, [&]() - { - extractMods(); - }); - connect(&m_extractFutureWatcher, &QFutureWatcher::canceled, this, [&]() - { - emitAborted(); - }); - m_extractFutureWatcher.setFuture(m_extractFuture); - - modsToExtract.remove(modPath); } -void PackInstallTask::decompMods() -{ - setStatus(tr("Extracting 'decomp' mods...")); - - if(modsToDecomp.isEmpty()) { +void PackInstallTask::onModsExtracted() { + qDebug() << "PackInstallTask::onModsExtracted: " << QThread::currentThreadId(); + if(m_modExtractFuture.result()) { install(); - return; + } + else { + emitFailed(tr("Failed to extract mods...")); + } +} + +bool PackInstallTask::extractMods( + const QMap &toExtract, + const QMap &toDecomp, + const QMap &toCopy +) { + qDebug() << "PackInstallTask::extractMods: " << QThread::currentThreadId(); + + setStatus(tr("Extracting mods...")); + for (auto iter = toExtract.begin(); iter != toExtract.end(); iter++) { + auto &modPath = iter.key(); + auto &mod = iter.value(); + + QString extractToDir; + if(mod.type == ModType::Extract) { + extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw); + } + else if(mod.type == ModType::TexturePackExtract) { + extractToDir = FS::PathCombine("texturepacks", "extracted"); + } + else if(mod.type == ModType::ResourcePackExtract) { + extractToDir = FS::PathCombine("resourcepacks", "extracted"); + } + + QDir extractDir(m_stagingPath); + auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir); + + QString folderToExtract = ""; + if(mod.type == ModType::Extract) { + folderToExtract = mod.extractFolder; + folderToExtract.remove(QRegExp("^/")); + } + + qDebug() << "Extracting " + mod.file + " to " + extractToDir; + if(!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) { + // assume error + return false; + } } - auto modPath = modsToDecomp.firstKey(); - auto mod = modsToDecomp.value(modPath); + for (auto iter = toDecomp.begin(); iter != toDecomp.end(); iter++) { + auto &modPath = iter.key(); + auto &mod = iter.value(); + auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw); - auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw); + QDir extractDir(m_stagingPath); + auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile); - QDir extractDir(m_stagingPath); - auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile); + qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir; + if(!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) { + qWarning() << "Failed to extract" << mod.decompFile; + return false; + } + } - qWarning() << "Extracting " + mod.decompFile + " to " + extractToDir; - - m_decompFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractFile, modPath, mod.decompFile, extractToPath); - connect(&m_decompFutureWatcher, &QFutureWatcher::finished, this, [&]() - { - install(); - }); - connect(&m_decompFutureWatcher, &QFutureWatcher::canceled, this, [&]() - { - emitAborted(); - }); - m_decompFutureWatcher.setFuture(m_decompFuture); - - modsToDecomp.remove(modPath); + for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) { + auto &from = iter.key(); + auto &to = iter.value(); + FS::copy fileCopyOperation(from, to); + if(!fileCopyOperation()) { + qWarning() << "Failed to copy" << from << "to" << to; + return false; + } + } + return true; } void PackInstallTask::install() { + qDebug() << "PackInstallTask::install: " << QThread::currentThreadId(); setStatus(tr("Installing modpack")); auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg"); diff --git a/api/logic/modplatform/atlauncher/ATLPackInstallTask.h b/api/logic/modplatform/atlauncher/ATLPackInstallTask.h index 12e6bcf5..78544bab 100644 --- a/api/logic/modplatform/atlauncher/ATLPackInstallTask.h +++ b/api/logic/modplatform/atlauncher/ATLPackInstallTask.h @@ -11,6 +11,8 @@ #include "minecraft/PackProfile.h" #include "meta/Version.h" +#include + namespace ATLauncher { class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask @@ -30,6 +32,9 @@ private slots: void onDownloadSucceeded(); void onDownloadFailed(QString reason); + void onModsDownloaded(); + void onModsExtracted(); + private: QString getDirForModType(ModType type, QString raw); QString getVersionForLoader(QString uid); @@ -40,9 +45,12 @@ private: void installConfigs(); void extractConfigs(); - void installMods(); - void extractMods(); - void decompMods(); + void downloadMods(); + bool extractMods( + const QMap &toExtract, + const QMap &toDecomp, + const QMap &toCopy + ); void install(); private: @@ -55,14 +63,18 @@ private: QMap modsToExtract; QMap modsToDecomp; + QMap modsToCopy; QString archivePath; QStringList jarmods; Meta::VersionPtr minecraftVersion; QMap componentsToInstall; - QFuture m_extractFuture; - QFutureWatcher m_extractFutureWatcher; + QFuture> m_extractFuture; + QFutureWatcher> m_extractFutureWatcher; + + QFuture m_modExtractFuture; + QFutureWatcher m_modExtractFutureWatcher; QFuture m_decompFuture; QFutureWatcher m_decompFutureWatcher; diff --git a/api/logic/modplatform/legacy_ftb/PackInstallTask.h b/api/logic/modplatform/legacy_ftb/PackInstallTask.h index 1eec1880..7868d1c4 100644 --- a/api/logic/modplatform/legacy_ftb/PackInstallTask.h +++ b/api/logic/modplatform/legacy_ftb/PackInstallTask.h @@ -8,6 +8,8 @@ #include "meta/VersionList.h" #include "PackHelpers.h" +#include + namespace LegacyFTB { class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask @@ -40,8 +42,8 @@ private slots: private: /* data */ bool abortable = false; std::unique_ptr m_packZip; - QFuture m_extractFuture; - QFutureWatcher m_extractFutureWatcher; + QFuture> m_extractFuture; + QFutureWatcher> m_extractFutureWatcher; NetJobPtr netJobContainer; QString archivePath; diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp b/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp index 9be99d06..96e1804d 100644 --- a/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp +++ b/api/logic/modplatform/technic/SingleZipPackInstallTask.cpp @@ -79,7 +79,7 @@ void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current, void Technic::SingleZipPackInstallTask::extractFinished() { m_packZip.reset(); - if (m_extractFuture.result().isEmpty()) + if (!m_extractFuture.result()) { emitFailed(tr("Failed to extract modpack")); return; diff --git a/api/logic/modplatform/technic/SingleZipPackInstallTask.h b/api/logic/modplatform/technic/SingleZipPackInstallTask.h index ecf4445a..c56b9e46 100644 --- a/api/logic/modplatform/technic/SingleZipPackInstallTask.h +++ b/api/logic/modplatform/technic/SingleZipPackInstallTask.h @@ -25,6 +25,8 @@ #include #include +#include + namespace Technic { class MULTIMC_LOGIC_EXPORT SingleZipPackInstallTask : public InstanceTask @@ -51,8 +53,8 @@ private: QString m_archivePath; NetJobPtr m_filesNetJob; std::unique_ptr m_packZip; - QFuture m_extractFuture; - QFutureWatcher m_extractFutureWatcher; + QFuture> m_extractFuture; + QFutureWatcher> m_extractFutureWatcher; }; } // namespace Technic diff --git a/api/logic/modplatform/technic/SolderPackInstallTask.cpp b/api/logic/modplatform/technic/SolderPackInstallTask.cpp index a858de49..1d17073c 100644 --- a/api/logic/modplatform/technic/SolderPackInstallTask.cpp +++ b/api/logic/modplatform/technic/SolderPackInstallTask.cpp @@ -117,7 +117,7 @@ void Technic::SolderPackInstallTask::downloadSucceeded() while (m_modCount > i) { auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i)); - if (MMCZip::extractDir(path, extractDir).isEmpty()) + if (!MMCZip::extractDir(path, extractDir)) { return false; }