diff options
Diffstat (limited to 'logic')
-rw-r--r-- | logic/BaseInstaller.cpp | 2 | ||||
-rw-r--r-- | logic/ForgeInstaller.cpp | 6 | ||||
-rw-r--r-- | logic/ForgeInstaller.h | 4 | ||||
-rw-r--r-- | logic/LiteLoaderInstaller.cpp | 4 | ||||
-rw-r--r-- | logic/MMCJson.cpp | 53 | ||||
-rw-r--r-- | logic/MMCJson.h | 46 | ||||
-rw-r--r-- | logic/OneSixFTBInstance.cpp | 72 | ||||
-rw-r--r-- | logic/OneSixInstance.cpp | 33 | ||||
-rw-r--r-- | logic/OneSixInstance.h | 10 | ||||
-rw-r--r-- | logic/OneSixInstance_p.h | 6 | ||||
-rw-r--r-- | logic/OneSixUpdate.cpp | 8 | ||||
-rw-r--r-- | logic/OneSixVersionBuilder.cpp | 951 | ||||
-rw-r--r-- | logic/OneSixVersionBuilder.h | 30 | ||||
-rw-r--r-- | logic/VersionFile.cpp | 549 | ||||
-rw-r--r-- | logic/VersionFile.h | 97 | ||||
-rw-r--r-- | logic/VersionFinal.cpp (renamed from logic/OneSixVersion.cpp) | 52 | ||||
-rw-r--r-- | logic/VersionFinal.h (renamed from logic/OneSixVersion.h) | 10 | ||||
-rw-r--r-- | logic/lists/LiteLoaderVersionList.cpp | 55 | ||||
-rw-r--r-- | logic/lists/LiteLoaderVersionList.h | 11 |
19 files changed, 936 insertions, 1063 deletions
diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp index 92aa0c92..669fd0ac 100644 --- a/logic/BaseInstaller.cpp +++ b/logic/BaseInstaller.cpp @@ -17,7 +17,7 @@ #include <QFile> -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 3e18d17f..6f238c21 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -14,7 +14,7 @@ */ #include "ForgeInstaller.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "net/HttpMetaCache.h" #include <quazip.h> @@ -33,7 +33,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - std::shared_ptr<OneSixVersion> newVersion; + std::shared_ptr<VersionFinal> newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -66,7 +66,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // read the forge version info { - newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); + newVersion = VersionFinal::fromJson(versionInfoVal.toObject()); if (!newVersion) return; } diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index c5052092..df029f38 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -20,7 +20,7 @@ #include <QString> #include <memory> -class OneSixVersion; +class VersionFinal; class ForgeInstaller : public BaseInstaller { @@ -33,7 +33,7 @@ public: private: // the version, read from the installer - std::shared_ptr<OneSixVersion> m_forge_version; + std::shared_ptr<VersionFinal> m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp index 126027fb..bb4b07ca 100644 --- a/logic/LiteLoaderInstaller.cpp +++ b/logic/LiteLoaderInstaller.cpp @@ -20,7 +20,7 @@ #include "logger/QsLog.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" @@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to) obj.insert("+libraries", libraries); obj.insert("name", QString("LiteLoader")); obj.insert("fileId", id()); - obj.insert("version", to->intendedVersionId()); + obj.insert("version", m_version->version); obj.insert("mcVersion", to->intendedVersionId()); QFile file(filename(to->instanceRoot())); diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp new file mode 100644 index 00000000..14cde0c1 --- /dev/null +++ b/logic/MMCJson.cpp @@ -0,0 +1,53 @@ +#include "MMCJson.h" +#include <QString> + +bool MMCJson::ensureBoolean(const QJsonValue val, const QString what) +{ + if (!val.isBool()) + throw JSONValidationError(what + " is not boolean"); + return val.isBool(); +} + +QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what) +{ + if(val.isNull()) + throw JSONValidationError(what + " does not exist"); + return val; +} + +QJsonArray MMCJson::ensureArray(const QJsonValue val, const QString what) +{ + if (!val.isArray()) + throw JSONValidationError(what + " is not an array"); + return val.toArray(); +} + +double MMCJson::ensureDouble(const QJsonValue val, const QString what) +{ + if (!val.isDouble()) + throw JSONValidationError(what + " is not a number"); + double ret = val.toDouble(); +} + +int MMCJson::ensureInteger(const QJsonValue val, const QString what) +{ + double ret = ensureDouble(val, what); + if (fmod(ret, 1) != 0) + throw JSONValidationError(what + " is not an integer"); + return ret; +} + +QJsonObject MMCJson::ensureObject(const QJsonValue val, const QString what) +{ + if (!val.isObject()) + throw JSONValidationError(what + " is not an object"); + return val.toObject(); +} + +QString MMCJson::ensureString(const QJsonValue val, const QString what) +{ + if (!val.isString()) + throw JSONValidationError(what + " is not a string"); + return val.toString(); +} + diff --git a/logic/MMCJson.h b/logic/MMCJson.h new file mode 100644 index 00000000..3e7342b5 --- /dev/null +++ b/logic/MMCJson.h @@ -0,0 +1,46 @@ +/** + * Some de-bullshitting for Qt JSON failures. + * + * Simple exception-throwing + */ + +#pragma once +#include <QJsonValue> +#include <QJsonObject> +#include <QJsonArray> +#include "MMCError.h" + +class JSONValidationError : public MMCError +{ +public: + JSONValidationError(QString cause) : MMCError(cause) {}; + virtual QString errorName() + { + return "JSONValidationError"; + }; + virtual ~JSONValidationError() {}; +}; + +namespace MMCJson +{ +/// make sure the value exists. throw otherwise. +QJsonValue ensureExists(QJsonValue val, const QString what = "value"); + +/// make sure the value is converted into an object. throw otherwise. +QJsonObject ensureObject(const QJsonValue val, const QString what = "value"); + +/// make sure the value is converted into an array. throw otherwise. +QJsonArray ensureArray(const QJsonValue val, QString what = "value"); + +/// make sure the value is converted into a string. throw otherwise. +QString ensureString(const QJsonValue val, QString what = "value"); + +/// make sure the value is converted into a boolean. throw otherwise. +bool ensureBoolean(const QJsonValue val, QString what = "value"); + +/// make sure the value is converted into an integer. throw otherwise. +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"); +} diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 91efce8e..cdb3f53e 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -1,6 +1,6 @@ #include "OneSixFTBInstance.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "tasks/SequentialTask.h" #include "ForgeInstaller.h" @@ -10,76 +10,6 @@ #include "MultiMC.h" #include "pathutils.h" -class OneSixFTBInstanceForge : public Task -{ - Q_OBJECT -public: - explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) : - Task(parent), instance(inst), version("Forge " + version) - { - } - - void executeTask() - { - for (int i = 0; i < MMC->forgelist()->count(); ++i) - { - if (MMC->forgelist()->at(i)->name() == version) - { - forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i)); - break; - } - } - if (!forgeVersion) - { - emitFailed(QString("Couldn't find forge version ") + version ); - return; - } - entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); - if (entry->stale) - { - setStatus(tr("Downloading Forge...")); - fjob = new NetJob("Forge download"); - fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry)); - connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);}); - connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge); - connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); }); - fjob->start(); - } - else - { - installForge(); - } - } - -private -slots: - void installForge() - { - setStatus(tr("Installing Forge...")); - QString forgePath = entry->getFullPath(); - ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!instance->reloadVersion()) - { - emitFailed(tr("Couldn't load the version config")); - return; - } - auto version = instance->getFullVersion(); - if (!forge.add(instance)) - { - emitFailed(tr("Couldn't install Forge")); - return; - } - emitSucceeded(); - } - -private: - OneSixFTBInstance *instance; - QString version; - ForgeVersionPtr forgeVersion; - MetaEntryPtr entry; - NetJob *fjob; -}; - OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : OneSixInstance(rootDir, settings, parent) { diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 10411c56..f6fe49f1 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -19,7 +19,7 @@ #include "OneSixInstance_p.h" #include "OneSixUpdate.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "pathutils.h" #include "logger/QsLog.h" #include "assets/AssetsUtils.h" @@ -27,6 +27,7 @@ #include "icons/IconList.h" #include "MinecraftProcess.h" #include "gui/dialogs/OneSixModEditDialog.h" +#include <MMCError.h> OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent) @@ -34,8 +35,8 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, I_D(OneSixInstance); d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); - d->version.reset(new OneSixVersion(this, this)); - d->vanillaVersion.reset(new OneSixVersion(this, this)); + d->version.reset(new VersionFinal(this, this)); + d->vanillaVersion.reset(new VersionFinal(this, this)); } void OneSixInstance::init() @@ -79,7 +80,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with) return result; } -QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version) +QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version) { QDir assetsDir = QDir("assets/"); QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); @@ -316,25 +317,27 @@ QString OneSixInstance::currentVersionId() const return intendedVersionId(); } -bool OneSixInstance::reloadVersion(QWidget *widgetParent) +bool OneSixInstance::reloadVersion() { I_D(OneSixInstance); - bool ret = d->version->reload(widgetParent, false, externalPatches()); - if (ret) - { - ret = d->vanillaVersion->reload(widgetParent, true, externalPatches()); - } - if (ret) + try { + d->version->reload(false, externalPatches()); + d->vanillaVersion->reload(true, externalPatches()); setFlags(flags() & ~VersionBrokenFlag); emit versionReloaded(); + return true; } - else + catch(MMCError error) { + d->version->clear(); + d->vanillaVersion->clear(); setFlags(flags() | VersionBrokenFlag); + //TODO: rethrow to show some error message(s)? + emit versionReloaded(); + return false; } - return ret; } void OneSixInstance::clearVersion() @@ -345,13 +348,13 @@ void OneSixInstance::clearVersion() emit versionReloaded(); } -std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const +std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const { I_D(const OneSixInstance); return d->version; } -std::shared_ptr<OneSixVersion> OneSixInstance::getVanillaVersion() const +std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const { I_D(const OneSixInstance); return d->vanillaVersion; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 06fd9de3..c7ef2ee8 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -17,7 +17,7 @@ #include "BaseInstance.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "ModList.h" class OneSixInstance : public BaseInstance @@ -54,13 +54,13 @@ public: virtual QDialog *createModEditDialog(QWidget *parent) override; /// reload the full version json files. return true on success! - bool reloadVersion(QWidget *widgetParent = 0); + bool reloadVersion(); /// clears all version information in preparation for an update void clearVersion(); /// get the current full version info - std::shared_ptr<OneSixVersion> getFullVersion() const; + std::shared_ptr<VersionFinal> getFullVersion() const; /// gets the current version info, but only for version.json - std::shared_ptr<OneSixVersion> getVanillaVersion() const; + std::shared_ptr<VersionFinal> getVanillaVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; @@ -80,5 +80,5 @@ signals: private: QStringList processMinecraftArgs(AuthSessionPtr account); - QDir reconstructAssets(std::shared_ptr<OneSixVersion> version); + QDir reconstructAssets(std::shared_ptr<VersionFinal> version); }; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index 0cc46f33..2dffa62c 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -16,13 +16,13 @@ #pragma once #include "BaseInstance_p.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "ModList.h" struct OneSixInstancePrivate : public BaseInstancePrivate { - std::shared_ptr<OneSixVersion> version; - std::shared_ptr<OneSixVersion> vanillaVersion; + std::shared_ptr<VersionFinal> version; + std::shared_ptr<VersionFinal> vanillaVersion; std::shared_ptr<ModList> loader_mod_list; std::shared_ptr<ModList> resource_pack_list; }; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f87c65e7..750aeabb 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -25,7 +25,7 @@ #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" #include "net/ForgeMirrors.h" @@ -150,7 +150,7 @@ void OneSixUpdate::assetIndexStart() { setStatus(tr("Updating assets index...")); OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + std::shared_ptr<VersionFinal> version = inst->getFullVersion(); QString assetName = version->assets; QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; QString localPath = assetName + ".json"; @@ -174,7 +174,7 @@ void OneSixUpdate::assetIndexFinished() AssetsIndex index; OneSixInstance *inst = (OneSixInstance *)m_inst; - std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + std::shared_ptr<VersionFinal> version = inst->getFullVersion(); QString assetName = version->assets; QString asset_fname = "assets/indexes/" + assetName + ".json"; @@ -243,7 +243,7 @@ void OneSixUpdate::jarlibStart() } // Build a list of URLs that will need to be downloaded. - std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + std::shared_ptr<VersionFinal> version = inst->getFullVersion(); // minecraft.jar for this version { QString version_id = version->id; diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index f6917697..0c63dc2a 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -26,806 +26,35 @@ #include <QDir> #include <QDebug> -#include "OneSixVersion.h" +#include "VersionFinal.h" #include "OneSixInstance.h" #include "OneSixRule.h" +#include "VersionFile.h" +#include "MMCJson.h" #include "modutils.h" #include "logger/QsLog.h" -#define CURRENT_MINIMUM_LAUNCHER_VERSION 14 - -struct VersionFile -{ - int order; - QString name; - QString fileId; - QString version; - // TODO use the mcVersion to determine if a version file should be removed on update - QString mcVersion; - QString filename; - // TODO requirements - // QMap<QString, QString> requirements; - QString id; - QString mainClass; - QString overwriteMinecraftArguments; - QString addMinecraftArguments; - QString removeMinecraftArguments; - QString processArguments; - QString type; - QString releaseTime; - QString time; - QString assets; - int minimumLauncherVersion = -1; - - bool shouldOverwriteTweakers = false; - QStringList overwriteTweakers; - QStringList addTweakers; - QStringList removeTweakers; - - struct Library - { - QString name; - QString url; - QString hint; - QString absoluteUrl; - bool applyExcludes = false; - QStringList excludes; - bool applyNatives = false; - QList<QPair<OpSys, QString>> natives; - bool applyRules = false; - QList<std::shared_ptr<Rule>> rules; - - // user for '+' libraries - enum InsertType - { - Apply, - Append, - Prepend, - Replace - }; - InsertType insertType = Append; - QString insertData; - enum DependType - { - Soft, - Hard - }; - DependType dependType = Soft; - }; - bool shouldOverwriteLibs = false; - QList<Library> overwriteLibs; - QList<Library> addLibs; - QList<QString> removeLibs; - - enum ApplyError - { - LauncherVersionError, - OtherError, - NoApplyError - }; - - static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename, - bool &isError) - { - isError = true; - Library out; - if (!libObj.contains("name")) - { - QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; - return out; - } - out.name = libObj.value("name").toString(); - - auto readString = [libObj, filename](const QString &key, QString &variable) - { - if (libObj.contains(key)) - { - QJsonValue val = libObj.value(key); - if (!val.isString()) - { - QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; - } - else - { - variable = val.toString(); - } - } - }; - - readString("url", out.url); - readString("MMC-hint", out.hint); - readString("MMC-absulute_url", out.absoluteUrl); - readString("MMC-absoluteUrl", out.absoluteUrl); - if (libObj.contains("extract")) - { - if (!libObj.value("extract").isObject()) - { - QLOG_ERROR() - << filename - << "contains a library with an 'extract' field that's not an object"; - return out; - } - QJsonObject extractObj = libObj.value("extract").toObject(); - if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) - { - QLOG_ERROR() << filename - << "contains a library with an invalid 'extract' field"; - return out; - } - out.applyExcludes = true; - QJsonArray excludeArray = extractObj.value("exclude").toArray(); - for (auto excludeVal : excludeArray) - { - if (!excludeVal.isString()) - { - QLOG_WARN() << filename << "contains a library that contains an 'extract' " - "field that contains an invalid 'exclude' entry " - "(skipping)"; - } - else - { - out.excludes.append(excludeVal.toString()); - } - } - } - if (libObj.contains("natives")) - { - if (!libObj.value("natives").isObject()) - { - QLOG_ERROR() - << filename - << "contains a library with a 'natives' field that's not an object"; - return out; - } - out.applyNatives = true; - QJsonObject nativesObj = libObj.value("natives").toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) - { - if (!it.value().isString()) - { - QLOG_WARN() << filename << "contains an invalid native (skipping)"; - } - OpSys opSys = OpSys_fromString(it.key()); - if (opSys != Os_Other) - { - out.natives.append(qMakePair(opSys, it.value().toString())); - } - } - } - if (libObj.contains("rules")) - { - out.applyRules = true; - out.rules = rulesFromJsonV4(libObj); - } - isError = false; - return out; - } - static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, - const bool requireOrder, bool &isError, const OneSixVersionBuilder::ParseFlags flags = OneSixVersionBuilder::NoFlags) - { - VersionFile out; - isError = true; - if (doc.isEmpty() || doc.isNull()) - { - QLOG_ERROR() << filename << "is empty or null"; - return out; - } - if (!doc.isObject()) - { - QLOG_ERROR() << "The root of" << filename << "is not an object"; - return out; - } - - QJsonObject root = doc.object(); - - if (requireOrder) - { - if (root.contains("order")) - { - if (root.value("order").isDouble()) - { - out.order = root.value("order").toDouble(); - } - else - { - QLOG_ERROR() << "'order' field contains an invalid value in" << filename; - return out; - } - } - else - { - QLOG_ERROR() << filename << "doesn't contain an order field"; - } - } - - 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.filename = filename; - - auto readString = [root, filename](const QString &key, QString &variable) - { - if (root.contains(key)) - { - QJsonValue val = root.value(key); - if (!val.isString()) - { - QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; - } - else - { - variable = val.toString(); - } - } - }; - - if (!(flags & OneSixVersionBuilder::IsFTBPackJson)) - { - readString("id", out.id); - } - readString("mainClass", out.mainClass); - readString("processArguments", out.processArguments); - readString("minecraftArguments", out.overwriteMinecraftArguments); - readString("+minecraftArguments", out.addMinecraftArguments); - readString("-minecraftArguments", out.removeMinecraftArguments); - readString("type", out.type); - readString("releaseTime", out.releaseTime); - readString("time", out.time); - readString("assets", out.assets); - if (root.contains("minimumLauncherVersion")) - { - QJsonValue val = root.value("minimumLauncherVersion"); - if (!val.isDouble()) - { - QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename - << "(skipping)"; - } - else - { - out.minimumLauncherVersion = val.toDouble(); - } - } - - if (root.contains("tweakers")) - { - QJsonValue tweakersVal = root.value("tweakers"); - if (!tweakersVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a 'tweakers' field, but it's not an array"; - return out; - } - out.shouldOverwriteTweakers = true; - QJsonArray tweakers = root.value("tweakers").toArray(); - for (auto tweakerVal : tweakers) - { - if (!tweakerVal.isString()) - { - QLOG_ERROR() << filename - << "contains a 'tweakers' field entry that's not a string"; - return out; - } - out.overwriteTweakers.append(tweakerVal.toString()); - } - } - if (root.contains("+tweakers")) - { - QJsonValue tweakersVal = root.value("+tweakers"); - if (!tweakersVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a '+tweakers' field, but it's not an array"; - return out; - } - QJsonArray tweakers = root.value("+tweakers").toArray(); - for (auto tweakerVal : tweakers) - { - if (!tweakerVal.isString()) - { - QLOG_ERROR() << filename - << "contains a '+tweakers' field entry that's not a string"; - return out; - } - out.addTweakers.append(tweakerVal.toString()); - } - } - if (root.contains("-tweakers")) - { - QJsonValue tweakersVal = root.value("-tweakers"); - if (!tweakersVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a '-tweakers' field, but it's not an array"; - return out; - } - out.shouldOverwriteTweakers = true; - QJsonArray tweakers = root.value("-tweakers").toArray(); - for (auto tweakerVal : tweakers) - { - if (!tweakerVal.isString()) - { - QLOG_ERROR() << filename - << "contains a '-tweakers' field entry that's not a string"; - return out; - } - out.removeTweakers.append(tweakerVal.toString()); - } - } - - if (root.contains("libraries")) - { - out.shouldOverwriteLibs = !(flags & OneSixVersionBuilder::IsFTBPackJson); - QJsonValue librariesVal = root.value("libraries"); - if (!librariesVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a 'libraries' field, but its not an array"; - return out; - } - QJsonArray librariesArray = librariesVal.toArray(); - for (auto libVal : librariesArray) - { - if (!libVal.isObject()) - { - QLOG_ERROR() << filename << "contains a library that's not an object"; - return out; - } - QJsonObject libObj = libVal.toObject(); - bool error; - Library lib = fromLibraryJson(libObj, filename, error); - if (error) - { - QLOG_ERROR() << "Error while reading a library entry in" << filename; - return out; - } - if (flags & OneSixVersionBuilder::IsFTBPackJson) - { - lib.hint = "local"; - lib.insertType = Library::Prepend; - out.addLibs.prepend(lib); - } - else - { - out.overwriteLibs.append(lib); - } - } - } - if (root.contains("+libraries")) - { - QJsonValue librariesVal = root.value("+libraries"); - if (!librariesVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a '+libraries' field, but its not an array"; - return out; - } - QJsonArray librariesArray = librariesVal.toArray(); - for (auto libVal : librariesArray) - { - if (!libVal.isObject()) - { - QLOG_ERROR() << filename << "contains a library that's not an object"; - return out; - } - QJsonObject libObj = libVal.toObject(); - bool error; - Library lib = fromLibraryJson(libObj, filename, error); - if (error) - { - QLOG_ERROR() << "Error while reading a library entry in" << filename; - return out; - } - if (!libObj.contains("insert")) - { - QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in" - << filename; - return out; - } - QJsonValue insertVal = libObj.value("insert"); - QString insertString; - { - if (insertVal.isString()) - { - insertString = insertVal.toString(); - } - else if (insertVal.isObject()) - { - QJsonObject insertObj = insertVal.toObject(); - if (insertObj.isEmpty()) - { - QLOG_ERROR() << "One library has an empty insert object in" - << filename; - return out; - } - insertString = insertObj.keys().first(); - lib.insertData = insertObj.value(insertString).toString(); - } - } - if (insertString == "apply") - { - lib.insertType = Library::Apply; - } - else if (insertString == "prepend") - { - lib.insertType = Library::Prepend; - } - else if (insertString == "append") - { - lib.insertType = Library::Prepend; - } - else if (insertString == "replace") - { - lib.insertType = Library::Replace; - } - else - { - QLOG_ERROR() << "A '+' library in" << filename - << "contains an invalid insert type"; - return out; - } - if (libObj.contains("MMC-depend") && libObj.value("MMC-depend").isString()) - { - const QString dependString = libObj.value("MMC-depend").toString(); - if (dependString == "hard") - { - lib.dependType = Library::Hard; - } - else if (dependString == "soft") - { - lib.dependType = Library::Soft; - } - else - { - QLOG_ERROR() << "A '+' library in" << filename - << "contains an invalid depend type"; - return out; - } - } - out.addLibs.append(lib); - } - } - if (root.contains("-libraries")) - { - QJsonValue librariesVal = root.value("-libraries"); - if (!librariesVal.isArray()) - { - QLOG_ERROR() << filename - << "contains a '-libraries' field, but its not an array"; - return out; - } - QJsonArray librariesArray = librariesVal.toArray(); - for (auto libVal : librariesArray) - { - if (!libVal.isObject()) - { - QLOG_ERROR() << filename << "contains a library that's not an object"; - return out; - } - QJsonObject libObj = libVal.toObject(); - if (!libObj.contains("name")) - { - QLOG_ERROR() << filename << "contains a library without a name"; - return out; - } - if (!libObj.value("name").isString()) - { - QLOG_ERROR() << filename - << "contains a library without a valid 'name' field"; - return out; - } - out.removeLibs.append(libObj.value("name").toString()); - } - } - - isError = false; - return out; - } - - static std::shared_ptr<OneSixLibrary> createLibrary(const Library &lib) - { - std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib.name)); - if (!lib.url.isEmpty()) - { - out->setBaseUrl(lib.url); - } - out->setHint(lib.hint); - if (!lib.absoluteUrl.isEmpty()) - { - out->setAbsoluteUrl(lib.absoluteUrl); - } - out->setAbsoluteUrl(lib.absoluteUrl); - out->extract_excludes = lib.excludes; - for (auto native : lib.natives) - { - out->addNative(native.first, native.second); - } - out->setRules(lib.rules); - out->finalize(); - return out; - } - int findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, const QString &needle) - { - for (int i = 0; i < haystack.size(); ++i) - { - if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix) - .indexIn(haystack.at(i)->rawName()) != -1) - { - return i; - } - } - return -1; - } - ApplyError applyTo(OneSixVersion *version) - { - if (minimumLauncherVersion != -1) - { - if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) - { - QLOG_ERROR() << filename << "is for a different launcher version (" - << minimumLauncherVersion << "), current supported is" - << CURRENT_MINIMUM_LAUNCHER_VERSION; - return LauncherVersionError; - } - } - - if (!version->id.isNull() && !mcVersion.isNull()) - { - if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard) - .indexIn(version->id) == -1) - { - QLOG_ERROR() << filename << "is for a different version of Minecraft"; - return OtherError; - } - } - - if (!id.isNull()) - { - version->id = id; - } - if (!mainClass.isNull()) - { - version->mainClass = mainClass; - } - if (!processArguments.isNull()) - { - version->processArguments = processArguments; - } - if (!type.isNull()) - { - version->type = type; - } - if (!releaseTime.isNull()) - { - version->releaseTime = releaseTime; - } - if (!time.isNull()) - { - version->time = time; - } - if (!assets.isNull()) - { - version->assets = assets; - } - if (minimumLauncherVersion >= 0) - { - version->minimumLauncherVersion = minimumLauncherVersion; - } - if (!overwriteMinecraftArguments.isNull()) - { - version->minecraftArguments = overwriteMinecraftArguments; - } - if (!addMinecraftArguments.isNull()) - { - version->minecraftArguments += addMinecraftArguments; - } - if (!removeMinecraftArguments.isNull()) - { - version->minecraftArguments.remove(removeMinecraftArguments); - } - if (shouldOverwriteTweakers) - { - version->tweakers = overwriteTweakers; - } - for (auto tweaker : addTweakers) - { - version->tweakers += tweaker; - } - for (auto tweaker : removeTweakers) - { - version->tweakers.removeAll(tweaker); - } - if (shouldOverwriteLibs) - { - version->libraries.clear(); - for (auto lib : overwriteLibs) - { - version->libraries.append(createLibrary(lib)); - } - } - for (auto lib : addLibs) - { - switch (lib.insertType) - { - case Library::Apply: - { - - int index = findLibrary(version->libraries, lib.name); - if (index >= 0) - { - auto library = version->libraries[index]; - if (!lib.url.isNull()) - { - library->setBaseUrl(lib.url); - } - if (!lib.hint.isNull()) - { - library->setHint(lib.hint); - } - if (!lib.absoluteUrl.isNull()) - { - library->setAbsoluteUrl(lib.absoluteUrl); - } - if (lib.applyExcludes) - { - library->extract_excludes = lib.excludes; - } - if (lib.applyNatives) - { - library->clearSuffixes(); - for (auto native : lib.natives) - { - library->addNative(native.first, native.second); - } - } - if (lib.applyRules) - { - library->setRules(lib.rules); - } - library->finalize(); - } - else - { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; - } - break; - } - case Library::Append: - case Library::Prepend: - { - - const int startOfVersion = lib.name.lastIndexOf(':') + 1; - const int index = - findLibrary(version->libraries, - QString(lib.name).replace(startOfVersion, INT_MAX, '*')); - if (index < 0) - { - if (lib.insertType == Library::Append) - { - version->libraries.append(createLibrary(lib)); - } - else - { - version->libraries.prepend(createLibrary(lib)); - } - } - else - { - auto otherLib = version->libraries.at(index); - const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); - const Util::Version otherVersion = otherLib->version(); - // if the existing version is a hard dependency we can either use it or - // fail, but we can't change it - if (otherLib->dependType == OneSixLibrary::Hard) - { - // we need a higher version, or we're hard to and the versions aren't - // equal - if (ourVersion > otherVersion || - (lib.dependType == Library::Hard && ourVersion != otherVersion)) - { - QLOG_ERROR() << "Error resolving library dependencies between" - << otherLib->rawName() << "and" << lib.name << "in" - << filename; - return OtherError; - } - else - { - // the library is already existing, so we don't have to do anything - } - } - else if (otherLib->dependType == OneSixLibrary::Soft) - { - // if we are higher it means we should update - if (ourVersion > otherVersion) - { - auto library = createLibrary(lib); - if (Util::Version(otherLib->minVersion) < ourVersion) - { - library->minVersion = ourVersion.toString(); - } - version->libraries.replace(index, library); - } - else - { - // our version is smaller than the existing version, but we require - // it: fail - if (lib.dependType == Library::Hard) - { - QLOG_ERROR() << "Error resolving library dependencies between" - << otherLib->rawName() << "and" << lib.name << "in" - << filename; - return OtherError; - } - } - } - } - break; - } - case Library::Replace: - { - int index = findLibrary(version->libraries, lib.insertData); - if (index >= 0) - { - version->libraries.replace(index, createLibrary(lib)); - } - else - { - QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; - } - break; - } - } - } - for (auto lib : removeLibs) - { - int index = findLibrary(version->libraries, lib); - if (index >= 0) - { - version->libraries.removeAt(index); - } - else - { - QLOG_WARN() << "Couldn't find" << lib << "(skipping)"; - } - } - - OneSixVersion::VersionFile versionFile; - versionFile.name = name; - versionFile.id = fileId; - versionFile.version = this->version; - versionFile.mcVersion = mcVersion; - versionFile.filename = filename; - versionFile.order = order; - version->versionFiles.append(versionFile); - - return NoApplyError; - } -}; - OneSixVersionBuilder::OneSixVersionBuilder() { } -bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, - QWidget *widgetParent, const bool onlyVanilla, const QStringList &external) +void OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla, const QStringList &external) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = instance; - builder.m_widgetParent = widgetParent; - return builder.build(onlyVanilla, external); + builder.buildInternal(onlyVanilla, external); } -bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) +void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj) { OneSixVersionBuilder builder; builder.m_version = version; builder.m_instance = 0; - builder.m_widgetParent = 0; - return builder.read(obj); + builder.readJsonAndApply(obj); } -bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &external) +void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external) { m_version->clear(); @@ -836,37 +65,23 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte if(!external.isEmpty()) for (auto fileName : external) { QLOG_INFO() << "Reading" << fileName; - VersionFile file; - ParseFlags flags = fileName.endsWith("pack.json") ? IsFTBPackJson : NoFlags; - if (!read(QFileInfo(fileName), false, &file, flags)) - { - return false; - } + VersionFile file = parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json")); file.name = QFileInfo(fileName).fileName(); file.fileId = "org.multimc.external." + file.name; file.version = QString(); file.mcVersion = QString(); - bool isError = false; - auto errorcode = file.applyTo(m_version); - if(errorcode != VersionFile::NoApplyError) - return false; + file.applyTo(m_version); } // else, if there's custom json, we just do that. else if (QFile::exists(root.absoluteFilePath("custom.json"))) { QLOG_INFO() << "Reading custom.json"; - VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) - { - return false; - } + VersionFile file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false); file.name = "custom.json"; file.filename = "custom.json"; file.fileId = "org.multimc.custom.json"; file.version = QString(); - auto errorcode = file.applyTo(m_version); - if(errorcode != VersionFile::NoApplyError) - return false; + file.applyTo(m_version); // QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC")); // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.") } @@ -875,25 +90,13 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte { // version.json QLOG_INFO() << "Reading version.json"; - VersionFile file; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) - { - return false; - } - file.name = "version.json"; + VersionFile file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false); + file.name = "Minecraft"; file.fileId = "org.multimc.version.json"; file.version = m_instance->intendedVersionId(); file.mcVersion = m_instance->intendedVersionId(); - auto error = file.applyTo(m_version); - if (error != VersionFile::NoApplyError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr( - "Error while applying %1. Please check MultiMC-0.log for more info.") - .arg(root.absoluteFilePath("version.json"))); - return false; - } + file.applyTo(m_version); + // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.").arg(root.absoluteFilePath("version.json"))); if (onlyVanilla) break; @@ -906,132 +109,85 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { QLOG_INFO() << "Reading" << info.fileName(); - VersionFile file; - if (!read(info, true, &file)) - { - return false; - } + VersionFile file = parseJsonFile(info, true); if (overrideOrder.contains(file.fileId)) { file.order = overrideOrder.value(file.fileId); } if (files.contains(file.order)) { - QLOG_ERROR() << file.fileId << "has the same order as" << files[file.order].second.fileId; - return false; + throw VersionBuildError(QObject::tr("%1 has the same order as %2").arg(file.fileId, files[file.order].second.fileId)); } files.insert(file.order, qMakePair(info.fileName(), file)); } for (auto order : files.keys()) { QLOG_DEBUG() << "Applying file with order" << order; - auto filePair = files[order]; - auto error = filePair.second.applyTo(m_version); - if (error != VersionFile::NoApplyError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("Error while applying %1. Please check MultiMC-0.log " - "for more info.").arg(filePair.first)); - return false; - } + auto & filePair = files[order]; + filePair.second.applyTo(m_version); } } while(0); // some final touches + finalizeVersion(); +} + +void OneSixVersionBuilder::finalizeVersion() +{ + if (m_version->assets.isEmpty()) { - if (m_version->assets.isEmpty()) + m_version->assets = "legacy"; + } + if (m_version->minecraftArguments.isEmpty()) + { + QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") { - m_version->assets = "legacy"; + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; } - if (m_version->minecraftArguments.isEmpty()) + else if (toCompare == "username_session") { - QString toCompare = m_version->processArguments.toLower(); - if (toCompare == "legacy") - { - m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if (toCompare == "username_session") - { - m_version->minecraftArguments = - "--username ${auth_player_name} --session ${auth_session}"; - } - else if (toCompare == "username_session_version") - { - m_version->minecraftArguments = "--username ${auth_player_name} " - "--session ${auth_session} " - "--version ${profile_name}"; - } + m_version->minecraftArguments = + "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + m_version->minecraftArguments = "--username ${auth_player_name} " + "--session ${auth_session} " + "--version ${profile_name}"; } } - - return true; } -bool OneSixVersionBuilder::read(const QJsonObject &obj) +void OneSixVersionBuilder::readJsonAndApply(const QJsonObject &obj) { m_version->clear(); - bool isError = false; - VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError); - if (isError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("Error while reading. Please check MultiMC-0.log for more info.")); - return false; - } - VersionFile::ApplyError error = file.applyTo(m_version); - if (error == VersionFile::OtherError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); - return false; - } - else if (error == VersionFile::LauncherVersionError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC")); - return false; - } + VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false); + // QObject::tr("Error while reading. Please check MultiMC-0.log for more info.")); - return true; + file.applyTo(m_version); + // QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); + // QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC")); } -bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder, - VersionFile *out, const ParseFlags flags) +VersionFile OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB) { QFile file(fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); - return false; + throw JSONValidationError(QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); if (error.error != QJsonParseError::NoError) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), - QObject::tr("Unable to parse %1: %2 at %3") + throw JSONValidationError(QObject::tr("Unable to parse %1: %2 at %3") .arg(file.fileName(), error.errorString()) .arg(error.offset)); - return false; - } - bool isError = false; - *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError, flags); - if (isError) - { - QMessageBox::critical( - m_widgetParent, QObject::tr("Error"), - QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.") - .arg(file.fileName())); - ; } - return true; + return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB); + // QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.").arg(file.fileName()); } QMap<QString, int> OneSixVersionBuilder::readOverrideOrders(OneSixInstance *instance) @@ -1072,6 +228,7 @@ QMap<QString, int> OneSixVersionBuilder::readOverrideOrders(OneSixInstance *inst } return out; } + bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance) { diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index 8cf6f32f..c48e8ec5 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -17,39 +17,31 @@ #include <QString> #include <QMap> +#include "VersionFile.h" -class OneSixVersion; +class VersionFinal; class OneSixInstance; -class QWidget; class QJsonObject; class QFileInfo; -class VersionFile; class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla, const QStringList &external); - static bool read(OneSixVersion *version, const QJsonObject &obj); + static void build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla, + const QStringList &external); + static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj); + static QMap<QString, int> readOverrideOrders(OneSixInstance *instance); static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance); - enum ParseFlag - { - NoFlags = 0x0, - IsFTBPackJson = 0x1 - }; - Q_DECLARE_FLAGS(ParseFlags, ParseFlag) - private: - OneSixVersion *m_version; + VersionFinal *m_version; OneSixInstance *m_instance; - QWidget *m_widgetParent; - bool build(const bool onlyVanilla, const QStringList &external); - bool read(const QJsonObject &obj); + void buildInternal(const bool onlyVanilla, const QStringList &external); + void readJsonAndApply(const QJsonObject &obj); + void finalizeVersion(); - bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags); + VersionFile parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false); }; - -Q_DECLARE_OPERATORS_FOR_FLAGS(OneSixVersionBuilder::ParseFlags) diff --git a/logic/VersionFile.cpp b/logic/VersionFile.cpp new file mode 100644 index 00000000..b7695907 --- /dev/null +++ b/logic/VersionFile.cpp @@ -0,0 +1,549 @@ +#include <QJsonArray> +#include <QJsonDocument> + +#include <modutils.h> + +#include "logger/QsLog.h" +#include "logic/VersionFile.h" +#include "logic/OneSixLibrary.h" +#include "logic/VersionFinal.h" +#include "MMCJson.h" + +using namespace MMCJson; + +#define CURRENT_MINIMUM_LAUNCHER_VERSION 14 + +RawLibrary RawLibrary::fromJson(const QJsonObject &libObj, + const QString &filename) +{ + RawLibrary out; + if (!libObj.contains("name")) + { + throw JSONValidationError(filename + + "contains a library that doesn't have a 'name' field"); + } + out.name = libObj.value("name").toString(); + + auto readString = [libObj, filename](const QString & key, QString & variable) + { + if (libObj.contains(key)) + { + QJsonValue val = libObj.value(key); + if (!val.isString()) + { + QLOG_WARN() << key << "is not a string in" << filename << "(skipping)"; + } + else + { + variable = val.toString(); + } + } + }; + + readString("url", out.url); + readString("MMC-hint", out.hint); + readString("MMC-absulute_url", out.absoluteUrl); + readString("MMC-absoluteUrl", out.absoluteUrl); + if (libObj.contains("extract")) + { + out.applyExcludes = true; + auto extractObj = ensureObject(libObj.value("extract")); + for (auto excludeVal : ensureArray(extractObj.value("exclude"))) + { + out.excludes.append(ensureString(excludeVal)); + } + } + if (libObj.contains("natives")) + { + out.applyNatives = true; + QJsonObject nativesObj = ensureObject(libObj.value("natives")); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + { + if (!it.value().isString()) + { + QLOG_WARN() << filename << "contains an invalid native (skipping)"; + } + OpSys opSys = OpSys_fromString(it.key()); + if (opSys != Os_Other) + { + out.natives.append(qMakePair(opSys, it.value().toString())); + } + } + } + if (libObj.contains("rules")) + { + out.applyRules = true; + out.rules = rulesFromJsonV4(libObj); + } + return out; +} + +VersionFile VersionFile::fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, const bool isFTB) +{ + VersionFile out; + if (doc.isEmpty() || doc.isNull()) + { + throw JSONValidationError(filename + " is empty or null"); + } + if (!doc.isObject()) + { + throw JSONValidationError("The root of " + filename + " is not an object"); + } + + QJsonObject root = doc.object(); + + if (requireOrder) + { + if (root.contains("order")) + { + out.order = ensureInteger(root.value("order")); + } + else + { + // FIXME: evaluate if we don't want to throw exceptions here instead + QLOG_ERROR() << filename << "doesn't contain an order field"; + } + } + + 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.filename = filename; + + auto readString = [root, filename](const QString & key, QString & variable) + { + if (root.contains(key)) + { + variable = ensureString(root.value(key)); + } + }; + + // FIXME: This should be ignored when applying. + if (!isFTB) + { + readString("id", out.id); + } + + readString("mainClass", out.mainClass); + readString("processArguments", out.processArguments); + readString("minecraftArguments", out.overwriteMinecraftArguments); + readString("+minecraftArguments", out.addMinecraftArguments); + readString("-minecraftArguments", out.removeMinecraftArguments); + readString("type", out.type); + readString("releaseTime", out.releaseTime); + readString("time", out.time); + readString("assets", out.assets); + + if (root.contains("minimumLauncherVersion")) + { + out.minimumLauncherVersion = ensureInteger(root.value("minimumLauncherVersion")); + } + + if (root.contains("tweakers")) + { + out.shouldOverwriteTweakers = true; + for (auto tweakerVal : ensureArray(root.value("tweakers"))) + { + out.overwriteTweakers.append(ensureString(tweakerVal)); + } + } + + if (root.contains("+tweakers")) + { + for (auto tweakerVal : ensureArray(root.value("+tweakers"))) + { + out.addTweakers.append(ensureString(tweakerVal)); + } + } + + if (root.contains("-tweakers")) + { + for (auto tweakerVal : ensureArray(root.value("-tweakers"))) + { + out.removeTweakers.append(ensureString(tweakerVal)); + } + } + + if (root.contains("libraries")) + { + // FIXME: This should be done when applying. + out.shouldOverwriteLibs = !isFTB; + for (auto libVal : ensureArray(root.value("libraries"))) + { + auto libObj = ensureObject(libVal); + + RawLibrary lib = RawLibrary::fromJson(libObj, filename); + // FIXME: This should be done when applying. + if (isFTB) + { + lib.hint = "local"; + lib.insertType = RawLibrary::Prepend; + out.addLibs.prepend(lib); + } + else + { + out.overwriteLibs.append(lib); + } + } + } + + if (root.contains("+libraries")) + { + for (auto libVal : ensureArray(root.value("+libraries"))) + { + QJsonObject libObj = ensureObject(libVal); + QJsonValue insertVal = ensureExists(libObj.value("insert")); + + // parse the library + RawLibrary lib = RawLibrary::fromJson(libObj, filename); + + // TODO: utility functions for handling this case. templates? + QString insertString; + { + if (insertVal.isString()) + { + insertString = insertVal.toString(); + } + else if (insertVal.isObject()) + { + QJsonObject insertObj = insertVal.toObject(); + if (insertObj.isEmpty()) + { + throw JSONValidationError("One library has an empty insert object in " + + filename); + } + insertString = insertObj.keys().first(); + lib.insertData = insertObj.value(insertString).toString(); + } + } + if (insertString == "apply") + { + lib.insertType = RawLibrary::Apply; + } + else if (insertString == "prepend") + { + lib.insertType = RawLibrary::Prepend; + } + else if (insertString == "append") + { + lib.insertType = RawLibrary::Prepend; + } + else if (insertString == "replace") + { + lib.insertType = RawLibrary::Replace; + } + else + { + throw JSONValidationError("A '+' library in " + filename + + " contains an invalid insert type"); + } + if (libObj.contains("MMC-depend")) + { + const QString dependString = ensureString(libObj.value("MMC-depend")); + if (dependString == "hard") + { + lib.dependType = RawLibrary::Hard; + } + else if (dependString == "soft") + { + lib.dependType = RawLibrary::Soft; + } + else + { + throw JSONValidationError("A '+' library in " + filename + + " contains an invalid depend type"); + } + } + out.addLibs.append(lib); + } + } + if (root.contains("-libraries")) + { + for (auto libVal : ensureArray(root.value("-libraries"))) + { + auto libObj = ensureObject(libVal); + out.removeLibs.append(ensureString(libObj.value("name"))); + } + } + return out; +} + +std::shared_ptr<OneSixLibrary> VersionFile::createLibrary(const RawLibrary &lib) +{ + std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib.name)); + if (!lib.url.isEmpty()) + { + out->setBaseUrl(lib.url); + } + out->setHint(lib.hint); + if (!lib.absoluteUrl.isEmpty()) + { + out->setAbsoluteUrl(lib.absoluteUrl); + } + out->setAbsoluteUrl(lib.absoluteUrl); + out->extract_excludes = lib.excludes; + for (auto native : lib.natives) + { + out->addNative(native.first, native.second); + } + out->setRules(lib.rules); + out->finalize(); + return out; +} + +int VersionFile::findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, + const QString &needle) +{ + for (int i = 0; i < haystack.size(); ++i) + { + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix) + .indexIn(haystack.at(i)->rawName()) != -1) + { + return i; + } + } + return -1; +} + +void VersionFile::applyTo(VersionFinal *version) +{ + if (minimumLauncherVersion != -1) + { + if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION) + { + throw VersionBuildError( + QString("%1 is for a different launcher version (%2), current supported is %3") + .arg(filename, minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION)); + } + } + + if (!version->id.isNull() && !mcVersion.isNull()) + { + if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) == + -1) + { + throw VersionBuildError( + QString("%1 is for a different version of Minecraft").arg(filename)); + } + } + + if (!id.isNull()) + { + version->id = id; + } + if (!mainClass.isNull()) + { + version->mainClass = mainClass; + } + if (!processArguments.isNull()) + { + version->processArguments = processArguments; + } + if (!type.isNull()) + { + version->type = type; + } + if (!releaseTime.isNull()) + { + version->releaseTime = releaseTime; + } + if (!time.isNull()) + { + version->time = time; + } + if (!assets.isNull()) + { + version->assets = assets; + } + if (minimumLauncherVersion >= 0) + { + version->minimumLauncherVersion = minimumLauncherVersion; + } + if (!overwriteMinecraftArguments.isNull()) + { + version->minecraftArguments = overwriteMinecraftArguments; + } + if (!addMinecraftArguments.isNull()) + { + version->minecraftArguments += addMinecraftArguments; + } + if (!removeMinecraftArguments.isNull()) + { + version->minecraftArguments.remove(removeMinecraftArguments); + } + if (shouldOverwriteTweakers) + { + version->tweakers = overwriteTweakers; + } + for (auto tweaker : addTweakers) + { + version->tweakers += tweaker; + } + for (auto tweaker : removeTweakers) + { + version->tweakers.removeAll(tweaker); + } + if (shouldOverwriteLibs) + { + version->libraries.clear(); + for (auto lib : overwriteLibs) + { + version->libraries.append(createLibrary(lib)); + } + } + for (auto lib : addLibs) + { + switch (lib.insertType) + { + case RawLibrary::Apply: + { + + int index = findLibrary(version->libraries, lib.name); + if (index >= 0) + { + auto library = version->libraries[index]; + if (!lib.url.isNull()) + { + library->setBaseUrl(lib.url); + } + if (!lib.hint.isNull()) + { + library->setHint(lib.hint); + } + if (!lib.absoluteUrl.isNull()) + { + library->setAbsoluteUrl(lib.absoluteUrl); + } + if (lib.applyExcludes) + { + library->extract_excludes = lib.excludes; + } + if (lib.applyNatives) + { + library->clearSuffixes(); + for (auto native : lib.natives) + { + library->addNative(native.first, native.second); + } + } + if (lib.applyRules) + { + library->setRules(lib.rules); + } + library->finalize(); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.name << "(skipping)"; + } + break; + } + case RawLibrary::Append: + case RawLibrary::Prepend: + { + + const int startOfVersion = lib.name.lastIndexOf(':') + 1; + const int index = findLibrary( + version->libraries, QString(lib.name).replace(startOfVersion, INT_MAX, '*')); + if (index < 0) + { + if (lib.insertType == RawLibrary::Append) + { + version->libraries.append(createLibrary(lib)); + } + else + { + version->libraries.prepend(createLibrary(lib)); + } + } + else + { + auto otherLib = version->libraries.at(index); + const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX); + const Util::Version otherVersion = otherLib->version(); + // if the existing version is a hard dependency we can either use it or + // fail, but we can't change it + if (otherLib->dependType == OneSixLibrary::Hard) + { + // we need a higher version, or we're hard to and the versions aren't + // equal + if (ourVersion > otherVersion || + (lib.dependType == RawLibrary::Hard && ourVersion != otherVersion)) + { + throw VersionBuildError( + QString( + "Error resolving library dependencies between %1 and %2 in %3.") + .arg(otherLib->rawName(), lib.name, filename)); + } + else + { + // the library is already existing, so we don't have to do anything + } + } + else if (otherLib->dependType == OneSixLibrary::Soft) + { + // if we are higher it means we should update + if (ourVersion > otherVersion) + { + auto library = createLibrary(lib); + if (Util::Version(otherLib->minVersion) < ourVersion) + { + library->minVersion = ourVersion.toString(); + } + version->libraries.replace(index, library); + } + else + { + // our version is smaller than the existing version, but we require + // it: fail + if (lib.dependType == RawLibrary::Hard) + { + throw VersionBuildError(QString( + "Error resolving library dependencies between %1 and %2 in %3.") + .arg(otherLib->rawName(), lib.name, + filename)); + } + } + } + } + break; + } + case RawLibrary::Replace: + { + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.replace(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; + } + } + } + for (auto lib : removeLibs) + { + int index = findLibrary(version->libraries, lib); + if (index >= 0) + { + version->libraries.removeAt(index); + } + else + { + QLOG_WARN() << "Couldn't find" << lib << "(skipping)"; + } + } + + VersionFinal::VersionFile versionFile; + versionFile.name = name; + versionFile.id = fileId; + versionFile.version = this->version; + versionFile.mcVersion = mcVersion; + versionFile.filename = filename; + versionFile.order = order; + version->versionFiles.append(versionFile); +} diff --git a/logic/VersionFile.h b/logic/VersionFile.h new file mode 100644 index 00000000..0475f927 --- /dev/null +++ b/logic/VersionFile.h @@ -0,0 +1,97 @@ +#pragma once + +#include <QString> +#include <QStringList> +#include <memory> +#include "logic/OpSys.h" +#include "logic/OneSixRule.h" +#include "MMCError.h" + +class VersionFinal; + +class VersionBuildError : public MMCError +{ +public: + VersionBuildError(QString cause) : MMCError(cause) {}; + virtual QString errorName() + { + return "VersionBuildError"; + }; + virtual ~VersionBuildError() {}; +}; + +struct RawLibrary +{ + QString name; + QString url; + QString hint; + QString absoluteUrl; + bool applyExcludes = false; + QStringList excludes; + bool applyNatives = false; + QList<QPair<OpSys, QString>> natives; + bool applyRules = false; + QList<std::shared_ptr<Rule>> rules; + + // user for '+' libraries + enum InsertType + { + Apply, + Append, + Prepend, + Replace + }; + InsertType insertType = Append; + QString insertData; + enum DependType + { + Soft, + Hard + }; + DependType dependType = Soft; + + static RawLibrary fromJson(const QJsonObject &libObj, const QString &filename); +}; + +struct VersionFile +{ +public: /* methods */ + static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, const bool isFTB = false); + + static std::shared_ptr<OneSixLibrary> createLibrary(const RawLibrary &lib); + int findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, const QString &needle); + void applyTo(VersionFinal *version); + +public: /* data */ + int order = 0; + QString name; + QString fileId; + QString version; + // TODO use the mcVersion to determine if a version file should be removed on update + QString mcVersion; + QString filename; + // TODO requirements + // QMap<QString, QString> requirements; + QString id; + QString mainClass; + QString overwriteMinecraftArguments; + QString addMinecraftArguments; + QString removeMinecraftArguments; + QString processArguments; + QString type; + QString releaseTime; + QString time; + QString assets; + int minimumLauncherVersion = -1; + + bool shouldOverwriteTweakers = false; + QStringList overwriteTweakers; + QStringList addTweakers; + QStringList removeTweakers; + + bool shouldOverwriteLibs = false; + QList<RawLibrary> overwriteLibs; + QList<RawLibrary> addLibs; + QList<QString> removeLibs; +};
\ No newline at end of file diff --git a/logic/OneSixVersion.cpp b/logic/VersionFinal.cpp index 06e748bd..ec450eda 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/VersionFinal.cpp @@ -13,28 +13,28 @@ * limitations under the License. */ -#include "OneSixVersion.h" +#include "VersionFinal.h" #include <QDebug> #include <QFile> #include "OneSixVersionBuilder.h" -OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) +VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent) : QAbstractListModel(parent), m_instance(instance) { clear(); } -bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla, const QStringList &external) +bool VersionFinal::reload(const bool onlyVanilla, const QStringList &external) { + //FIXME: source of epic failure. beginResetModel(); - bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla, external); + OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external); endResetModel(); - return ret; } -void OneSixVersion::clear() +void VersionFinal::clear() { beginResetModel(); id.clear(); @@ -52,9 +52,9 @@ void OneSixVersion::clear() endResetModel(); } -void OneSixVersion::dump() const +void VersionFinal::dump() const { - qDebug().nospace() << "OneSixVersion(" + qDebug().nospace() << "VersionFinal(" << "\n\tid=" << id << "\n\ttime=" << time << "\n\treleaseTime=" << releaseTime @@ -72,7 +72,7 @@ void OneSixVersion::dump() const qDebug().nospace() << "\n)"; } -bool OneSixVersion::canRemove(const int index) const +bool VersionFinal::canRemove(const int index) const { if (index < versionFiles.size()) { @@ -81,7 +81,7 @@ bool OneSixVersion::canRemove(const int index) const return false; } -QString OneSixVersion::versionFileId(const int index) const +QString VersionFinal::versionFileId(const int index) const { if (index < 0 || index >= versionFiles.size()) { @@ -90,7 +90,7 @@ QString OneSixVersion::versionFileId(const int index) const return versionFiles.at(index).id; } -bool OneSixVersion::remove(const int index) +bool VersionFinal::remove(const int index) { if (canRemove(index)) { @@ -99,7 +99,7 @@ bool OneSixVersion::remove(const int index) return false; } -QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs() +QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs() { QList<std::shared_ptr<OneSixLibrary> > output; for (auto lib : libraries) @@ -112,7 +112,7 @@ QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs() return output; } -QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs() +QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs() { QList<std::shared_ptr<OneSixLibrary> > output; for (auto lib : libraries) @@ -125,17 +125,21 @@ QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs() return output; } -std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj) +std::shared_ptr<VersionFinal> VersionFinal::fromJson(const QJsonObject &obj) { - std::shared_ptr<OneSixVersion> version(new OneSixVersion(0)); - if (OneSixVersionBuilder::read(version.get(), obj)) + std::shared_ptr<VersionFinal> version(new VersionFinal(0)); + try { - return version; + OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj); } - return 0; + catch(MMCError err) + { + return 0; + } + return version; } -QVariant OneSixVersion::data(const QModelIndex &index, int role) const +QVariant VersionFinal::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -161,7 +165,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const return QVariant(); } -QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const +QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { @@ -181,24 +185,24 @@ QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int return QVariant(); } -Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const +Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } -int OneSixVersion::rowCount(const QModelIndex &parent) const +int VersionFinal::rowCount(const QModelIndex &parent) const { return versionFiles.size(); } -int OneSixVersion::columnCount(const QModelIndex &parent) const +int VersionFinal::columnCount(const QModelIndex &parent) const { return 2; } -QDebug operator<<(QDebug &dbg, const OneSixVersion *version) +QDebug operator<<(QDebug &dbg, const VersionFinal *version) { version->dump(); return dbg.maybeSpace(); diff --git a/logic/OneSixVersion.h b/logic/VersionFinal.h index fee47fa3..e5a38423 100644 --- a/logic/OneSixVersion.h +++ b/logic/VersionFinal.h @@ -25,11 +25,11 @@ class OneSixInstance; -class OneSixVersion : public QAbstractListModel +class VersionFinal : public QAbstractListModel { Q_OBJECT public: - explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0); + explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; @@ -37,7 +37,7 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - bool reload(QWidget *widgetParent, const bool onlyVanilla = false, const QStringList &external = QStringList()); + bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList()); void clear(); void dump() const; @@ -54,7 +54,7 @@ public: QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs(); QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs(); - static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj); + static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj); // data members public: @@ -133,5 +133,5 @@ private: OneSixInstance *m_instance; }; -QDebug operator<<(QDebug &dbg, const OneSixVersion *version); +QDebug operator<<(QDebug &dbg, const VersionFinal *version); QDebug operator<<(QDebug &dbg, const OneSixLibrary *library); diff --git a/logic/lists/LiteLoaderVersionList.cpp b/logic/lists/LiteLoaderVersionList.cpp index b8cea442..ef95eefd 100644 --- a/logic/lists/LiteLoaderVersionList.cpp +++ b/logic/lists/LiteLoaderVersionList.cpp @@ -92,7 +92,6 @@ void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions) LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist) { m_list = vlist; - vlistReply = nullptr; } LLListLoadTask::~LLListLoadTask() @@ -102,23 +101,49 @@ LLListLoadTask::~LLListLoadTask() void LLListLoadTask::executeTask() { setStatus(tr("Loading LiteLoader version list...")); - auto worker = MMC->qnam(); - vlistReply = worker->get(QNetworkRequest(QUrl(URLConstants::LITELOADER_URL))); - connect(vlistReply, SIGNAL(finished()), this, SLOT(listDownloaded())); + auto job = new NetJob("Version index"); + // we do not care if the version is stale or not. + auto liteloaderEntry = MMC->metacache()->resolveEntry("liteloader", "versions.json"); + + // verify by poking the server. + liteloaderEntry->stale = true; + + job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL), + liteloaderEntry)); + + connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed())); + + listJob.reset(job); + connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded())); + connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); + listJob->start(); +} + +void LLListLoadTask::listFailed() +{ + emitFailed("Failed to load LiteLoader version list."); + return; } void LLListLoadTask::listDownloaded() { - if (vlistReply->error() != QNetworkReply::NoError) + QByteArray data; { - vlistReply->deleteLater(); - emitFailed("Failed to load LiteLoader version list" + vlistReply->errorString()); - return; + auto dlJob = listDownload; + auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath(); + QFile listFile(filename); + if (!listFile.open(QIODevice::ReadOnly)) + { + emitFailed("Failed to open the LiteLoader version list."); + return; + } + data = listFile.readAll(); + listFile.close(); + dlJob.reset(); } QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError); - vlistReply->deleteLater(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { @@ -140,7 +165,12 @@ void LLListLoadTask::listDownloaded() emitFailed("Error parsing version list JSON: missing 'versions' object"); return; } - const QJsonObject versions = root.value("versions").toObject(); + + auto meta = root.value("meta").toObject(); + QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics.")); + QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com"); + QString authors = meta.value("authors").toString("Mumfrey"); + auto versions = root.value("versions").toObject(); QList<BaseVersionPtr> tempList; for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt) @@ -170,6 +200,9 @@ void LLListLoadTask::listDownloaded() version->md5 = artefact.value("md5").toString(); version->timestamp = artefact.value("timestamp").toDouble(); version->tweakClass = artefact.value("tweakClass").toString(); + version->authors = authors; + version->description = description; + version->defaultUrl = defaultUrl; const QJsonArray libs = artefact.value("libraries").toArray(); for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt) { diff --git a/logic/lists/LiteLoaderVersionList.h b/logic/lists/LiteLoaderVersionList.h index 8f761caf..bfc913e5 100644 --- a/logic/lists/LiteLoaderVersionList.h +++ b/logic/lists/LiteLoaderVersionList.h @@ -22,6 +22,7 @@ #include "BaseVersionList.h" #include "logic/tasks/Task.h" #include "logic/BaseVersion.h" +#include <logic/net/NetJob.h> class LLListLoadTask; class QNetworkReply; @@ -46,6 +47,7 @@ public: return version; } + // important info QString version; QString file; QString mcVersion; @@ -54,6 +56,11 @@ public: bool isLatest; QString tweakClass; QStringList libraries; + + // meta + QString defaultUrl; + QString description; + QString authors; }; typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr; @@ -96,8 +103,10 @@ public: protected slots: void listDownloaded(); + void listFailed(); protected: - QNetworkReply *vlistReply; + NetJobPtr listJob; + CacheDownloadPtr listDownload; LiteLoaderVersionList *m_list; }; |