diff options
| author | Petr Mrázek <peterix@gmail.com> | 2014-05-11 12:37:21 +0200 |
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2014-06-09 01:38:30 +0200 |
| commit | 69c3e7111f93290d1278d6116e9fd50079b4fe79 (patch) | |
| tree | cbaed3022e8705f1da29777afea0fca16c1abe60 | |
| parent | 92abe4c603e1f4931cd02ae6b752cb7054d8e30d (diff) | |
| download | PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.gz PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.tar.bz2 PrismLauncher-69c3e7111f93290d1278d6116e9fd50079b4fe79.zip | |
Make 1.6+ work with new instance format.
29 files changed, 929 insertions, 540 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 0be887dc..57b6352f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -485,11 +485,13 @@ SET(MULTIMC_SOURCES logic/minecraft/RawLibrary.h logic/minecraft/VersionBuilder.cpp logic/minecraft/VersionBuilder.h + logic/minecraft/VersionBuildError.h logic/minecraft/VersionFile.cpp logic/minecraft/VersionFile.h logic/minecraft/VersionFinal.cpp logic/minecraft/VersionFinal.h logic/minecraft/VersionPatch.h + logic/minecraft/VersionSource.h # Various base classes logic/BaseInstaller.h diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp index 65423436..f7b4b6b0 100644 --- a/logic/MMCJson.cpp +++ b/logic/MMCJson.cpp @@ -11,7 +11,7 @@ bool MMCJson::ensureBoolean(const QJsonValue val, const QString what) QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what) { - if(val.isNull()) + if(val.isUndefined() || val.isUndefined()) throw JSONValidationError(what + " does not exist"); return val; } @@ -59,3 +59,24 @@ QString MMCJson::ensureString(const QJsonValue val, const QString what) return val.toString(); } +void MMCJson::writeString(QJsonObject &to, QString key, QString value) +{ + if(value.size()) + { + to.insert(key, value); + } +} + +void MMCJson::writeStringList(QJsonObject &to, QString key, QStringList values) +{ + if(values.size()) + { + QJsonArray array; + for(auto value: values) + { + array.append(value); + } + to.insert(key, array); + } +} + diff --git a/logic/MMCJson.h b/logic/MMCJson.h index 71ded435..8408f29b 100644 --- a/logic/MMCJson.h +++ b/logic/MMCJson.h @@ -43,4 +43,23 @@ int ensureInteger(const QJsonValue val, QString what = "value"); /// make sure the value is converted into a double precision floating number. throw otherwise. double ensureDouble(const QJsonValue val, QString what = "value"); + +void writeString(QJsonObject & to, QString key, QString value); + +void writeStringList (QJsonObject & to, QString key, QStringList values); + +template <typename T> +void writeObjectList (QJsonObject & to, QString key, QList<T> values) +{ + if(values.size()) + { + QJsonArray array; + for(auto value: values) + { + array.append(value->toJson()); + } + to.insert(key, array); + } +} } + diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f77913dc..2a7bb5b6 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -34,15 +34,12 @@ #include "logic/net/URLConstants.h" #include "logic/assets/AssetsUtils.h" -OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) - : Task(parent), m_inst(inst) +OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst) { } void OneSixUpdate::executeTask() { - QString intendedVersion = m_inst->intendedVersionId(); - // Make directories QDir mcDir(m_inst->minecraftRoot()); if (!mcDir.exists() && !mcDir.mkpath(".")) @@ -51,97 +48,37 @@ void OneSixUpdate::executeTask() return; } - if (m_inst->shouldUpdate()) + // Get a pointer to the version object that corresponds to the instance's version. + targetVersion = std::dynamic_pointer_cast<MinecraftVersion>( + MMC->minecraftlist()->findVersion(m_inst->intendedVersionId())); + if (targetVersion == nullptr) { - // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = std::dynamic_pointer_cast<MinecraftVersion>( - MMC->minecraftlist()->findVersion(intendedVersion)); - if (targetVersion == nullptr) - { - // don't do anything if it was invalid - emitFailed(tr("The specified Minecraft version is invalid. Choose a different one.")); - return; - } - // builtins need no updates, so only update for Mojang - if(targetVersion->m_versionSource == MinecraftVersion::Mojang) - { - versionFileStart(); - return; - } + // don't do anything if it was invalid + emitFailed(tr("The specified Minecraft version is invalid. Choose a different one.")); + return; } - jarlibStart(); -} - -void OneSixUpdate::versionFileStart() -{ - if (m_inst->providesVersionFile()) + if (m_inst->providesVersionFile() || !targetVersion->needsUpdate()) { jarlibStart(); return; } - QLOG_INFO() << m_inst->name() << ": getting version file."; - setStatus(tr("Getting the version files from Mojang...")); - - QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + - targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; - auto job = new NetJob("Version index"); - job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); - specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); - connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), - SIGNAL(progress(qint64, qint64))); - specificVersionDownloadJob->start(); -} - -void OneSixUpdate::versionFileFinished() -{ - NetActionPtr DlJob = specificVersionDownloadJob->first(); - - QString version_id = targetVersion->descriptor(); - QString inst_dir = m_inst->instanceRoot(); - // save the version file in $instanceId/version.json - { - QString version1 = PathCombine(inst_dir, "/version.json"); - ensureFilePathExists(version1); - // FIXME: detect errors here, download to a temp file, swap - QSaveFile vfile1(version1); - if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) - { - emitFailed(tr("Can't open %1 for writing.").arg(version1)); - return; - } - auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data; - qint64 actual = 0; - if ((actual = vfile1.write(data)) != data.size()) - { - emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size())); - return; - } - if (!vfile1.commit()) - { - emitFailed(tr("Can't commit changes to %1").arg(version1)); - return; - } - } - - // the version is downloaded safely. update is 'done' at this point - m_inst->setShouldUpdate(false); - - // delete any custom version inside the instance (it's no longer relevant, we did an update) - QString custom = PathCombine(inst_dir, "/custom.json"); - QFile finfo(custom); - if (finfo.exists()) + versionUpdateTask = MMC->minecraftlist()->createUpdateTask(m_inst->intendedVersionId()); + if (!versionUpdateTask) { - finfo.remove(); + jarlibStart(); + return; } - // NOTE: Version is reloaded in jarlibStart - jarlibStart(); + connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart())); + connect(versionUpdateTask.get(), SIGNAL(failed(QString)), SLOT(versionUpdateFailed(QString))); + connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + setStatus(tr("Getting the version files from Mojang...")); + versionUpdateTask->start(); } -void OneSixUpdate::versionFileFailed() +void OneSixUpdate::versionUpdateFailed(QString reason) { - emitFailed(tr("Failed to download the version description. Try again.")); + emitFailed(reason); } void OneSixUpdate::assetIndexStart() @@ -236,12 +173,12 @@ void OneSixUpdate::jarlibStart() { inst->reloadVersion(); } - catch(MMCError & e) + catch (MMCError &e) { emitFailed(e.cause()); return; } - catch(...) + catch (...) { emitFailed(tr("Failed to load the version description file for reasons unknown.")); return; @@ -275,7 +212,7 @@ void OneSixUpdate::jarlibStart() { if (lib->hint() == "local") { - if(!lib->filesExist(m_inst->librariesPath())) + if (!lib->filesExist(m_inst->librariesPath())) brokenLocalLibs.append(lib); continue; } @@ -312,16 +249,19 @@ void OneSixUpdate::jarlibStart() f(raw_storage, raw_dl); } } - if(!brokenLocalLibs.empty()) + if (!brokenLocalLibs.empty()) { jarlibDownloadJob.reset(); QStringList failed; - for(auto brokenLib : brokenLocalLibs) + for (auto brokenLib : brokenLocalLibs) { failed.append(brokenLib->files()); } QString failed_all = failed.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)); + 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 @@ -344,27 +284,27 @@ void OneSixUpdate::jarlibFinished() { OneSixInstance *inst = (OneSixInstance *)m_inst; std::shared_ptr<VersionFinal> version = inst->getFullVersion(); - + // create stripped jar, if needed - if(version->hasJarMods()) + if (version->hasJarMods()) { - //FIXME: good candidate for moving elsewhere (jar location resolving/version caching). + // FIXME: good candidate for moving elsewhere (jar location resolving/version caching). QString version_id = version->id; QString localPath = version_id + "/" + version_id + ".jar"; QString strippedPath = version_id + "/" + version_id + "-stripped.jar"; auto metacache = MMC->metacache(); auto entry = metacache->resolveEntry("versions", localPath); auto entryStripped = metacache->resolveEntry("versions", strippedPath); - + QString fullJarPath = entry->getFullPath(); QString fullStrippedJarPath = entryStripped->getFullPath(); - - if(entry->md5sum != jarHashOnEntry || !QFileInfo::exists(fullStrippedJarPath)) + + if (entry->md5sum != jarHashOnEntry || !QFileInfo::exists(fullStrippedJarPath)) { stripJar(fullJarPath, fullStrippedJarPath); } } - if(version->traits.contains("legacyFML")) + if (version->traits.contains("legacyFML")) { fmllibsStart(); } @@ -378,7 +318,8 @@ void OneSixUpdate::jarlibFailed() { QStringList failed = jarlibDownloadJob->getFailedFiles(); QString failed_all = failed.join("\n"); - emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all)); + emitFailed( + tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all)); } void OneSixUpdate::stripJar(QString origPath, QString newPath) @@ -462,7 +403,6 @@ bool OneSixUpdate::MergeZipFiles(QuaZip *into, QString from) return true; } - void OneSixUpdate::fmllibsStart() { // Get the mod list @@ -471,7 +411,7 @@ void OneSixUpdate::fmllibsStart() bool forge_present = false; QString version = inst->intendedVersionId(); - auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; + auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping; if (!fmlLibsMapping.contains(version)) { assetIndexStart(); @@ -528,7 +468,7 @@ void OneSixUpdate::fmllibsStart() void OneSixUpdate::fmllibsFinished() { legacyDownloadJob.reset(); - if(!fmlLibsToProcess.isEmpty()) + if (!fmlLibsToProcess.isEmpty()) { setStatus(tr("Copying FML libraries into the instance...")); OneSixInstance *inst = (OneSixInstance *)m_inst; @@ -539,7 +479,7 @@ void OneSixUpdate::fmllibsFinished() progress(index, fmlLibsToProcess.size()); auto entry = metacache->resolveEntry("fmllibs", lib.filename); auto path = PathCombine(inst->libDir(), lib.filename); - if(!ensureFilePathExists(path)) + if (!ensureFilePathExists(path)) { emitFailed(tr("Failed creating FML library folder inside the instance.")); return; diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index 00f7c135..139143db 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -36,9 +36,7 @@ public: private slots: - void versionFileStart(); - void versionFileFinished(); - void versionFileFailed(); + void versionUpdateFailed(QString reason); void jarlibStart(); void jarlibFinished(); @@ -58,12 +56,14 @@ slots: void stripJar(QString origPath, QString newPath); bool MergeZipFiles(QuaZip *into, QString from); private: - NetJobPtr specificVersionDownloadJob; NetJobPtr jarlibDownloadJob; NetJobPtr legacyDownloadJob; - // target version, determined during this task + /// target version, determined during this task std::shared_ptr<MinecraftVersion> targetVersion; + /// the task that is spawned for version updates + std::shared_ptr<Task> versionUpdateTask; + OneSixInstance *m_inst = nullptr; QString jarHashOnEntry; QList<FMLlib> fmlLibsToProcess; diff --git a/logic/VersionFilterData.cpp b/logic/VersionFilterData.cpp index 8322dad4..8b521266 100644 --- a/logic/VersionFilterData.cpp +++ b/logic/VersionFilterData.cpp @@ -1,4 +1,5 @@ #include "VersionFilterData.h" +#include "minecraft/ParseUtils.h" extern VersionFilterData g_VersionFilterData = VersionFilterData(); @@ -57,8 +58,11 @@ VersionFilterData::VersionFilterData() // don't use installers for those. forgeInstallerBlacklist = QSet<QString>({"1.5.2"}); - legacyLaunchWhitelist = - QSet<QString>({"1.5.2", "1.5.1", "1.5", "1.4.7", "1.4.6", "1.4.5", "1.4.4", "1.4.3", - "1.4.2", "1.4.1", "1.4", "1.3.2", "1.3.1", "1.3", "1.2.5", "1.2.4", - "1.2.3", "1.2.2", "1.2.1", "1.1", "1.0.1", "1.0"}); + // these won't show up in version lists because they are extremely bad and dangerous + legacyBlacklist = QSet<QString>({"rd-160052"}); + /* + * nothing older than this will be accepted from Mojang servers + * (these versions need to be tested by us first) + */ + legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00"); } diff --git a/logic/VersionFilterData.h b/logic/VersionFilterData.h index 562654a9..e010adc7 100644 --- a/logic/VersionFilterData.h +++ b/logic/VersionFilterData.h @@ -2,6 +2,7 @@ #include <QMap> #include <QString> #include <QSet> +#include <QDateTime> struct FMLlib { @@ -17,7 +18,9 @@ struct VersionFilterData QMap<QString, QList<FMLlib>> fmlLibsMapping; // set of minecraft versions for which using forge installers is blacklisted QSet<QString> forgeInstallerBlacklist; - // set of 'legacy' versions (ones that use the legacy launch) - QSet<QString> legacyLaunchWhitelist; + // set of 'legacy' versions that will not show up in the version lists. + QSet<QString> legacyBlacklist; + // no new versions below this date will be accepted from Mojang servers + QDateTime legacyCutoffDate; }; extern VersionFilterData g_VersionFilterData; diff --git a/logic/minecraft/JarMod.cpp b/logic/minecraft/JarMod.cpp index 99a30aa5..18a9411c 100644 --- a/logic/minecraft/JarMod.cpp +++ b/logic/minecraft/JarMod.cpp @@ -1,5 +1,6 @@ #include "JarMod.h" #include "logic/MMCJson.h" +using namespace MMCJson; JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename) { @@ -28,6 +29,7 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename) }; readString("url", out->baseurl); + readString("MMC-hint", out->hint); readString("MMC-absoluteUrl", out->absoluteUrl); if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty()) { @@ -36,6 +38,16 @@ JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename) return out; } +QJsonObject Jarmod::toJson() +{ + QJsonObject out; + writeString(out, "name", name); + writeString(out, "url", baseurl); + writeString(out, "MMC-absoluteUrl", absoluteUrl); + writeString(out, "MMC-hint", hint); + return out; +} + QString Jarmod::url() { if(!absoluteUrl.isEmpty()) diff --git a/logic/minecraft/JarMod.h b/logic/minecraft/JarMod.h index da5d8db0..c438dbcd 100644 --- a/logic/minecraft/JarMod.h +++ b/logic/minecraft/JarMod.h @@ -8,6 +8,7 @@ class Jarmod { public: /* methods */ static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename); + QJsonObject toJson(); QString url(); public: /* data */ QString name; diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp index 2191e8af..e0cbce8d 100644 --- a/logic/minecraft/MinecraftVersion.cpp +++ b/logic/minecraft/MinecraftVersion.cpp @@ -1,29 +1,26 @@ #include "MinecraftVersion.h" #include "VersionFinal.h" +#include "VersionBuildError.h" +#include "VersionBuilder.h" bool MinecraftVersion::usesLegacyLauncher() { return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch"); } + QString MinecraftVersion::descriptor() { return m_descriptor; } + QString MinecraftVersion::name() { return m_name; } + QString MinecraftVersion::typeString() const { - if (is_latest && is_snapshot) - { - return QObject::tr("Latest snapshot"); - } - else if (is_latest) - { - return QObject::tr("Latest release"); - } - else if (is_snapshot) + if (is_snapshot) { return QObject::tr("Snapshot"); } @@ -32,22 +29,41 @@ QString MinecraftVersion::typeString() const return QObject::tr("Regular release"); } } + bool MinecraftVersion::hasJarMods() { return false; } -bool MinecraftVersion::isVanilla() + +bool MinecraftVersion::isMinecraftVersion() { return true; } +// 1. assume the local file is good. load, check. If it's good, apply. +// 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version). +void MinecraftVersion::applyFileTo(VersionFinal *version) +{ + QFileInfo versionFile(QString("versions/%1/%1.json").arg(m_descriptor)); + + auto versionObj = VersionBuilder::parseJsonFile(versionFile, false, false); + versionObj->applyTo(version); +} + void MinecraftVersion::applyTo(VersionFinal *version) { - // FIXME: make this work. - if(m_versionSource != Builtin) + // do we have this one cached? + if (m_versionSource == Local) { + applyFileTo(version); return; } + // if not builtin, do not proceed any further. + if (m_versionSource != Builtin) + { + throw VersionIncomplete(QObject::tr( + "Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor)); + } if (!m_descriptor.isNull()) { version->id = m_descriptor; @@ -81,15 +97,35 @@ void MinecraftVersion::applyTo(VersionFinal *version) } version->traits.unite(m_traits); } + int MinecraftVersion::getOrder() { return order; } + void MinecraftVersion::setOrder(int order) { this->order = order; } + QList<JarmodPtr> MinecraftVersion::getJarMods() { return QList<JarmodPtr>(); } + +QString MinecraftVersion::getPatchName() +{ + return "Minecraft"; +} +QString MinecraftVersion::getPatchVersion() +{ + return m_descriptor; +} +QString MinecraftVersion::getPatchID() +{ + return "net.minecraft"; +} +QString MinecraftVersion::getPatchFilename() +{ + return QString(); +} diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h index 6a1c54cb..02afd709 100644 --- a/logic/minecraft/MinecraftVersion.h +++ b/logic/minecraft/MinecraftVersion.h @@ -22,26 +22,49 @@ #include "logic/BaseVersion.h" #include "VersionPatch.h" #include "VersionFile.h" +#include "VersionSource.h" class VersionFinal; +class MinecraftVersion; +typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr; -struct MinecraftVersion : public BaseVersion, public VersionPatch +class MinecraftVersion : public BaseVersion, public VersionPatch { +public: /* methods */ + bool usesLegacyLauncher(); + virtual QString descriptor() override; + virtual QString name() override; + virtual QString typeString() const override; + virtual bool hasJarMods() override; + virtual bool isMinecraftVersion() override; + virtual void applyTo(VersionFinal *version) override; + virtual int getOrder(); + virtual void setOrder(int order); + virtual QList<JarmodPtr> getJarMods() override; + virtual QString getPatchID() override; + virtual QString getPatchVersion() override; + virtual QString getPatchName() override; + virtual QString getPatchFilename() override; + bool needsUpdate() + { + return m_versionSource == Remote; + } + bool hasUpdate() + { + return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate); + } + +private: /* methods */ + void applyFileTo(VersionFinal *version); + +public: /* data */ /// The URL that this version will be downloaded from. maybe. QString download_url; - /// is this the latest version? - bool is_latest = false; - /// is this a snapshot? bool is_snapshot = false; - /// where is this from? - enum VersionSource - { - Builtin, - Mojang - } m_versionSource = Builtin; + VersionSource m_versionSource = Builtin; /// the human readable version name QString m_name; @@ -74,31 +97,7 @@ struct MinecraftVersion : public BaseVersion, public VersionPatch /// order of this file... default = -2 int order = -2; - - bool usesLegacyLauncher(); - virtual QString descriptor() override; - virtual QString name() override; - virtual QString typeString() const override; - virtual bool hasJarMods() override; - virtual bool isVanilla() override; - virtual void applyTo(VersionFinal *version) override; - virtual int getOrder(); - virtual void setOrder(int order); - virtual QList<JarmodPtr> getJarMods() override; - virtual QString getPatchID() - { - return "net.minecraft"; - } - virtual QString getPatchVersion() - { - return m_descriptor; - } - virtual QString getPatchName() - { - return "Minecraft"; - } - virtual QString getPatchFilename() - { - return QString(); - }; + + /// an update available from Mojang + MinecraftVersionPtr upstreamUpdate; }; diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp index 1aa220e8..5a5ea348 100644 --- a/logic/minecraft/MinecraftVersionList.cpp +++ b/logic/minecraft/MinecraftVersionList.cpp @@ -13,27 +13,35 @@ * limitations under the License. */ -#include "MinecraftVersionList.h" -#include "MultiMC.h" -#include "logic/net/URLConstants.h" +#include <QtXml> #include "logic/MMCJson.h" -#include "ParseUtils.h" +#include <QtAlgorithms> +#include <QtNetwork> -#include <QtXml> +#include "MultiMC.h" +#include "MMCError.h" -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonArray> -#include <QJsonValue> -#include <QJsonParseError> +#include "MinecraftVersionList.h" +#include "logic/net/URLConstants.h" -#include <QtAlgorithms> +#include "ParseUtils.h" +#include "VersionBuilder.h" +#include <logic/VersionFilterData.h> +#include <pathutils.h> -#include <QtNetwork> +class ListLoadError : public MMCError +{ +public: + ListLoadError(QString cause) : MMCError(cause) {}; + virtual ~ListLoadError() noexcept + { + } +}; MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) { loadBuiltinList(); + loadCachedList(); } Task *MinecraftVersionList::getLoadTask() @@ -68,6 +76,35 @@ void MinecraftVersionList::sortInternal() qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); } +void MinecraftVersionList::loadCachedList() +{ + QFile localIndex("versions/versions.json"); + if (!localIndex.exists()) + { + return; + } + if (!localIndex.open(QIODevice::ReadOnly)) + { + // FIXME: this is actually a very bad thing! How do we deal with this? + QLOG_ERROR() << "The minecraft version cache can't be read."; + return; + } + auto data = localIndex.readAll(); + try + { + loadMojangList(data, Local); + } + catch (MMCError &e) + { + // the cache has gone bad for some reason... flush it. + QLOG_ERROR() << "The minecraft version cache is corrupted. Flushing cache."; + localIndex.close(); + localIndex.remove(); + return; + } + m_hasLocalIndex = true; +} + void MinecraftVersionList::loadBuiltinList() { // grab the version list data from internal resources. @@ -93,19 +130,22 @@ void MinecraftVersionList::loadBuiltinList() continue; } + if (g_VersionFilterData.legacyBlacklist.contains(versionID)) + { + QLOG_ERROR() << "Blacklisted legacy version ignored: " << versionID; + continue; + } + // Now, we construct the version object and add it to the list. std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion()); mcVersion->m_name = mcVersion->m_descriptor = versionID; // Parse the timestamp. - try - { - parse_timestamp(versionObj.value("releaseTime").toString(""), - mcVersion->m_releaseTimeString, mcVersion->m_releaseTime); - } - catch (MMCError &e) + if (!parse_timestamp(versionObj.value("releaseTime").toString(""), + mcVersion->m_releaseTimeString, mcVersion->m_releaseTime)) { - QLOG_ERROR() << "Error while parsing version" << versionID << ":" << e.cause(); + QLOG_ERROR() << "Error while parsing version" << versionID + << ": invalid version timestamp"; continue; } @@ -113,7 +153,7 @@ void MinecraftVersionList::loadBuiltinList() mcVersion->download_url = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/"; - mcVersion->m_versionSource = MinecraftVersion::Builtin; + mcVersion->m_versionSource = Builtin; mcVersion->m_appletClass = versionObj.value("appletClass").toString(""); mcVersion->m_mainClass = versionObj.value("mainClass").toString(""); mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy"); @@ -124,10 +164,141 @@ void MinecraftVersionList::loadBuiltinList() mcVersion->m_traits.insert(MMCJson::ensureString(traitVal)); } } + m_lookup[versionID] = mcVersion; m_vlist.append(mcVersion); } } +void MinecraftVersionList::loadMojangList(QByteArray data, VersionSource source) +{ + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) + { |
