From f032e32133023ed8396fc2b6ead7eadc2816a25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 26 Mar 2016 16:56:57 +0100 Subject: NOISSUE finalize support for new mojang version format --- application/MainWindow.cpp | 4 +- application/handlers/WebResourceHandler.cpp | 2 +- application/pages/global/AccountListPage.cpp | 2 +- .../org/multimc/onesix/OneSixLauncher.java | 40 ++-- logic/CMakeLists.txt | 3 - logic/minecraft/Library.cpp | 205 ++++++++++++++++----- logic/minecraft/Library.h | 89 +++++---- logic/minecraft/MinecraftProfile.cpp | 63 ++++--- logic/minecraft/MinecraftProfile.h | 9 +- logic/minecraft/MinecraftVersion.cpp | 2 + logic/minecraft/MinecraftVersionList.cpp | 3 +- logic/minecraft/MojangDownloadInfo.h | 4 + logic/minecraft/MojangVersionFormat.cpp | 36 ++-- logic/minecraft/VersionFile.cpp | 14 +- logic/minecraft/VersionFile.h | 4 +- logic/minecraft/forge/ForgeInstaller.cpp | 14 +- logic/minecraft/forge/ForgeMirror.h | 10 - logic/minecraft/forge/ForgeMirrors.cpp | 118 ------------ logic/minecraft/forge/ForgeMirrors.h | 61 ------ logic/minecraft/forge/ForgeVersionList.cpp | 4 +- logic/minecraft/forge/ForgeXzDownload.cpp | 63 +------ logic/minecraft/forge/ForgeXzDownload.h | 7 - logic/minecraft/ftb/FTBProfileStrategy.cpp | 2 +- logic/minecraft/legacy/LegacyUpdate.cpp | 6 +- .../minecraft/liteloader/LiteLoaderVersionList.cpp | 4 +- logic/minecraft/onesix/OneSixInstance.cpp | 56 +++--- logic/minecraft/onesix/OneSixProfileStrategy.cpp | 2 +- logic/minecraft/onesix/OneSixUpdate.cpp | 67 +------ logic/minecraft/onesix/OneSixUpdate.h | 1 - logic/minecraft/onesix/OneSixVersionFormat.cpp | 12 +- logic/net/CacheDownload.cpp | 21 +-- logic/net/HttpMetaCache.cpp | 34 ++-- logic/net/HttpMetaCache.h | 54 +++++- logic/net/URLConstants.cpp | 34 ++-- logic/net/URLConstants.h | 38 ++-- logic/notifications/NotificationChecker.cpp | 2 +- logic/trans/TranslationDownloader.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/data/lib-native-arch.json | 46 +++++ tests/data/lib-native.json | 52 ++++++ tests/data/lib-simple.json | 11 ++ tests/tst_Library.cpp | 195 ++++++++++++++++++++ 42 files changed, 795 insertions(+), 602 deletions(-) delete mode 100644 logic/minecraft/forge/ForgeMirror.h delete mode 100644 logic/minecraft/forge/ForgeMirrors.cpp delete mode 100644 logic/minecraft/forge/ForgeMirrors.h create mode 100644 tests/data/lib-native-arch.json create mode 100644 tests/data/lib-native.json create mode 100644 tests/data/lib-simple.json create mode 100644 tests/tst_Library.cpp diff --git a/application/MainWindow.cpp b/application/MainWindow.cpp index 1633cc39..ef758fae 100644 --- a/application/MainWindow.cpp +++ b/application/MainWindow.cpp @@ -534,7 +534,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow auto meta = Env::getInstance().metacache()->resolveEntry("skins", profile.id + ".png"); auto action = CacheDownload::make(QUrl("https://" + URLConstants::SKINS_BASE + profile.id + ".png"), meta); skin_dls.append(action); - meta->stale = true; + meta->setStale(true); } } if (!skin_dls.isEmpty()) @@ -1028,7 +1028,7 @@ InstancePtr MainWindow::instanceFromZipPack(QString instName, QString instGroup, { const QString path = url.host() + '/' + url.path(); auto entry = ENV.metacache()->resolveEntry("general", path); - entry->stale = true; + entry->setStale(true); CacheDownloadPtr dl = CacheDownload::make(url, entry); NetJob job(tr("Modpack download")); job.addNetAction(dl); diff --git a/application/handlers/WebResourceHandler.cpp b/application/handlers/WebResourceHandler.cpp index 5e51584b..757b870a 100644 --- a/application/handlers/WebResourceHandler.cpp +++ b/application/handlers/WebResourceHandler.cpp @@ -13,7 +13,7 @@ WebResourceHandler::WebResourceHandler(const QString &url) : QObject(), m_url(url) { MetaEntryPtr entry = ENV.metacache()->resolveEntry("icons", url); - if (!entry->stale) + if (!entry->isStale()) { setResultFromFile(entry->getFullPath()); } diff --git a/application/pages/global/AccountListPage.cpp b/application/pages/global/AccountListPage.cpp index 84d317c1..89b853c5 100644 --- a/application/pages/global/AccountListPage.cpp +++ b/application/pages/global/AccountListPage.cpp @@ -133,7 +133,7 @@ void AccountListPage::addAccount(const QString &errMsg) auto action = CacheDownload::make( QUrl("https://" + URLConstants::SKINS_BASE + profile.id + ".png"), meta); job->addNetAction(action); - meta->stale = true; + meta->setStale(true); } job->start(); diff --git a/depends/launcher/org/multimc/onesix/OneSixLauncher.java b/depends/launcher/org/multimc/onesix/OneSixLauncher.java index d94d06b0..0d1e2174 100644 --- a/depends/launcher/org/multimc/onesix/OneSixLauncher.java +++ b/depends/launcher/org/multimc/onesix/OneSixLauncher.java @@ -31,6 +31,8 @@ public class OneSixLauncher implements Launcher // parameters, separated from ParamBucket private List libraries; private List extlibs; + private List extlibs32; + private List extlibs64; private List mcparams; private List mods; private List jarmods; @@ -38,7 +40,7 @@ public class OneSixLauncher implements Launcher private List traits; private String appletClass; private String mainClass; - private String natives; + private String nativePath; private String userName, sessionId; private String windowTitle; private String windowParams; @@ -54,7 +56,22 @@ public class OneSixLauncher implements Launcher private void processParams(ParamBucket params) throws NotFoundException { libraries = params.all("cp"); - extlibs = params.all("ext"); + extlibs = params.allSafe("ext", new ArrayList()); + extlibs32 = params.allSafe("ext32", new ArrayList()); + extlibs64 = params.allSafe("ext64", new ArrayList()); + + // Unify the extracted native libs according to actual system architecture + String property = System.getProperty("os.arch"); + boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); + if(is_64) + { + extlibs.addAll(extlibs64); + } + else + { + extlibs.addAll(extlibs32); + } + mcparams = params.allSafe("param", new ArrayList() ); mainClass = params.firstSafe("mainClass", "net.minecraft.client.Minecraft"); appletClass = params.firstSafe("appletClass", "net.minecraft.client.MinecraftApplet"); @@ -62,7 +79,7 @@ public class OneSixLauncher implements Launcher jarmods = params.allSafe("jarmod", new ArrayList()); coremods = params.allSafe("coremod", new ArrayList()); traits = params.allSafe("traits", new ArrayList()); - natives = params.first("natives"); + nativePath = params.first("natives"); userName = params.first("userName"); sessionId = params.first("sessionId"); @@ -95,7 +112,7 @@ public class OneSixLauncher implements Launcher Utils.log(); Utils.log("Native path:"); - Utils.log(" " + natives); + Utils.log(" " + nativePath); Utils.log(); Utils.log("Traits:"); @@ -343,16 +360,13 @@ public class OneSixLauncher implements Launcher // extract native libs (depending on platform here... java!) Utils.log("Preparing native libraries..."); - String property = System.getProperty("os.arch"); - boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); for(String extlib: extlibs) { try { - String cleanlib = extlib.replace("${arch}", is_64 ? "64" : "32"); - File cleanlibf = new File(cleanlib); - Utils.log("Extracting " + cleanlibf.getName()); - Utils.unzipNatives(cleanlibf, new File(natives)); + File extlibf = new File(extlib); + Utils.log("Extracting " + extlibf.getName()); + Utils.unzipNatives(extlibf, new File(nativePath)); } catch (IOException e) { System.err.println("Failed to extract native library:"); @@ -365,9 +379,9 @@ public class OneSixLauncher implements Launcher // set the native libs path... the brute force way try { - System.setProperty("java.library.path", natives); - System.setProperty("org.lwjgl.librarypath", natives); - System.setProperty("net.java.games.input.librarypath", natives); + System.setProperty("java.library.path", nativePath); + System.setProperty("org.lwjgl.librarypath", nativePath); + System.setProperty("net.java.games.input.librarypath", nativePath); // by the power of reflection, initialize native libs again. DIRTY! // this is SO BAD. imagine doing that to ld Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); diff --git a/logic/CMakeLists.txt b/logic/CMakeLists.txt index 62a4e738..19236e1b 100644 --- a/logic/CMakeLists.txt +++ b/logic/CMakeLists.txt @@ -284,9 +284,6 @@ set(LOGIC_SOURCES minecraft/forge/ForgeVersion.cpp minecraft/forge/ForgeVersionList.h minecraft/forge/ForgeVersionList.cpp - minecraft/forge/ForgeMirror.h - minecraft/forge/ForgeMirrors.h - minecraft/forge/ForgeMirrors.cpp minecraft/forge/ForgeXzDownload.h minecraft/forge/ForgeXzDownload.cpp minecraft/forge/LegacyForge.h diff --git a/logic/minecraft/Library.cpp b/logic/minecraft/Library.cpp index 9a1d2cc7..922db84e 100644 --- a/logic/minecraft/Library.cpp +++ b/logic/minecraft/Library.cpp @@ -1,57 +1,176 @@ #include "Library.h" +#include +#include +#include #include -QStringList Library::files() const +void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32, QStringList& native64) const { - QStringList retval; - QString storage = storageSuffix(); - if (storage.contains("${arch}")) + auto actualPath = [&](QString relPath) { - QString cooked_storage = storage; - cooked_storage.replace("${arch}", "32"); - retval.append(cooked_storage); - cooked_storage = storage; - cooked_storage.replace("${arch}", "64"); - retval.append(cooked_storage); + QFileInfo out(FS::PathCombine(storagePrefix(), relPath)); + return out.absoluteFilePath(); + }; + if(m_mojangDownloads) + { + if(m_mojangDownloads->artifact) + { + auto artifact = m_mojangDownloads->artifact; + jar += actualPath(artifact->path); + } + if(!isNative()) + return; + if(m_nativeClassifiers.contains(system)) + { + auto nativeClassifier = m_nativeClassifiers[system]; + if(nativeClassifier.contains("${arch}")) + { + auto nat32Classifier = nativeClassifier; + nat32Classifier.replace("${arch}", "32"); + auto nat64Classifier = nativeClassifier; + nat64Classifier.replace("${arch}", "64"); + auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); + if(nat32info) + native32 += actualPath(nat32info->path); + auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); + if(nat64info) + native64 += actualPath(nat64info->path); + } + else + { + native += actualPath(m_mojangDownloads->getDownloadInfo(nativeClassifier)->path); + } + } } else - retval.append(storage); - return retval; -} - -bool Library::filesExist(const QDir &base) const -{ - auto libFiles = files(); - for(auto file: libFiles) { - QFileInfo info(base, file); - qWarning() << info.absoluteFilePath() << "doesn't exist"; - if (!info.exists()) - return false; + QString raw_storage = storageSuffix(system); + if(isNative()) + { + if (raw_storage.contains("${arch}")) + { + auto nat32Storage = raw_storage; + nat32Storage.replace("${arch}", "32"); + auto nat64Storage = raw_storage; + nat64Storage.replace("${arch}", "64"); + native32 += actualPath(nat32Storage); + native64 += actualPath(nat64Storage); + } + else + { + native += actualPath(raw_storage); + } + } + else + { + jar += actualPath(raw_storage); + } } - return true; } -QString Library::url() const +QList Library::getDownloads(OpSys system, HttpMetaCache * cache, QStringList &failedFiles) const { - if (!m_absolute_url.isEmpty()) - { - return m_absolute_url; - } + QList out; + bool isLocal = (hint() == "local"); + bool isForge = (hint() == "forge-pack-xz"); - if (m_base_url.isEmpty()) + auto add_download = [&](QString storage, QString dl) { - return QString("https://" + URLConstants::LIBRARY_BASE) + storageSuffix(); - } + auto entry = cache->resolveEntry("libraries", storage); + if (!entry->isStale()) + return true; + if(isLocal) + { + QFileInfo fileinfo(entry->getFullPath()); + if(!fileinfo.exists()) + { + failedFiles.append(entry->getFullPath()); + return false; + } + return true; + } + if (isForge) + { + out.append(ForgeXzDownload::make(storage, entry)); + } + else + { + out.append(CacheDownload::make(dl, entry)); + } + return true; + }; - if(m_base_url.endsWith('/')) + if(m_mojangDownloads) { - return m_base_url + storageSuffix(); + if(m_mojangDownloads->artifact) + { + auto artifact = m_mojangDownloads->artifact; + add_download(artifact->path, artifact->url); + } + if(m_nativeClassifiers.contains(system)) + { + auto nativeClassifier = m_nativeClassifiers[system]; + if(nativeClassifier.contains("${arch}")) + { + auto nat32Classifier = nativeClassifier; + nat32Classifier.replace("${arch}", "32"); + auto nat64Classifier = nativeClassifier; + nat64Classifier.replace("${arch}", "64"); + auto nat32info = m_mojangDownloads->getDownloadInfo(nat32Classifier); + if(nat32info) + add_download(nat32info->path, nat32info->url); + auto nat64info = m_mojangDownloads->getDownloadInfo(nat64Classifier); + if(nat64info) + add_download(nat64info->path, nat64info->url); + } + else + { + auto info = m_mojangDownloads->getDownloadInfo(nativeClassifier); + if(info) + { + add_download(info->path, info->url); + } + } + } } else { - return m_base_url + QChar('/') + storageSuffix(); + QString raw_storage = storageSuffix(system); + auto raw_dl = [&](){ + if (!m_absoluteURL.isEmpty()) + { + return m_absoluteURL; + } + + if (m_repositoryURL.isEmpty()) + { + return QString("https://" + URLConstants::LIBRARY_BASE) + raw_storage; + } + + if(m_repositoryURL.endsWith('/')) + { + return m_repositoryURL + raw_storage; + } + else + { + return m_repositoryURL + QChar('/') + raw_storage; + } + }(); + if (raw_storage.contains("${arch}")) + { + QString cooked_storage = raw_storage; + QString cooked_dl = raw_dl; + add_download(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); + cooked_storage = raw_storage; + cooked_dl = raw_dl; + add_download(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); + } + else + { + add_download(raw_storage, raw_dl); + } } + return out; } bool Library::isActive() const @@ -74,7 +193,7 @@ bool Library::isActive() const } if (isNative()) { - result = result && m_native_classifiers.contains(currentSystem); + result = result && m_nativeClassifiers.contains(currentSystem); } return result; } @@ -98,7 +217,7 @@ QString Library::storagePrefix() const return m_storagePrefix; } -QString Library::storageSuffix() const +QString Library::storageSuffix(OpSys system) const { // non-native? use only the gradle specifier if (!isNative()) @@ -108,9 +227,9 @@ QString Library::storageSuffix() const // otherwise native, override classifiers. Mojang HACK! GradleSpecifier nativeSpec = m_name; - if (m_native_classifiers.contains(currentSystem)) + if (m_nativeClassifiers.contains(system)) { - nativeSpec.setClassifier(m_native_classifiers[currentSystem]); + nativeSpec.setClassifier(m_nativeClassifiers[system]); } else { @@ -118,13 +237,3 @@ QString Library::storageSuffix() const } return nativeSpec.toPath(); } - -QString Library::storagePath() const -{ - return FS::PathCombine(storagePrefix(), storageSuffix()); -} - -bool Library::storagePathIsDefault() const -{ - return m_storagePrefix.isEmpty(); -} diff --git a/logic/minecraft/Library.h b/logic/minecraft/Library.h index 35b5cb99..fdce93f3 100644 --- a/logic/minecraft/Library.h +++ b/logic/minecraft/Library.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -12,16 +13,19 @@ #include "minecraft/OpSys.h" #include "GradleSpecifier.h" #include "net/URLConstants.h" +#include "MojangDownloadInfo.h" + +#include "multimc_logic_export.h" -struct MojangLibraryDownloadInfo; class Library; typedef std::shared_ptr LibraryPtr; -class Library +class MULTIMC_LOGIC_EXPORT Library { friend class OneSixVersionFormat; friend class MojangVersionFormat; + friend class LibraryTest; public: Library() { @@ -35,13 +39,14 @@ public: { auto newlib = std::make_shared(); newlib->m_name = base->m_name; - newlib->m_base_url = base->m_base_url; + newlib->m_repositoryURL = base->m_repositoryURL; newlib->m_hint = base->m_hint; - newlib->m_absolute_url = base->m_absolute_url; - newlib->extract_excludes = base->extract_excludes; - newlib->m_native_classifiers = base->m_native_classifiers; + newlib->m_absoluteURL = base->m_absoluteURL; + newlib->m_extractExcludes = base->m_extractExcludes; + newlib->m_nativeClassifiers = base->m_nativeClassifiers; newlib->m_rules = base->m_rules; newlib->m_storagePrefix = base->m_storagePrefix; + newlib->m_mojangDownloads = base->m_mojangDownloads; return newlib; } @@ -83,45 +88,27 @@ public: /* methods */ /// Returns true if the library is native bool isNative() const { - return m_native_classifiers.size() != 0; + return m_nativeClassifiers.size() != 0; } void setStoragePrefix(QString prefix = QString()); - /// the default storage prefix used by MultiMC - static QString defaultStoragePrefix(); - - bool storagePathIsDefault() const; - - /// Get the prefix - root of the storage to be used - QString storagePrefix() const; - - /// Get the relative path where the library should be saved - QString storageSuffix() const; - - /// Get the absolute path where the library should be saved - QString storagePath() const; - /// Set the url base for downloads - void setBaseUrl(const QString &base_url) + void setRepositoryURL(const QString &base_url) { - m_base_url = base_url; + m_repositoryURL = base_url; } - /// List of files this library describes. Required because of platform-specificness of native libs - QStringList files() const; - - /// List Shortcut for checking if all the above files exist - bool filesExist(const QDir &base) const; + void getApplicableFiles(OpSys system, QStringList & jar, QStringList & native, QStringList & native32, QStringList & native64) const; void setAbsoluteUrl(const QString &absolute_url) { - m_absolute_url = absolute_url; + m_absoluteURL = absolute_url; } - QString absoluteUrl() const + void setMojangDownloadInfo(MojangLibraryDownloadInfo::Ptr info) { - return m_absolute_url; + m_mojangDownloads = info; } void setHint(const QString &hint) @@ -129,11 +116,6 @@ public: /* methods */ m_hint = hint; } - QString hint() const - { - return m_hint; - } - /// Set the load rules void setRules(QList> rules) { @@ -143,22 +125,33 @@ public: /* methods */ /// Returns true if the library should be loaded (or extracted, in case of natives) bool isActive() const; - /// Get the URL to download the library from - QString url() const; + // Get a list of downloads for this library + QList getDownloads(OpSys system, class HttpMetaCache * cache, QStringList &failedFiles) const; + +private: /* methods */ + /// the default storage prefix used by MultiMC + static QString defaultStoragePrefix(); + + /// Get the prefix - root of the storage to be used + QString storagePrefix() const; + + /// Get the relative path where the library should be saved + QString storageSuffix(OpSys system) const; + + QString hint() const + { + return m_hint; + } protected: /* data */ /// the basic gradle dependency specifier. GradleSpecifier m_name; - /// where to store the lib locally - QString m_storage_path; - /// is this lib actually active on the current OS? - bool m_is_active = false; /// DEPRECATED URL prefix of the maven repo where the file can be downloaded - QString m_base_url; + QString m_repositoryURL; /// DEPRECATED: MultiMC-specific absolute URL. takes precedence over the implicit maven repo URL, if defined - QString m_absolute_url; + QString m_absoluteURL; /** * MultiMC-specific type hint - modifies how the library is treated @@ -172,13 +165,13 @@ protected: /* data */ QString m_storagePrefix; /// true if the library had an extract/excludes section (even empty) - bool applyExcludes = false; + bool m_hasExcludes = false; /// a list of files that shouldn't be extracted from the library - QStringList extract_excludes; + QStringList m_extractExcludes; /// native suffixes per OS - QMap m_native_classifiers; + QMap m_nativeClassifiers; /// true if the library had a rules section (even empty) bool applyRules = false; @@ -187,5 +180,5 @@ protected: /* data */ QList> m_rules; /// MOJANG: container with Mojang style download info - std::shared_ptr m_mojang_downloads; + MojangLibraryDownloadInfo::Ptr m_mojangDownloads; }; diff --git a/logic/minecraft/MinecraftProfile.cpp b/logic/minecraft/MinecraftProfile.cpp index 71ece012..70d0cee4 100644 --- a/logic/minecraft/MinecraftProfile.cpp +++ b/logic/minecraft/MinecraftProfile.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -68,9 +69,9 @@ void MinecraftProfile::clear() m_mainClass.clear(); m_appletClass.clear(); m_libraries.clear(); - m_nativeLibraries.clear(); m_traits.clear(); m_jarMods.clear(); + mojangDownloads.clear(); m_problemSeverity = ProblemSeverity::PROBLEM_NONE; } @@ -428,6 +429,18 @@ void MinecraftProfile::applyMinecraftAssets(MojangAssetIndexInfo::Ptr assets) } } +void MinecraftProfile::applyMojangDownload(const QString &key, MojangDownloadInfo::Ptr download) +{ + if(download) + { + mojangDownloads[key] = download; + } + else + { + mojangDownloads.remove(key); + } +} + void MinecraftProfile::applyTraits(const QSet& traits) { this->m_traits.unite(traits); @@ -466,35 +479,24 @@ static int findLibraryByName(QList haystack, const GradleSpecifier & void MinecraftProfile::applyLibrary(LibraryPtr library) { - auto insert = [&](QList & into) - { - // find the library by name. - const int index = findLibraryByName(into, library->rawName()); - // library not found? just add it. - if (index < 0) - { - into.append(Library::limitedCopy(library)); - return; - } - auto existingLibrary = into.at(index); - // if we are higher it means we should update - if (Version(library->version()) > Version(existingLibrary->version())) - { - auto libraryCopy = Library::limitedCopy(library); - into.replace(index, libraryCopy); - } - }; if(!library->isActive()) { return; } - if(library->isNative()) + // find the library by name. + const int index = findLibraryByName(m_libraries, library->rawName()); + // library not found? just add it. + if (index < 0) { - insert(m_nativeLibraries); + m_libraries.append(Library::limitedCopy(library)); + return; } - else + auto existingLibrary = m_libraries.at(index); + // if we are higher it means we should update + if (Version(library->version()) > Version(existingLibrary->version())) { - insert(m_libraries); + auto libraryCopy = Library::limitedCopy(library); + m_libraries.replace(index, libraryCopy); } } @@ -571,12 +573,21 @@ const QList & MinecraftProfile::getLibraries() const return m_libraries; } -const QList & MinecraftProfile::getNativeLibraries() const +QString MinecraftProfile::getMainJarUrl() const { - return m_nativeLibraries; + auto iter = mojangDownloads.find("client"); + if(iter != mojangDownloads.end()) + { + // current + return iter.value()->url; + } + else + { + // legacy fallback + return URLConstants::getLegacyJarUrl(getMinecraftVersion()); + } } - void MinecraftProfile::installJarMods(QStringList selectedFiles) { m_strategy->installJarMods(selectedFiles); diff --git a/logic/minecraft/MinecraftProfile.h b/logic/minecraft/MinecraftProfile.h index ce0ff3cf..ca9288ad 100644 --- a/logic/minecraft/MinecraftProfile.h +++ b/logic/minecraft/MinecraftProfile.h @@ -97,6 +97,7 @@ public: /* application of profile variables from patches */ void applyJarMods(const QList &jarMods); void applyLibrary(LibraryPtr library); void applyProblemSeverity(ProblemSeverity severity); + void applyMojangDownload(const QString & key, MojangDownloadInfo::Ptr download); public: /* getters for profile variables */ QString getMinecraftVersion() const; @@ -109,7 +110,7 @@ public: /* getters for profile variables */ const QStringList & getTweakers() const; const QList & getJarMods() const; const QList & getLibraries() const; - const QList & getNativeLibraries() const; + QString getMainJarUrl() const; bool hasTrait(const QString & trait) const; ProblemSeverity getProblemSeverity() const; @@ -139,6 +140,9 @@ private: /* data */ /// Assets type - "legacy" or a version ID MojangAssetIndexInfo::Ptr m_minecraftAssets; + // Mojang: list of 'downloads' - client jar, server jar, windows server exe, maybe more. + QMap > mojangDownloads; + /** * arguments that should be used for launching minecraft * @@ -159,9 +163,6 @@ private: /* data */ /// the list of libraries QList m_libraries; - /// the list of native libraries - QList m_nativeLibraries; - /// traits, collected from all the version files (version files can only add) QSet m_traits; diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp index a3855481..3167fc4a 100644 --- a/logic/minecraft/MinecraftVersion.cpp +++ b/logic/minecraft/MinecraftVersion.cpp @@ -72,10 +72,12 @@ void MinecraftVersion::applyFileTo(MinecraftProfile *profile) QString MinecraftVersion::getUrl() const { + // legacy fallback if(m_versionFileURL.isEmpty()) { return QString("http://") + URLConstants::AWS_DOWNLOAD_VERSIONS + m_descriptor + "/" + m_descriptor + ".json"; } + // current return m_versionFileURL; } diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp index 62c588d1..bd679c73 100644 --- a/logic/minecraft/MinecraftVersionList.cpp +++ b/logic/minecraft/MinecraftVersionList.cpp @@ -470,8 +470,7 @@ void MCVListVersionUpdateTask::executeTask() specificVersionDownloadJob.reset(job); connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded())); connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString))); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); + connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); specificVersionDownloadJob->start(); } diff --git a/logic/minecraft/MojangDownloadInfo.h b/logic/minecraft/MojangDownloadInfo.h index d8cd2e6d..1f3306e0 100644 --- a/logic/minecraft/MojangDownloadInfo.h +++ b/logic/minecraft/MojangDownloadInfo.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include struct MojangDownloadInfo @@ -22,6 +23,9 @@ struct MojangDownloadInfo struct MojangLibraryDownloadInfo { + MojangLibraryDownloadInfo(MojangDownloadInfo::Ptr artifact): artifact(artifact) {}; + MojangLibraryDownloadInfo() {}; + // types typedef std::shared_ptr Ptr; diff --git a/logic/minecraft/MojangVersionFormat.cpp b/logic/minecraft/MojangVersionFormat.cpp index 41723493..34129c9e 100644 --- a/logic/minecraft/MojangVersionFormat.cpp +++ b/logic/minecraft/MojangVersionFormat.cpp @@ -8,7 +8,7 @@ using namespace Json; #include "ParseUtils.h" -static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 14; +static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18; static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj); static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj); @@ -130,7 +130,7 @@ QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info) void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out) { - Bits::readString(in, "id", out->id); + Bits::readString(in, "id", out->minecraftVersion); Bits::readString(in, "mainClass", out->mainClass); Bits::readString(in, "minecraftArguments", out->minecraftArguments); if(out->minecraftArguments.isEmpty()) @@ -212,7 +212,7 @@ VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc out->name = "Minecraft"; out->fileId = "net.minecraft"; - out->version = out->id; + out->version = out->minecraftVersion; out->filename = filename; @@ -231,7 +231,7 @@ VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out) { - writeString(out, "id", in->id); + writeString(out, "id", in->minecraftVersion); writeString(out, "mainClass", in->mainClass); writeString(out, "minecraftArguments", in->minecraftArguments); writeString(out, "type", in->type); @@ -294,14 +294,14 @@ LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const } out->m_name = libObj.value("name").toString(); - Bits::readString(libObj, "url", out->m_base_url); + Bits::readString(libObj, "url", out->m_repositoryURL); if (libObj.contains("extract")) { - out->applyExcludes = true; + out->m_hasExcludes = true; auto extractObj = requireObject(libObj.value("extract")); for (auto excludeVal : requireArray(extractObj.value("exclude"))) { - out->extract_excludes.append(requireString(excludeVal)); + out->m_extractExcludes.append(requireString(excludeVal)); } } if (libObj.contains("natives")) @@ -316,7 +316,7 @@ LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const OpSys opSys = OpSys_fromString(it.key()); if (opSys != Os_Other) { - out->m_native_classifiers[opSys] = it.value().toString(); + out->m_nativeClassifiers[opSys] = it.value().toString(); } } } @@ -327,7 +327,7 @@ LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const } if (libObj.contains("downloads")) { - out->m_mojang_downloads = libDownloadInfoFromJson(libObj); + out->m_mojangDownloads = libDownloadInfoFromJson(libObj); } return out; } @@ -336,27 +336,25 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library) { QJsonObject libRoot; libRoot.insert("name", (QString)library->m_name); - if (library->m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - library->m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - library->m_base_url != "https://" + URLConstants::LIBRARY_BASE && !library->m_base_url.isEmpty()) + if (!library->m_repositoryURL.isEmpty()) { - libRoot.insert("url", library->m_base_url); + libRoot.insert("url", library->m_repositoryURL); } if (library->isNative()) { QJsonObject nativeList; - auto iter = library->m_native_classifiers.begin(); - while (iter != library->m_native_classifiers.end()) + auto iter = library->m_nativeClassifiers.begin(); + while (iter != library->m_nativeClassifiers.end()) { nativeList.insert(OpSys_toString(iter.key()), iter.value()); iter++; } libRoot.insert("natives", nativeList); - if (library->extract_excludes.size()) + if (library->m_extractExcludes.size()) { QJsonArray excludes; QJsonObject extract; - for (auto exclude : library->extract_excludes) + for (auto exclude : library->m_extractExcludes) { excludes.append(exclude); } @@ -374,9 +372,9 @@ QJsonObject MojangVersionFormat::libraryToJson(Library *library) } libRoot.insert("rules", allRules); } - if(library->m_mojang_downloads) + if(library->m_mojangDownloads) { - auto downloadsObj = libDownloadInfoToJson(library->m_mojang_downloads); + auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads); libRoot.insert("downloads", downloadsObj); } return libRoot; diff --git a/logic/minecraft/VersionFile.cpp b/logic/minecraft/VersionFile.cpp index 451c3c8b..573c4cb4 100644 --- a/logic/minecraft/VersionFile.cpp +++ b/logic/minecraft/VersionFile.cpp @@ -25,14 +25,14 @@ bool VersionFile::hasJarMods() void VersionFile::applyTo(MinecraftProfile *profile) { auto theirVersion = profile->getMinecraftVersion(); - if (!theirVersion.isNull() && !mcVersion.isNull()) + if (!theirVersion.isNull() && !dependsOnMinecraftVersion.isNull()) { - if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1) + if (QRegExp(dependsOnMinecraftVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(theirVersion) == -1) { - throw MinecraftVersionMismatch(fileId, mcVersion, theirVersion); + throw MinecraftVersionMismatch(fileId, dependsOnMinecraftVersion, theirVersion); } } - profile->applyMinecraftVersion(id); + profile->applyMinecraftVersion(minecraftVersion); profile->applyMainClass(mainClass); profile->applyAppletClass(appletClass); profile->applyMinecraftArguments(minecraftArguments); @@ -51,4 +51,10 @@ void VersionFile::applyTo(MinecraftProfile *profile) profile->applyLibrary(library); } profile->applyProblemSeverity(getProblemSeverity()); + auto iter = mojangDownloads.begin(); + while(iter != mojangDownloads.end()) + { + profile->applyMojangDownload(iter.key(), iter.value()); + iter++; + } } diff --git a/logic/minecraft/VersionFile.h b/logic/minecraft/VersionFile.h index 7627cc96..1b692f0f 100644 --- a/logic/minecraft/VersionFile.h +++ b/logic/minecraft/VersionFile.h @@ -143,13 +143,13 @@ public: /* data */ QString version; /// MultiMC: dependency on a Minecraft version - QString mcVersion; + QString dependsOnMinecraftVersion; /// Mojang: used to version the Mojang version format int minimumLauncherVersion = -1; /// Mojang: version of Minecraft this is - QString id; + QString minecraftVersion; /// Mojang: class to launch Minecraft with QString mainClass; diff --git a/logic/minecraft/forge/ForgeInstaller.cpp b/logic/minecraft/forge/ForgeInstaller.cpp index 7957de0e..155a2cac 100644 --- a/logic/minecraft/forge/ForgeInstaller.cpp +++ b/logic/minecraft/forge/ForgeInstaller.cpp @@ -126,8 +126,8 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr QCryptographicHash md5sum(QCryptographicHash::Md5); md5sum.addData(data); - cacheentry->stale = false; - cacheentry->md5sum = md5sum.result().toHex().constData(); + cacheentry->setStale(false); + cacheentry->setMD5Sum(md5sum.result().toHex().constData()); ENV.metacache()->updateEntry(cacheentry); } file.close(); @@ -264,8 +264,8 @@ bool ForgeInstaller::add(OneSixInstance *to) m_forge_json->name = "Forge"; m_forge_json->fileId = id(); m_forge_json->version = m_forgeVersionString; - m_forge_json->mcVersion = to->intendedVersionId(); - m_forge_json->id.clear(); + m_forge_json->dependsOnMinecraftVersion = to->intendedVersionId(); + m_forge_json->minecraftVersion.clear(); m_forge_json->order = 5; QSaveFile file(filename(to->instanceRoot())); @@ -378,16 +378,16 @@ protected: * This fixes some problems with bad files acquired because of unhandled HTTP redirects * in old versions of MultiMC. */ - if (!entry->stale) + if (!entry->isStale()) { QFileInfo localFile(entry->getFullPath()); if (localFile.size() <= 0x4000) { - entry->stale = true; + entry->setStale(true); } } - if (entry->stale) + if (entry->isStale()) { NetJob *fjob = new NetJob("Forge download"); fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry)); diff --git a/logic/minecraft/forge/ForgeMirror.h b/logic/minecraft/forge/ForgeMirror.h deleted file mode 100644 index 2518dffe..00000000 --- a/logic/minecraft/forge/ForgeMirror.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include - -struct ForgeMirror -{ - QString name; - QString logo_url; - QString website_url; - QString mirror_url; -}; \ No newline at end of file diff --git a/logic/minecraft/forge/ForgeMirrors.cpp b/logic/minecraft/forge/ForgeMirrors.cpp deleted file mode 100644 index a2fc2c62..00000000 --- a/logic/minecraft/forge/ForgeMirrors.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Env.h" -#include "ForgeMirrors.h" -#include -#include -#include - -ForgeMirrors::ForgeMirrors(QList &libs, NetJobPtr parent_job, - QString mirrorlist) -{ - m_libs = libs; - m_parent_job = parent_job; - m_url = QUrl(mirrorlist); - m_status = Job_NotStarted; -} - -void ForgeMirrors::start() -{ - qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request(m_url); - request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); - auto worker = ENV.qnam(); - QNetworkReply *rep = worker->get(request); - - m_reply.reset(rep); - connect(rep, SIGNAL(downloadProgress(qint64, qint64)), - SLOT(downloadProgress(qint64, qint64))); - connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), - SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); -} - -void ForgeMirrors::downloadError(QNetworkReply::NetworkError error) -{ - // error happened during download. - qCritical() << "Error getting URL:" << m_url.toString().toLocal8Bit() - << "Network error: " << error; - m_status = Job_Failed; -} - -void ForgeMirrors::downloadFinished() -{ - // if the download succeeded - if (m_status != Job_Failed) - { - // nothing went wrong... ? - parseMirrorList(); - return; - } - // else the download failed, we use a fixed list - else - { - m_status = Job_Finished; - m_reply.reset(); - deferToFixedList(); - return; - } -} - -void ForgeMirrors::deferToFixedList() -{ - m_mirrors.clear(); - m_mirrors.append( - {"Minecraft Forge", "http://files.minecraftforge.net/forge_logo.png", - "http://files.minecraftforge.net/", "http://files.minecraftforge.net/maven/"}); - m_mirrors.append({"Creeper Host", - "http://files.minecraftforge.net/forge_logo.png", - "https://www.creeperhost.net/link.php?id=1", - "http://new.creeperrepo.net/forge/maven/"}); - injectDownloads(); - emit succeeded(m_index_within_job); -} - -void ForgeMirrors::parseMirrorList() -{ - m_status = Job_Finished; - auto data = m_reply->readAll(); - m_reply.reset(); - auto dataLines = data.split('\n'); - for(auto line: dataLines) - { - auto elements = line.split('!'); - if (elements.size() == 4) - { - m_mirrors.append({elements[0],elements[1],elements[2],elements[3]}); - } - } - if(!m_mirrors.size()) - deferToFixedList(); - injectDownloads(); - emit succeeded(m_index_within_job); -} - -void ForgeMirrors::injectDownloads() -{ - // shuffle the mirrors randomly - std::random_device rd; - std::mt19937 rng(rd()); - std::shuffle(m_mirrors.begin(), m_mirrors.end(), rng); - - // tell parent to download the libs - for(auto lib: m_libs) - { - lib->setMirrors(m_mirrors); - m_parent_job->addNetAction(lib); - } -} - -void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - m_total_progress = bytesTotal; - m_progress = bytesReceived; - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); -} - -void ForgeMirrors::downloadReadyRead() -{ -} diff --git a/logic/minecraft/forge/ForgeMirrors.h b/logic/minecraft/forge/ForgeMirrors.h deleted file mode 100644 index 0312829b..00000000 --- a/logic/minecraft/forge/ForgeMirrors.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2013-2015 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "ForgeXzDownload.h" - -#include "net/NetAction.h" -#include "net/HttpMetaCache.h" -#include "net/NetJob.h" - -#include -#include - -typedef std::shared_ptr ForgeMirrorsPtr; - -class ForgeMirrors : public NetAction -{ - Q_OBJECT -public: - QList m_libs; - NetJobPtr m_parent_job; - QList m_mirrors; - -public: - explicit ForgeMirrors(QList &libs, NetJobPtr parent_job, - QString mirrorlist); - static ForgeMirrorsPtr make(QList &libs, NetJobPtr parent_job, - QString mirrorlist) - { - return ForgeMirrorsPtr(new ForgeMirrors(libs, parent_job, mirrorlist)); - } - virtual ~ForgeMirrors(){}; -protected -slots: - virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - virtual void downloadError(QNetworkReply::NetworkError error); - virtual void downloadFinished(); - virtual void downloadReadyRead(); - -private: - void parseMirrorList(); - void deferToFixedList(); - void injectDownloads(); - -public -slots: - virtual void start(); -}; diff --git a/logic/minecraft/forge/ForgeVersionList.cpp b/logic/minecraft/forge/ForgeVersionList.cpp index 9b418310..907672f2 100644 --- a/logic/minecraft/forge/ForgeVersionList.cpp +++ b/logic/minecraft/forge/ForgeVersionList.cpp @@ -128,8 +128,8 @@ void ForgeListLoadTask::executeTask() auto gradleForgeListEntry = ENV.metacache()->resolveEntry("minecraftforge", "json"); // verify by poking the server. - forgeListEntry->stale = true; - gradleForgeListEntry->stale = true; + forgeListEntry->setStale(true); + gradleForgeListEntry->setStale(true); job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::FORGE_LEGACY_URL), forgeListEntry)); diff --git a/logic/minecraft/forge/ForgeXzDownload.cpp b/logic/minecraft/forge/ForgeXzDownload.cpp index 6009e31e..2a47bb26 100644 --- a/logic/minecraft/forge/ForgeXzDownload.cpp +++ b/logic/minecraft/forge/ForgeXzDownload.cpp @@ -30,19 +30,13 @@ ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : Ne m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX"); m_status = Job_NotStarted; m_url_path = relative_path; -} - -void ForgeXzDownload::setMirrors(QList &mirrors) -{ - m_mirror_index = 0; - m_mirrors = mirrors; - updateUrl(); + m_url = "http://files.minecraftforge.net/maven/" + m_url_path + ".pack.xz"; } void ForgeXzDownload::start() { m_status = Job_InProgress; - if (!m_entry->stale) + if (!m_entry->isStale()) { m_status = Job_Finished; emit succeeded(m_index_within_job); @@ -55,16 +49,10 @@ void ForgeXzDownload::start() emit failed(m_index_within_job); return; } - if (m_mirrors.empty()) - { - m_status = Job_Failed; - emit failed(m_index_within_job); - return; - } qDebug() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); auto worker = ENV.qnam(); @@ -96,44 +84,11 @@ void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) void ForgeXzDownload::failAndTryNextMirror() { m_status = Job_Failed; - int next = m_mirror_index + 1; - if(m_mirrors.size() == next) - m_mirror_index = 0; - else - m_mirror_index = next; - - updateUrl(); emit failed(m_index_within_job); } -void ForgeXzDownload::updateUrl() -{ - qDebug() << "Updating URL for " << m_url_path; - for (auto possible : m_mirrors) - { - qDebug() << "Possible: " << possible.name << " : " << possible.mirror_url; - } - QString aggregate = m_mirrors[m_mirror_index].mirror_url + m_url_path + ".pack.xz"; - m_url = QUrl(aggregate); -} - void ForgeXzDownload::downloadFinished() { - //TEST: defer to other possible mirrors (autofail the first one) - /* - qDebug() <<"dl " << index_within_job << " mirror " << m_mirror_index; - if( m_mirror_index == 0) - { - qDebug() <<"dl " << index_within_job << " AUTOFAIL"; - m_status = Job_Failed; - m_pack200_xz_file.close(); - m_pack200_xz_file.remove(); - m_reply.reset(); - failAndTryNextMirror(); - return; - } - */ - // if the download succeeded if (m_status != Job_Failed) { @@ -372,16 +327,14 @@ void ForgeXzDownload::decompressAndInstall() failAndTryNextMirror(); return; } - m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5) - .toHex() - .constData(); + auto hash = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5); + m_entry->setMD5Sum(hash.toHex().constData()); jar_file.close(); QFileInfo output_file_info(m_target_path); - m_entry->etag = m_reply->rawHeader("ETag").constData(); - m_entry->local_changed_timestamp = - output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); - m_entry->stale = false; + m_entry->setETag(m_reply->rawHeader("ETag").constData()); + m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); + m_entry->setStale(false); ENV.metacache()->updateEntry(m_entry); m_reply.reset(); diff --git a/logic/minecraft/forge/ForgeXzDownload.h b/logic/minecraft/forge/ForgeXzDownload.h index 45722812..67524405 100644 --- a/logic/minecraft/forge/ForgeXzDownload.h +++ b/logic/minecraft/forge/ForgeXzDownload.h @@ -19,7 +19,6 @@ #include "net/HttpMetaCache.h" #include #include -#include "ForgeMirror.h" typedef std::shared_ptr ForgeXzDownloadPtr; @@ -32,10 +31,6 @@ public: QString m_target_path; /// this is the output file, if any QTemporaryFile m_pack200_xz_file; - /// mirror index (NOT OPTICS, I SWEAR) - int m_mirror_index = 0; - /// list of mirrors to use. Mirror has the url base - QList m_mirrors; /// path relative to the mirror base QString m_url_path; @@ -46,7 +41,6 @@ public: return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry)); } virtual ~ForgeXzDownload(){}; - void setMirrors(QList & mirrors); protected slots: @@ -62,5 +56,4 @@ slots: private: void decompressAndInstall(); void failAndTryNextMirror(); - void updateUrl(); }; diff --git a/logic/minecraft/ftb/FTBProfileStrategy.cpp b/logic/minecraft/ftb/FTBProfileStrategy.cpp index d43fbf6e..f5faacae 100644 --- a/logic/minecraft/ftb/FTBProfileStrategy.cpp +++ b/logic/minecraft/ftb/FTBProfileStrategy.cpp @@ -59,7 +59,7 @@ void FTBProfileStrategy::loadDefaultBuiltinPatches() auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false); // adapt the loaded file - the FTB patch file format is different than ours. - file->id.clear(); + file->minecraftVersion.clear(); for(auto addLib: file->libraries) { addLib->setHint("local"); diff --git a/logic/minecraft/legacy/LegacyUpdate.cpp b/logic/minecraft/legacy/LegacyUpdate.cpp index af787f8c..2d7e8dd2 100644 --- a/logic/minecraft/legacy/LegacyUpdate.cpp +++ b/logic/minecraft/legacy/LegacyUpdate.cpp @@ -367,14 +367,12 @@ void LegacyUpdate::jarStart() setStatus(tr("Downloading new minecraft.jar ...")); QString version_id = inst->intendedVersionId(); - QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; auto dljob = new NetJob("Minecraft.jar for version " + version_id); auto metacache = ENV.metacache(); - auto entry = metacache->resolveEntry("versions", localPath); - dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); + auto entry = metacache->resolveEntry("versions", URLConstants::getJarPath(version_id)); + dljob->addNetAction(CacheDownload::make(QUrl(URLConstants::getLegacyJarUrl(version_id)), entry)); connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); connect(dljob, SIGNAL(failed(QString)), SLOT(jarFailed(QString))); connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); diff --git a/logic/minecraft/liteloader/LiteLoaderVersionList.cpp b/logic/minecraft/liteloader/LiteLoaderVersionList.cpp index 42698ccc..b0c9736a 100644 --- a/logic/minecraft/liteloader/LiteLoaderVersionList.cpp +++ b/logic/minecraft/liteloader/LiteLoaderVersionList.cpp @@ -144,7 +144,7 @@ void LLListLoadTask::executeTask() auto liteloaderEntry = ENV.metacache()->resolveEntry("liteloader", "versions.json"); // verify by poking the server. - liteloaderEntry->stale = true; + liteloaderEntry->setStale(true); job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL), liteloaderEntry)); @@ -251,7 +251,7 @@ void LLListLoadTask::listDownloaded() // hack to make liteloader 1.7.10_00 work if(lib->rawName() == GradleSpecifier("org.ow2.asm:asm-all:5.0.3")) { - lib->setBaseUrl("http://repo.maven.apache.org/maven2/"); + lib->setRepositoryURL("http://repo.maven.apache.org/maven2/"); } version->libraries.append(lib); } diff --git a/logic/minecraft/onesix/OneSixInstance.cpp b/logic/minecraft/onesix/OneSixInstance.cpp index 328c3b38..8d46eefc 100644 --- a/logic/minecraft/onesix/OneSixInstance.cpp +++ b/logic/minecraft/onesix/OneSixInstance.cpp @@ -180,24 +180,6 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session) launchScript += "jarmod " + jarmod->originalName + " (" + jarmod->name + ")\n"; } - // libraries and class path. - { - auto libs = m_profile->getLibraries(); - for (auto lib : libs) - { - launchScript += "cp " + QFileInfo(lib->storagePath()).absoluteFilePath() + "\n"; - } - auto jarMods = getJarMods(); - if (!jarMods.isEmpty()) - { - launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("minecraft.jar") + "\n"; - } - else - { - QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar"; - launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n"; - } - } auto mainClass = m_profile->getMainClass(); if (!mainClass.isEmpty()) { @@ -234,15 +216,43 @@ QString OneSixInstance::createLaunchScript(AuthSessionPtr session) launchScript += "sessionId " + session->session + "\n"; } - // native libraries (mostly LWJGL) + // libraries and class path. { - QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); - for (auto native : m_profile->getNativeLibraries()) + auto libs = m_profile->getLibraries(); + + QStringList jar, native, native32, native64; + for (auto lib : libs) + { + lib->getApplicableFiles(currentSystem, jar, native, native32, native64); + } + for(auto file: jar) + { + launchScript += "cp " + file + "\n"; + } + for(auto file: native) { - QFileInfo finfo(native->storagePath()); - launchScript += "ext " + finfo.absoluteFilePath() + "\n"; + launchScript += "ext " + file + "\n"; } + for(auto file: native32) + { + launchScript += "ext32 " + file + "\n"; + } + for(auto file: native64) + { + launchScript += "ext64 " + file + "\n"; + } + QDir natives_dir(FS::PathCombine(instanceRoot(), "natives/")); launchScript += "natives " + natives_dir.absolutePath() + "\n"; + auto jarMods = getJarMods(); + if (!jarMods.isEmpty()) + { + launchScript += "cp " + QDir(instanceRoot()).absoluteFilePath("minecraft.jar") + "\n"; + } + else + { + QString relpath = m_profile->getMinecraftVersion() + "/" + m_profile->getMinecraftVersion() + ".jar"; + launchScript += "cp " + versionsPath().absoluteFilePath(relpath) + "\n"; + } } // traits. including legacyLaunch and others ;) diff --git a/logic/minecraft/onesix/OneSixProfileStrategy.cpp b/logic/minecraft/onesix/OneSixProfileStrategy.cpp index aaaa9d97..af42286d 100644 --- a/logic/minecraft/onesix/OneSixProfileStrategy.cpp +++ b/logic/minecraft/onesix/OneSixProfileStrategy.cpp @@ -54,7 +54,7 @@ void OneSixProfileStrategy::upgradeDeprecatedFiles() auto file = ProfileUtils::parseJsonFile(QFileInfo(sourceFile), false); ProfileUtils::removeLwjglFromPatch(file); file->fileId = "net.minecraft"; - file->version = file->id; + file->version = file->minecraftVersion; file->name = "Minecraft"; auto data = OneSixVersionFormat::versionFileToJson(file, false).toJson(); QSaveFile newPatchFile(mcJson); diff --git a/logic/minecraft/onesix/OneSixUpdate.cpp b/logic/minecraft/onesix/OneSixUpdate.cpp index 10d1e294..1c2cd196 100644 --- a/logic/minecraft/onesix/OneSixUpdate.cpp +++ b/logic/minecraft/onesix/OneSixUpdate.cpp @@ -14,6 +14,7 @@ */ #include "Env.h" +#include #include "OneSixUpdate.h" #include "OneSixInstance.h" @@ -29,7 +30,6 @@ #include "minecraft/MinecraftVersionList.h" #include "minecraft/MinecraftProfile.h" #include "minecraft/Library.h" -#include "minecraft/forge/ForgeMirrors.h" #include "net/URLConstants.h" #include "minecraft/AssetsUtils.h" #include "Exception.h" @@ -95,7 +95,7 @@ void OneSixUpdate::assetIndexStart() auto metacache = ENV.metacache(); auto entry = metacache->resolveEntry("asset_indexes", localPath); - entry->stale = true; + entry->setStale(true); job->addNetAction(CacheDownload::make(indexUrl, entry)); jarlibDownloadJob.reset(job); @@ -174,88 +174,41 @@ void OneSixUpdate::jarlibStart() { QString version_id = profile->getMinecraftVersion(); QString localPath = version_id + "/" + version_id + ".jar"; - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; + 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(CacheDownload::make(QUrl(urlstr), entry)); - jarHashOnEntry = entry->md5sum; - jarlibDownloadJob.reset(job); } - auto libs = profile->getNativeLibraries(); - libs.append(profile->getLibraries()); + auto libs = profile->getLibraries(); auto metacache = ENV.metacache(); - QList ForgeLibs; QList brokenLocalLibs; + QStringList failedFiles; for (auto lib : libs) { - if (lib->hint() == "local") - { - if (!lib->filesExist(m_inst->librariesPath())) - brokenLocalLibs.append(lib); - continue; - } - - QString raw_storage = lib->storageSuffix(); - QString raw_dl = lib->url(); - - auto f = [&](QString storage, QString dl) - { - auto entry = metacache->resolveEntry("libraries", storage); - if (entry->stale) - { - if (lib->hint() == "forge-pack-xz") - { - ForgeLibs.append(ForgeXzDownload::make(storage, entry)); - } - else - { - jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); - } - } - }; - if (raw_storage.contains("${arch}")) - { - QString cooked_storage = raw_storage; - QString cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "32"), cooked_dl.replace("${arch}", "32")); - cooked_storage = raw_storage; - cooked_dl = raw_dl; - f(cooked_storage.replace("${arch}", "64"), cooked_dl.replace("${arch}", "64")); - } - else + auto dls = lib->getDownloads(currentSystem, metacache.get(), failedFiles); + for(auto dl : dls) { - f(raw_storage, raw_dl); + jarlibDownloadJob->addNetAction(dl); } } if (!brokenLocalLibs.empty()) { jarlibDownloadJob.reset(); - QStringList failed; - for (auto brokenLib : brokenLocalLibs) - { - failed.append(brokenLib->files()); - } - QString failed_all = failed.join("\n"); + + 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; } - // TODO: think about how to propagate this from the original json file... or IF AT ALL - QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; - if (!ForgeLibs.empty()) - { - jarlibDownloadJob->addNetAction( - ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); - } connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.get(), &NetJob::failed, this, &OneSixUpdate::jarlibFailed); diff --git a/logic/minecraft/onesix/OneSixUpdate.h b/logic/minecraft/onesix/OneSixUpdate.h index 6901e3d6..b5195364 100644 --- a/logic/minecraft/onesix/OneSixUpdate.h +++ b/logic/minecraft/onesix/OneSixUpdate.h @@ -63,6 +63,5 @@ private: std::shared_ptr versionUpdateTask; OneSixInstance *m_inst = nullptr; - QString jarHashOnEntry; QList fmlLibsToProcess; }; diff --git a/logic/minecraft/onesix/OneSixVersionFormat.cpp b/logic/minecraft/onesix/OneSixVersionFormat.cpp index b5e05b22..49a18563 100644 --- a/logic/minecraft/onesix/OneSixVersionFormat.cpp +++ b/logic/minecraft/onesix/OneSixVersionFormat.cpp @@ -19,16 +19,16 @@ LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const { LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename); readString(libObj, "MMC-hint", out->m_hint); - readString(libObj, "MMC-absulute_url", out->m_absolute_url); - readString(libObj, "MMC-absoluteUrl", out->m_absolute_url); + readString(libObj, "MMC-absulute_url", out->m_absoluteURL); + readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL); return out; } QJsonObject OneSixVersionFormat::libraryToJson(Library *library) { QJsonObject libRoot = MojangVersionFormat::libraryToJson(library); - if (library->m_absolute_url.size()) - libRoot.insert("MMC-absoluteUrl", library->m_absolute_url); + if (library->m_absoluteURL.size()) + libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL); if (library->m_hint.size()) libRoot.insert("MMC-hint", library->m_hint); return libRoot; @@ -64,7 +64,7 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc out->name = root.value("name").toString(); out->fileId = root.value("fileId").toString(); out->version = root.value("version").toString(); - out->mcVersion = root.value("mcVersion").toString(); + out->dependsOnMinecraftVersion = root.value("mcVersion").toString(); out->filename = filename; MojangVersionFormat::readVersionProperties(root, out.get()); @@ -167,7 +167,7 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch writeString(root, "name", patch->name); writeString(root, "fileId", patch->fileId); writeString(root, "version", patch->version); - writeString(root, "mcVersion", patch->mcVersion); + writeString(root, "mcVersion", patch->dependsOnMinecraftVersion); MojangVersionFormat::writeVersionProperties(patch.get(), root); diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 1d48170a..d79feb14 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -34,7 +34,7 @@ CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry) void CacheDownload::start() { m_status = Job_InProgress; - if (!m_entry->stale) + if (!m_entry->isStale()) { m_status = Job_Finished; emit succeeded(m_index_within_job); @@ -65,11 +65,11 @@ void CacheDownload::start() QFile current(m_target_path); if(current.exists() && current.size() != 0) { - if (m_entry->remote_changed_timestamp.size()) + if (m_entry->getRemoteChangedTimestamp().size()) request.setRawHeader(QString("If-Modified-Since").toLatin1(), - m_entry->remote_changed_timestamp.toLatin1()); - if (m_entry->etag.size()) - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + m_entry->getRemoteChangedTimestamp().toLatin1()); + if (m_entry->getETag().size()) + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); } request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); @@ -138,7 +138,7 @@ void CacheDownload::downloadFinished() if (m_output_file->commit()) { m_status = Job_Finished; - m_entry->md5sum = md5sum.result().toHex().constData(); + m_entry->setMD5Sum(md5sum.result().toHex().constData()); } else { @@ -160,14 +160,13 @@ void CacheDownload::downloadFinished() QFileInfo output_file_info(m_target_path); - m_entry->etag = m_reply->rawHeader("ETag").constData(); + m_entry->setETag(m_reply->rawHeader("ETag").constData()); if (m_reply->hasRawHeader("Last-Modified")) { - m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData(); + m_entry->setRemoteChangedTimestamp(m_reply->rawHeader("Last-Modified").constData()); } - m_entry->local_changed_timestamp = - output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); - m_entry->stale = false; + m_entry->setLocalChangedTimestamp(output_file_info.lastModified().toUTC().toMSecsSinceEpoch()); + m_entry->setStale(false); ENV.metacache()->updateEntry(m_entry); m_reply.reset(); diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 3d6aef0d..68bfa89c 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -32,7 +32,7 @@ QString MetaEntry::getFullPath() { // FIXME: make local? - return FS::PathCombine(ENV.metacache()->getBasePath(base), path); + return FS::PathCombine(basePath, relativePath); } HttpMetaCache::HttpMetaCache(QString path) : QObject() @@ -65,8 +65,7 @@ MetaEntryPtr HttpMetaCache::getEntry(QString base, QString resource_path) return MetaEntryPtr(); } -MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, - QString expected_etag) +MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, QString expected_etag) { auto entry = getEntry(base, resource_path); // it's not present? generate a default stale entry @@ -114,15 +113,16 @@ MetaEntryPtr HttpMetaCache::resolveEntry(QString base, QString resource_path, } // entry passed all the checks we cared about. + entry->basePath = getBasePath(base); return entry; } bool HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) { - if (!m_entries.contains(stale_entry->base)) + if (!m_entries.contains(stale_entry->baseId)) { qCritical() << "Cannot add entry with unknown base: " - << stale_entry->base.toLocal8Bit(); + << stale_entry->baseId.toLocal8Bit(); return false; } if (stale_entry->stale) @@ -130,7 +130,7 @@ bool HttpMetaCache::updateEntry(MetaEntryPtr stale_entry) qCritical() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); return false; } - m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry; + m_entries[stale_entry->baseId].entry_list[stale_entry->relativePath] = stale_entry; SaveEventually(); return true; } @@ -148,9 +148,10 @@ bool HttpMetaCache::evictEntry(MetaEntryPtr entry) MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path) { - auto foo = new MetaEntry; - foo->base = base; - foo->path = resource_path; + auto foo = new MetaEntry(); + foo->baseId = base; + foo->basePath = getBasePath(base); + foo->relativePath = resource_path; foo->stale = true; return MetaEntryPtr(foo); } @@ -177,6 +178,9 @@ QString HttpMetaCache::getBasePath(QString base) void HttpMetaCache::Load() { + if(m_index_file.isNull()) + return; + QFile index(m_index_file); if (!index.open(QIODevice::ReadOnly)) return; @@ -206,9 +210,9 @@ void HttpMetaCache::Load() if (!m_entries.contains(base)) continue; auto &entrymap = m_entries[base]; - auto foo = new MetaEntry; - foo->base = base; - QString path = foo->path = element_obj.value("path").toString(); + auto foo = new MetaEntry(); + foo->baseId = base; + QString path = foo->relativePath = element_obj.value("path").toString(); foo->md5sum = element_obj.value("md5sum").toString(); foo->etag = element_obj.value("etag").toString(); foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); @@ -229,6 +233,8 @@ void HttpMetaCache::SaveEventually() void HttpMetaCache::SaveNow() { + if(m_index_file.isNull()) + return; QJsonObject toplevel; toplevel.insert("version", QJsonValue(QString("1"))); QJsonArray entriesArr; @@ -242,8 +248,8 @@ void HttpMetaCache::SaveNow() continue; } QJsonObject entryObj; - entryObj.insert("base", QJsonValue(entry->base)); - entryObj.insert("path", QJsonValue(entry->path)); + entryObj.insert("base", QJsonValue(entry->baseId)); + entryObj.insert("path", QJsonValue(entry->relativePath)); entryObj.insert("md5sum", QJsonValue(entry->md5sum)); entryObj.insert("etag", QJsonValue(entry->etag)); entryObj.insert("last_changed_timestamp", diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index fba3fe5a..7b626c70 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -23,16 +23,58 @@ class HttpMetaCache; -struct MULTIMC_LOGIC_EXPORT MetaEntry +class MULTIMC_LOGIC_EXPORT MetaEntry { - QString base; - QString path; +friend class HttpMetaCache; +protected: + MetaEntry() {} +public: + bool isStale() + { + return stale; + } + void setStale(bool stale) + { + this->stale = stale; + } + QString getFullPath(); + QString getRemoteChangedTimestamp() + { + return remote_changed_timestamp; + } + void setRemoteChangedTimestamp(QString remote_changed_timestamp) + { + this->remote_changed_timestamp = remote_changed_timestamp; + } + void setLocalChangedTimestamp(qint64 timestamp) + { + local_changed_timestamp = timestamp; + } + QString getETag() + { + return etag; + } + void setETag(QString etag) + { + this->etag = etag; + } + QString getMD5Sum() + { + return md5sum; + } + void setMD5Sum(QString md5sum) + { + this->md5sum = md5sum; + } +protected: + QString baseId; + QString basePath; + QString relativePath; QString md5sum; QString etag; qint64 local_changed_timestamp = 0; QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time bool stale = true; - QString getFullPath(); }; typedef std::shared_ptr MetaEntryPtr; @@ -42,7 +84,7 @@ class MULTIMC_LOGIC_EXPORT HttpMetaCache : public QObject Q_OBJECT public: // supply path to the cache index file - HttpMetaCache(QString path); + HttpMetaCache(QString path = QString()); ~HttpMetaCache(); // get the entry solely from the cache @@ -80,4 +122,4 @@ private: QMap m_entries; QString m_index_file; QTimer saveBatchingTimer; -}; \ No newline at end of file +}; diff --git a/logic/net/URLConstants.cpp b/logic/net/URLConstants.cpp index 85224767..bd476b2c 100644 --- a/logic/net/URLConstants.cpp +++ b/logic/net/URLConstants.cpp @@ -1,24 +1,16 @@ #include "URLConstants.h" -namespace URLConstants + +namespace URLConstants { + +QString getLegacyJarUrl(QString version) { -const QString AWS_DOWNLOAD_BASE("s3.amazonaws.com/Minecraft.Download/"); -const QString AWS_DOWNLOAD_VERSIONS(AWS_DOWNLOAD_BASE + "versions/"); -const QString AWS_DOWNLOAD_LIBRARIES(AWS_DOWNLOAD_BASE + "libraries/"); -const QString AWS_DOWNLOAD_INDEXES(AWS_DOWNLOAD_BASE + "indexes/"); -const QString ASSETS_BASE("assets.minecraft.net/"); -const QString RESOURCE_BASE("resources.download.minecraft.net/"); -const QString LIBRARY_BASE("libraries.minecraft.net/"); -//const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/"); -const QString SKINS_BASE("crafatar.com/skins/"); -const QString AUTH_BASE("authserver.mojang.com/"); -const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json"); -const QString - FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json"); -const QString MOJANG_STATUS_URL("http://status.mojang.com/check"); -const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news"); -const QString LITELOADER_URL("http://dl.liteloader.com/versions/versions.json"); -const QString IMGUR_BASE_URL("https://api.imgur.com/3/"); -const QString FMLLIBS_OUR_BASE_URL("http://files.multimc.org/fmllibs/"); -const QString FMLLIBS_FORGE_BASE_URL("http://files.minecraftforge.net/fmllibs/"); -const QString TRANSLATIONS_BASE_URL("http://files.multimc.org/translations/"); + return "http://" + AWS_DOWNLOAD_VERSIONS + getJarPath(version); +} + +QString getJarPath(QString version) +{ + return version + "/" + version + ".jar"; +} + + } diff --git a/logic/net/URLConstants.h b/logic/net/URLConstants.h index b04f44ba..8923ef54 100644 --- a/logic/net/URLConstants.h +++ b/logic/net/URLConstants.h @@ -17,26 +17,24 @@ #include -#include "multimc_logic_export.h" - namespace URLConstants { -extern const QString AWS_DOWNLOAD_BASE; -extern const QString AWS_DOWNLOAD_VERSIONS; -extern const QString AWS_DOWNLOAD_LIBRARIES; -extern const QString AWS_DOWNLOAD_INDEXES; -extern const QString ASSETS_BASE; -extern const QString RESOURCE_BASE; -extern const QString LIBRARY_BASE; -MULTIMC_LOGIC_EXPORT extern const QString SKINS_BASE; -extern const QString AUTH_BASE; -extern const QString FORGE_LEGACY_URL; -extern const QString FORGE_GRADLE_URL; -extern const QString MOJANG_STATUS_URL; -extern const QString MOJANG_STATUS_NEWS_URL; -extern const QString LITELOADER_URL; -extern const QString IMGUR_BASE_URL; -extern const QString FMLLIBS_OUR_BASE_URL; -extern const QString FMLLIBS_FORGE_BASE_URL; -extern const QString TRANSLATIONS_BASE_URL; +const QString AWS_DOWNLOAD_VERSIONS("s3.amazonaws.com/Minecraft.Download/versions/"); +const QString RESOURCE_BASE("resources.download.minecraft.net/"); +const QString LIBRARY_BASE("libraries.minecraft.net/"); +//const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/"); +const QString SKINS_BASE("crafatar.com/skins/"); +const QString AUTH_BASE("authserver.mojang.com/"); +const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json"); +const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json"); +const QString MOJANG_STATUS_URL("http://status.mojang.com/check"); +const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news"); +const QString LITELOADER_URL("http://dl.liteloader.com/versions/versions.json"); +const QString IMGUR_BASE_URL("https://api.imgur.com/3/"); +const QString FMLLIBS_OUR_BASE_URL("http://files.multimc.org/fmllibs/"); +const QString FMLLIBS_FORGE_BASE_URL("http://files.minecraftforge.net/fmllibs/"); +const QString TRANSLATIONS_BASE_URL("http://files.multimc.org/translations/"); + +QString getJarPath(QString version); +QString getLegacyJarUrl(QString version); } diff --git a/logic/notifications/NotificationChecker.cpp b/logic/notifications/NotificationChecker.cpp index 4e35cacc..ab2570b7 100644 --- a/logic/notifications/NotificationChecker.cpp +++ b/logic/notifications/NotificationChecker.cpp @@ -54,7 +54,7 @@ void NotificationChecker::checkForNotifications() } m_checkJob.reset(new NetJob("Checking for notifications")); auto entry = ENV.metacache()->resolveEntry("root", "notifications.json"); - entry->stale = true; + entry->setStale(true); m_checkJob->addNetAction(m_download = CacheDownload::make(m_notificationsUrl, entry)); connect(m_download.get(), &CacheDownload::succeeded, this, &NotificationChecker::downloadSucceeded); diff --git a/logic/trans/TranslationDownloader.cpp b/logic/trans/TranslationDownloader.cpp index 5a9cde12..ee5c1fd2 100644 --- a/logic/trans/TranslationDownloader.cpp +++ b/logic/trans/TranslationDownloader.cpp @@ -28,7 +28,7 @@ void TranslationDownloader::indexRecieved() if (!line.isEmpty()) { MetaEntryPtr entry = ENV.metacache()->resolveEntry("translations", "mmc_" + line); - entry->stale = true; + entry->setStale(true); CacheDownloadPtr dl = CacheDownload::make( QUrl(URLConstants::TRANSLATIONS_BASE_URL + line), entry); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1061f911..67a7a45e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,6 +27,7 @@ add_unit_test(userutils tst_userutils.cpp) add_unit_test(modutils tst_modutils.cpp) add_unit_test(inifile tst_inifile.cpp) add_unit_test(FileSystem tst_FileSystem.cpp) +add_unit_test(Library tst_Library.cpp) add_unit_test(UpdateChecker tst_UpdateChecker.cpp) add_unit_test(DownloadTask tst_DownloadTask.cpp) add_unit_test(filematchers tst_filematchers.cpp) diff --git a/tests/data/lib-native-arch.json b/tests/data/lib-native-arch.json new file mode 100644 index 00000000..a73aac54 --- /dev/null +++ b/tests/data/lib-native-arch.json @@ -0,0 +1,46 @@ +{ + "downloads": { + "classifiers": { + "natives-osx": { + "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar", + "sha1": "62503ee712766cf77f97252e5902786fd834b8c5", + "size": 418331, + "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-osx.jar" + }, + "natives-windows-32": { + "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar", + "sha1": "7c6affe439099806a4f552da14c42f9d643d8b23", + "size": 386792, + "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar" + }, + "natives-windows-64": { + "path": "tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar", + "sha1": "39d0c3d363735b4785598e0e7fbf8297c706a9f9", + "size": 463390, + "url": "https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "tv.twitch:twitch-platform:5.16", + "natives": { + "linux": "natives-linux", + "osx": "natives-osx", + "windows": "natives-windows-${arch}" + }, + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "linux" + } + } + ] +} diff --git a/tests/data/lib-native.json b/tests/data/lib-native.json new file mode 100644 index 00000000..0a95b2b9 --- /dev/null +++ b/tests/data/lib-native.json @@ -0,0 +1,52 @@ +{ + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", + "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", + "size": 22, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" + }, + "classifiers": { + "natives-linux": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", + "sha1": "931074f46c795d2f7b30ed6395df5715cfd7675b", + "size": 578680, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" + }, + "natives-osx": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar", + "sha1": "bcab850f8f487c3f4c4dbabde778bb82bd1a40ed", + "size": 426822, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar" + }, + "natives-windows": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar", + "sha1": "b84d5102b9dbfabfeb5e43c7e2828d98a7fc80e0", + "size": 613748, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-windows.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", + "natives": { + "linux": "natives-linux", + "osx": "natives-osx", + "windows": "natives-windows" + }, + "rules": [ + { + "action": "allow" + }, + { + "action": "disallow", + "os": { + "name": "osx" + } + } + ] +} diff --git a/tests/data/lib-simple.json b/tests/data/lib-simple.json new file mode 100644 index 00000000..3ef0f490 --- /dev/null +++ b/tests/data/lib-simple.json @@ -0,0 +1,11 @@ +{ + "downloads": { + "artifact": { + "path": "com/paulscode/codecwav/20101023/codecwav-20101023.jar", + "sha1": "12f031cfe88fef5c1dd36c563c0a3a69bd7261da", + "size": 5618, + "url": "https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar" + } + }, + "name": "com.paulscode:codecwav:20101023" +} diff --git a/tests/tst_Library.cpp b/tests/tst_Library.cpp new file mode 100644 index 00000000..b5840261 --- /dev/null +++ b/tests/tst_Library.cpp @@ -0,0 +1,195 @@ +#include +#include "TestUtil.h" + +#include "minecraft/MojangVersionFormat.h" +#include "minecraft/onesix/OneSixVersionFormat.h" +#include "minecraft/Library.h" +#include "net/HttpMetaCache.h" +#include "FileSystem.h" + +class LibraryTest : public QObject +{ + Q_OBJECT +private: + LibraryPtr readMojangJson(const char *file) + { + auto path = QFINDTESTDATA(file); + QFile jsonFile(path); + jsonFile.open(QIODevice::ReadOnly); + auto data = jsonFile.readAll(); + jsonFile.close(); + return MojangVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file); + } + // get absolute path to expected storage, assuming default cache prefix + QStringList getStorage(QString relative) + { + return {FS::PathCombine(cache->getBasePath("libraries"), relative)}; + } +private +slots: + void initTestCase() + { + cache.reset(new HttpMetaCache()); + cache->addBase("libraries", QDir("libraries").absolutePath()); + } + void test_legacy() + { + Library test("test.package:testname:testversion"); + QCOMPARE(test.artifactPrefix(), QString("test.package:testname")); + QCOMPARE(test.isNative(), false); + + QStringList jar, native, native32, native64; + test.getApplicableFiles(currentSystem, jar, native, native32, native64); + QCOMPARE(jar, getStorage("test/package/testname/testversion/testname-testversion.jar")); + QCOMPARE(native, {}); + QCOMPARE(native32, {}); + QCOMPARE(native64, {}); + } + void test_legacy_url() + { + QStringList failedFiles; + Library test("test.package:testname:testversion"); + test.setRepositoryURL("file://foo/bar"); + auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles); + QCOMPARE(downloads.size(), 1); + QCOMPARE(failedFiles, {}); + NetActionPtr dl = downloads[0]; + QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar")); + } + void test_legacy_url_local_broken() + { + Library test("test.package:testname:testversion"); + QCOMPARE(test.isNative(), false); + QStringList failedFiles; + test.setHint("local"); + auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles); + QCOMPARE(downloads.size(), 0); + QCOMPARE(failedFiles, getStorage("test/package/testname/testversion/testname-testversion.jar")); + } + void test_legacy_native() + { + Library test("test.package:testname:testversion"); + test.m_nativeClassifiers[OpSys::Os_Linux]="linux"; + QCOMPARE(test.isNative(), true); + test.setRepositoryURL("file://foo/bar"); + { + QStringList jar, native, native32, native64; + test.getApplicableFiles(Os_Linux, jar, native, native32, native64); + QCOMPARE(jar, {}); + QCOMPARE(native, getStorage("test/package/testname/testversion/testname-testversion-linux.jar")); + QCOMPARE(native32, {}); + QCOMPARE(native64, {}); + QStringList failedFiles; + auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles); + QCOMPARE(dls.size(), 1); + QCOMPARE(failedFiles, {}); + auto dl = dls[0]; + QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux.jar")); + } + } + void test_legacy_native_arch() + { + Library test("test.package:testname:testversion"); + test.m_nativeClassifiers[OpSys::Os_Linux]="linux-${arch}"; + test.m_nativeClassifiers[OpSys::Os_OSX]="osx-${arch}"; + test.m_nativeClassifiers[OpSys::Os_Windows]="windows-${arch}"; + QCOMPARE(test.isNative(), true); + test.setRepositoryURL("file://foo/bar"); + { + QStringList jar, native, native32, native64; + test.getApplicableFiles(Os_Linux, jar, native, native32, native64); + QCOMPARE(jar, {}); + QCOMPARE(native, {}); + QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-linux-32.jar")); + QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-linux-64.jar")); + QStringList failedFiles; + auto dls = test.getDownloads(Os_Linux, cache.get(), failedFiles); + QCOMPARE(dls.size(), 2); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-32.jar")); + QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-linux-64.jar")); + } + { + QStringList jar, native, native32, native64; + test.getApplicableFiles(Os_Windows, jar, native, native32, native64); + QCOMPARE(jar, {}); + QCOMPARE(native, {}); + QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-windows-32.jar")); + QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-windows-64.jar")); + QStringList failedFiles; + auto dls = test.getDownloads(Os_Windows, cache.get(), failedFiles); + QCOMPARE(dls.size(), 2); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-32.jar")); + QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-windows-64.jar")); + } + { + QStringList jar, native, native32, native64; + test.getApplicableFiles(Os_OSX, jar, native, native32, native64); + QCOMPARE(jar, {}); + QCOMPARE(native, {}); + QCOMPARE(native32, getStorage("test/package/testname/testversion/testname-testversion-osx-32.jar")); + QCOMPARE(native64, getStorage("test/package/testname/testversion/testname-testversion-osx-64.jar")); + QStringList failedFiles; + auto dls = test.getDownloads(Os_OSX, cache.get(), failedFiles); + QCOMPARE(dls.size(), 2); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-32.jar")); + QCOMPARE(dls[1]->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion-osx-64.jar")); + } + } + void test_onenine() + { + auto test = readMojangJson("data/lib-simple.json"); + QStringList jar, native, native32, native64; + test->getApplicableFiles(Os_OSX, jar, native, native32, native64); + QCOMPARE(jar, getStorage("com/paulscode/codecwav/20101023/codecwav-20101023.jar")); + QCOMPARE(native, {}); + QCOMPARE(native32, {}); + QCOMPARE(native64, {}); + QStringList failedFiles; + auto dls = test->getDownloads(Os_Linux, cache.get(), failedFiles); + QCOMPARE(dls.size(), 1); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/com/paulscode/codecwav/20101023/codecwav-20101023.jar")); + } + void test_onenine_native() + { + auto test = readMojangJson("data/lib-native.json"); + QStringList jar, native, native32, native64; + test->getApplicableFiles(Os_OSX, jar, native, native32, native64); + QCOMPARE(jar, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar")); + QCOMPARE(native, getStorage("org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); + QCOMPARE(native32, {}); + QCOMPARE(native64, {}); + QStringList failedFiles; + auto dls = test->getDownloads(Os_OSX, cache.get(), failedFiles); + QCOMPARE(dls.size(), 2); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar")); + QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar")); + } + void test_onenine_native_arch() + { + auto test = readMojangJson("data/lib-native-arch.json"); + QStringList jar, native, native32, native64; + test->getApplicableFiles(Os_Windows, jar, native, native32, native64); + QCOMPARE(jar, {}); + QCOMPARE(native, {}); + QCOMPARE(native32, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); + QCOMPARE(native64, getStorage("tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); + QStringList failedFiles; + auto dls = test->getDownloads(Os_Windows, cache.get(), failedFiles); + QCOMPARE(dls.size(), 2); + QCOMPARE(failedFiles, {}); + QCOMPARE(dls[0]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-32.jar")); + QCOMPARE(dls[1]->m_url, QUrl("https://libraries.minecraft.net/tv/twitch/twitch-platform/5.16/twitch-platform-5.16-natives-windows-64.jar")); + } +private: + std::unique_ptr cache; + QString workDir; +}; + +QTEST_GUILESS_MAIN(LibraryTest) + +#include "tst_Library.moc" -- cgit v1.2.3