From f9ea3dbfdea0db5626a6afadcfd599a5f53d8abd Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Mon, 27 Jan 2014 19:20:07 +0100 Subject: Split parsing/applying. Better error logging. Fix crash. --- logic/ForgeInstaller.cpp | 49 +- logic/OneSixFTBInstance.cpp | 2 +- logic/OneSixInstance.cpp | 30 +- logic/OneSixInstance.h | 8 +- logic/OneSixInstance_p.h | 1 + logic/OneSixLibrary.cpp | 14 +- logic/OneSixLibrary.h | 2 + logic/OneSixUpdate.cpp | 4 +- logic/OneSixVersion.cpp | 5 +- logic/OneSixVersion.h | 6 +- logic/OneSixVersionBuilder.cpp | 1002 ++++++++++++++++++++++++++++------------ logic/OneSixVersionBuilder.h | 18 +- 12 files changed, 814 insertions(+), 327 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 863c3dfd..c0340e95 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "MultiMC.h" #include "OneSixInstance.h" @@ -146,36 +148,71 @@ bool ForgeInstaller::add(OneSixInstance *to) QJsonObject libObj = lib->toJson(); bool found = false; + bool equals = false; // find an entry that matches this one - for (auto tolib : to->getFullVersion()->libraries) + for (auto tolib : to->getNonCustomVersion()->libraries) { if (tolib->name() != libName) continue; found = true; + if (tolib->toJson() == libObj) + { + equals = true; + } // replace lib libObj.insert("insert", QString("apply")); break; } + if (equals) + { + continue; + } if (!found) { // add lib QJsonObject insertObj; - insertObj.insert("before", to->getFullVersion()->libraries.at(sliding_insert_window)->rawName()); + insertObj.insert( + "before", + to->getFullVersion()->libraries.at(sliding_insert_window + 1)->rawName()); libObj.insert("insert", insertObj); sliding_insert_window++; } - librariesPlus.append(libObj); + librariesPlus.prepend(libObj); } obj.insert("+libraries", librariesPlus); obj.insert("mainClass", m_forge_version->mainClass); - obj.insert("minecraftArguments", m_forge_version->minecraftArguments); - obj.insert("processArguments", m_forge_version->processArguments); + QString args = m_forge_version->minecraftArguments; + QStringList tweakers; + { + QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)"); + QRegularExpressionMatch match = expression.match(args); + while (match.hasMatch()) + { + tweakers.append(match.captured(1)); + args.remove(match.capturedStart(), match.capturedLength()); + match = expression.match(args); + } + } + if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments) + { + obj.insert("minecraftArguments", args); + } + if (!tweakers.isEmpty()) + { + obj.insert("+tweakers", QJsonArray::fromStringList(tweakers)); + } + if (!m_forge_version->processArguments.isEmpty() && + m_forge_version->processArguments != to->getNonCustomVersion()->processArguments) + { + obj.insert("processArguments", m_forge_version->processArguments); + } } QFile file(filename(to->instanceRoot())); if (!file.open(QFile::WriteOnly)) { - QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString(); + QLOG_ERROR() << "Error opening" << file.fileName() + << "for reading:" << file.errorString(); return false; } file.write(QJsonDocument(obj).toJson()); diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 71bc1470..54bfbe2b 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -55,7 +55,7 @@ slots: setStatus(tr("Installing Forge...")); QString forgePath = entry->getFullPath(); ForgeInstaller forge(forgePath, forgeVersion->universal_url); - if (!instance->reloadFullVersion()) + if (!instance->reloadVersion()) { emitFailed(tr("Couldn't load the version config")); return; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index c43a5187..19d5e112 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -35,13 +35,14 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, d->m_settings->registerSetting("IntendedVersion", ""); d->m_settings->registerSetting("ShouldUpdate", false); d->version.reset(new OneSixVersion(this, this)); + d->nonCustomVersion.reset(new OneSixVersion(this, this)); if (QDir(instanceRoot()).exists("version.json")) { - reloadFullVersion(); + reloadVersion(); } else { - clearFullVersion(); + clearVersion(); } } @@ -139,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; + for (auto tweaker : version->tweakers) + { + args_pattern += " --tweakClass " + tweaker; + } QMap token_mapping; // yggdrasil! @@ -287,7 +292,7 @@ bool OneSixInstance::setIntendedVersionId(QString version) settings().set("IntendedVersion", version); setShouldUpdate(true); QFile::remove(PathCombine(instanceRoot(), "version.json")); - clearFullVersion(); + clearVersion(); return true; } @@ -323,28 +328,39 @@ QString OneSixInstance::currentVersionId() const return intendedVersionId(); } -bool OneSixInstance::reloadFullVersion(QWidget *widgetParent) +bool OneSixInstance::reloadVersion(QWidget *widgetParent) { I_D(OneSixInstance); bool ret = d->version->reload(widgetParent); + if (ret) + { + ret = d->nonCustomVersion->reload(widgetParent, true); + } emit versionReloaded(); return ret; } -void OneSixInstance::clearFullVersion() +void OneSixInstance::clearVersion() { I_D(OneSixInstance); d->version->clear(); + d->nonCustomVersion->clear(); emit versionReloaded(); } -std::shared_ptr OneSixInstance::getFullVersion() +std::shared_ptr OneSixInstance::getFullVersion() const { - I_D(OneSixInstance); + I_D(const OneSixInstance); return d->version; } +std::shared_ptr OneSixInstance::getNonCustomVersion() const +{ + I_D(const OneSixInstance); + return d->nonCustomVersion; +} + QString OneSixInstance::defaultBaseJar() const { return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar"; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 94577828..ee64e886 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -52,11 +52,13 @@ public: virtual QDialog *createModEditDialog(QWidget *parent) override; /// reload the full version json files. return true on success! - bool reloadFullVersion(QWidget *widgetParent = 0); + bool reloadVersion(QWidget *widgetParent = 0); /// clears all version information in preparation for an update - void clearFullVersion(); + void clearVersion(); /// get the current full version info - std::shared_ptr getFullVersion(); + std::shared_ptr getFullVersion() const; + /// gets the current version info, excluding custom.json + std::shared_ptr getNonCustomVersion() const; /// is the current version original, or custom? virtual bool versionIsCustom() override; diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index a2b70ac9..6dc74e50 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -22,6 +22,7 @@ struct OneSixInstancePrivate : public BaseInstancePrivate { std::shared_ptr version; + std::shared_ptr nonCustomVersion; std::shared_ptr loader_mod_list; std::shared_ptr resource_pack_list; }; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 4d892d53..2b1f0600 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -93,6 +93,10 @@ void OneSixLibrary::addNative(OpSys os, const QString &suffix) m_is_native = true; m_native_suffixes[os] = suffix; } +void OneSixLibrary::clearSuffixes() +{ + m_native_suffixes.clear(); +} void OneSixLibrary::setRules(QList> rules) { m_rules = rules; @@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "32"); QString origin = PathCombine("libraries", cooked_storage); QString target_dir_cooked = PathCombine(target_dir, "32"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir) cooked_storage.replace("${arch}", "64"); origin = PathCombine("libraries", cooked_storage); target_dir_cooked = PathCombine(target_dir, "64"); - if(!ensureFolderPathExists(target_dir_cooked)) + if (!ensureFolderPathExists(target_dir_cooked)) { QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked; return false; @@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir) } else { - if(!ensureFolderPathExists(target_dir)) + if (!ensureFolderPathExists(target_dir)) { QLOG_ERROR() << "Couldn't create folder " + target_dir; return false; @@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson() libRoot.insert("MMC-hint", m_hint); if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && - m_base_url != "https://" + URLConstants::LIBRARY_BASE) + m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty()) + { libRoot.insert("url", m_base_url); + } if (isNative() && m_native_suffixes.size()) { QJsonObject nativeList; diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 52f89969..5384015a 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -107,6 +107,8 @@ public: void setIsNative(); /// Attach a name suffix to the specified OS native void addNative(OpSys os, const QString &suffix); + /// Clears all suffixes + void clearSuffixes(); /// Set the load rules void setRules(QList> rules); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index f51b6c23..c056bc4c 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -137,7 +137,7 @@ void OneSixUpdate::versionFileFinished() { finfo.remove(); } - inst->reloadFullVersion(); + inst->reloadVersion(); jarlibStart(); } @@ -235,7 +235,7 @@ void OneSixUpdate::jarlibStart() setStatus(tr("Getting the library files from Mojang...")); QLOG_INFO() << m_inst->name() << ": downloading libraries"; OneSixInstance *inst = (OneSixInstance *)m_inst; - bool successful = inst->reloadFullVersion(); + bool successful = inst->reloadVersion(); if (!successful) { emitFailed("Failed to load the version description file. It might be " diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 3db3bd77..1b6bc9cb 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -25,9 +25,9 @@ OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent) clear(); } -bool OneSixVersion::reload(QWidget *widgetParent) +bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom) { - return OneSixVersionBuilder::build(this, m_instance, widgetParent); + return OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom); } void OneSixVersion::clear() @@ -42,6 +42,7 @@ void OneSixVersion::clear() minimumLauncherVersion = 0xDEADBEAF; mainClass.clear(); libraries.clear(); + tweakers.clear(); } void OneSixVersion::dump() const diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 00afd010..767e05db 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -38,7 +38,7 @@ public: virtual int columnCount(const QModelIndex &parent) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; - bool reload(QWidget *widgetParent); + bool reload(QWidget *widgetParent, const bool excludeCustom = false); void clear(); void dump() const; @@ -78,6 +78,10 @@ public: * writing) */ int minimumLauncherVersion = 0xDEADBEEF; + /** + * A list of all tweaker classes + */ + QStringList tweakers; /** * The main class to load first */ diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp index 85f55a52..c031a94a 100644 --- a/logic/OneSixVersionBuilder.cpp +++ b/logic/OneSixVersionBuilder.cpp @@ -31,416 +31,844 @@ #include "OneSixRule.h" #include "logger/QsLog.h" -OneSixVersionBuilder::OneSixVersionBuilder() -{ - -} - -bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent) +struct VersionFile { - OneSixVersionBuilder builder; - builder.m_version = version; - builder.m_instance = instance; - builder.m_widgetParent = widgetParent; - return builder.build(); -} - -bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) -{ - OneSixVersionBuilder builder; - builder.m_version = version; - builder.m_instance = 0; - builder.m_widgetParent = 0; - return builder.read(obj); -} - -bool OneSixVersionBuilder::build() -{ - m_version->clear(); - - QDir root(m_instance->instanceRoot()); - QDir patches(root.absoluteFilePath("patches/")); - - // version.json -> patches/*.json -> custom.json + int order; + 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> natives; + bool applyRules = false; + QList> rules; - // version.json + // user for '+' libraries + enum InsertType + { + Apply, + Append, + Prepend, + InsertBefore, + InsertAfter, + Replace + }; + InsertType insertType; + QString insertData; + }; + bool shouldOverwriteLibs = false; + QList overwriteLibs; + QList addLibs; + QList removeLibs; + + static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename, + bool &isError) { - QLOG_INFO() << "Reading version.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("version.json")), &obj)) + isError = true; + Library out; + if (!libObj.contains("name")) { - return false; + QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field"; + return out; } - if (!apply(obj)) - { - return false; - } - } + out.name = libObj.value("name").toString(); - // patches/ - { - // load all, put into map for ordering, apply in the right order + 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(); + } + } + }; - QMap objects; - for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) + readString("url", out.url); + readString("MMC-hint", out.hint); + readString("MMC-absulute_url", out.absoluteUrl); + readString("MMC-absoluteUrl", out.absoluteUrl); + if (libObj.contains("extract")) { - QLOG_INFO() << "Reading" << info.fileName(); - QJsonObject obj; - if (!read(info, &obj)) + if (!libObj.value("extract").isObject()) { - return false; + QLOG_ERROR() + << filename + << "contains a library with an 'extract' field that's not an object"; + return out; } - if (!obj.contains("order") || !obj.value("order").isDouble()) + QJsonObject extractObj = libObj.value("extract").toObject(); + if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray()) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Missing or invalid 'order' in %1").arg(info.absoluteFilePath())); - return false; + QLOG_ERROR() << filename + << "contains a library with an invalid 'extract' field"; + return out; } - objects.insert(obj.value("order").toDouble(), obj); - } - for (auto object : objects.values()) - { - qDebug() << "Applying object with order" << objects.key(object); - if (!apply(object)) + out.applyExcludes = true; + QJsonArray excludeArray = extractObj.value("exclude").toArray(); + for (auto excludeVal : excludeArray) { - return false; + 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()); + } } } - } - - // custom.json - { - if (QFile::exists(root.absoluteFilePath("custom.json"))) + if (libObj.contains("natives")) { - QLOG_INFO() << "Reading custom.json"; - QJsonObject obj; - if (!read(QFileInfo(root.absoluteFilePath("custom.json")), &obj)) + if (!libObj.value("natives").isObject()) { - return false; + QLOG_ERROR() + << filename + << "contains a library with a 'natives' field that's not an object"; + return out; } - if (!apply(obj)) + out.applyNatives = true; + QJsonObject nativesObj = libObj.value("natives").toObject(); + for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) { - return false; + 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; } - - return true; -} - -bool OneSixVersionBuilder::read(const QJsonObject &obj) -{ - m_version->clear(); - - return apply(obj); -} - -void applyString(const QJsonObject &obj, const QString &key, QString &out, const bool onlyOverride = true) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - out = obj.value(key).toString(); - } - else if (!onlyOverride) + static VersionFile fromJson(const QJsonDocument &doc, const QString &filename, + const bool requireOrder, bool &isError) { - if (obj.contains("+" + key) && obj.value("+" + key).isString()) + VersionFile out; + isError = true; + if (doc.isEmpty() || doc.isNull()) { - out += obj.value("+" + key).toString(); + QLOG_ERROR() << filename << "is empty or null"; + return out; } - else if (obj.contains("-" + key) && obj.value("-" + key).isString()) + if (!doc.isObject()) { - out.remove(obj.value("-" + key).toString()); + QLOG_ERROR() << "The root of" << filename << "is not an object"; + return out; } - } -} -void applyString(const QJsonObject &obj, const QString &key, std::shared_ptr lib, void(OneSixLibrary::*func)(const QString &val)) -{ - if (obj.contains(key) && obj.value(key).isString()) - { - (lib.get()->*func)(obj.value(key).toString()); - } -} -bool OneSixVersionBuilder::apply(const QJsonObject &object) -{ - applyString(object, "id", m_version->id); - applyString(object, "mainClass", m_version->mainClass); - applyString(object, "minecraftArguments", m_version->minecraftArguments, false); - applyString(object, "processArguments", m_version->processArguments, false); - if (m_version->minecraftArguments.isEmpty()) - { - const QString toCompare = m_version->processArguments.toLower(); - if (toCompare == "legacy") + + QJsonObject root = doc.object(); + + if (requireOrder) { - m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; + 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"; + } } - else if (toCompare == "username_session") + + 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(); + } + } + }; + + 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")) { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; + QJsonValue val = root.value("minimumLauncherVersion"); + if (!val.isDouble()) + { + QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename + << "(skipping)"; + } + else + { + out.minimumLauncherVersion = val.toDouble(); + } } - else if (toCompare == "username_session_version") + + if (root.contains("tweakers")) { - m_version->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; + 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()); + } } - } - applyString(object, "type", m_version->type); - applyString(object, "releaseTime", m_version->releaseTime); - applyString(object, "time", m_version->time); - applyString(object, "assets", m_version->assets); - { - if (m_version->assets.isEmpty()) + if (root.contains("+tweakers")) { - m_version->assets = "legacy"; + 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 (object.contains("minimumLauncherVersion")) - { - auto minLauncherVersionVal = object.value("minimumLauncherVersion"); - if (minLauncherVersionVal.isDouble()) + if (root.contains("-tweakers")) { - m_version->minimumLauncherVersion = minLauncherVersionVal.toDouble(); + 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()); + } } - } - // libraries - if (object.contains("libraries")) - { - m_version->libraries.clear(); - auto librariesValue = object.value("libraries"); - if (!librariesValue.isArray()) + if (root.contains("libraries")) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + out.shouldOverwriteLibs = true; + 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; + } + out.overwriteLibs.append(lib); + } } - auto array = librariesValue.toArray(); - for (auto libVal : array) + if (root.contains("+libraries")) { - if (libVal.isObject()) + QJsonValue librariesVal = root.value("+libraries"); + if (!librariesVal.isArray()) { - if (!applyLibrary(libVal.toObject(), Override)) + 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")) { - return false; + 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 == "append") + { + lib.insertType = Library::Append; + } + else if (insertString == "prepend") + { + lib.insertType = Library::Prepend; + } + else if (insertString == "before") + { + lib.insertType = Library::InsertBefore; + } + else if (insertString == "after") + { + lib.insertType = Library::InsertAfter; + } + else if (insertString == "replace") + { + lib.insertType = Library::Replace; + } + else + { + QLOG_ERROR() << "A '+' library in" << filename + << "contains an invalid insert 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; } - // +libraries - if (object.contains("+libraries")) + static std::shared_ptr createLibrary(const Library &lib) { - auto librariesValue = object.value("+libraries"); - if (!librariesValue.isArray()) + std::shared_ptr out(new OneSixLibrary(lib.name)); + out->setBaseUrl(lib.url); + out->setHint(lib.hint); + out->setAbsoluteUrl(lib.absoluteUrl); + out->extract_excludes = lib.excludes; + for (auto native : lib.natives) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + out->addNative(native.first, native.second); } - for (auto libVal : librariesValue.toArray()) + out->setRules(lib.rules); + out->finalize(); + return out; + } + int findLibrary(QList> haystack, const QString &needle) + { + for (int i = 0; i < haystack.size(); ++i) { - if (libVal.isObject()) + if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix) + .indexIn(haystack.at(i)->rawName()) != -1) { - applyLibrary(libVal.toObject(), Add); + return i; } - } + return -1; } - - // -libraries - if (object.contains("-libraries")) + void applyTo(OneSixVersion *version, bool &isError) { - auto librariesValue = object.value("-libraries"); - if (!librariesValue.isArray()) + isError = true; + if (!id.isNull()) { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("One json files contains a libraries field, but it's not an array")); - return false; + 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; } - for (auto libVal : librariesValue.toArray()) + if (!removeMinecraftArguments.isNull()) { - if (libVal.isObject()) + 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: + version->libraries.append(createLibrary(lib)); + break; + case Library::Prepend: + version->libraries.prepend(createLibrary(lib)); + break; + case Library::InsertBefore: { - applyLibrary(libVal.toObject(), Remove); + + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + break; } + case Library::InsertAfter: + { + int index = findLibrary(version->libraries, lib.insertData); + if (index >= 0) + { + version->libraries.insert(index + 1, createLibrary(lib)); + } + else + { + QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)"; + } + 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)"; + } } + + isError = false; } +}; - return true; +OneSixVersionBuilder::OneSixVersionBuilder() +{ } -int findLibrary(QList > haystack, const QString &needle) +bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance, + QWidget *widgetParent, const bool excludeCustom) { - 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; + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = instance; + builder.m_widgetParent = widgetParent; + return builder.build(excludeCustom); } -bool OneSixVersionBuilder::applyLibrary(const QJsonObject &lib, const OneSixVersionBuilder::Type type) +bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj) { - // Library name - auto nameVal = lib.value("name"); - if (!nameVal.isString()) - { - return false; - } - auto name = nameVal.toString(); + OneSixVersionBuilder builder; + builder.m_version = version; + builder.m_instance = 0; + builder.m_widgetParent = 0; + return builder.read(obj); +} - if (type == Remove) +bool OneSixVersionBuilder::build(const bool excludeCustom) +{ + m_version->clear(); + + QDir root(m_instance->instanceRoot()); + QDir patches(root.absoluteFilePath("patches/")); + + // version.json -> patches/*.json -> custom.json + + // version.json { - int index = findLibrary(m_version->libraries, name); - if (index >= 0) + QLOG_INFO() << "Reading version.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file)) + { + return false; + } + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { - m_version->libraries.removeAt(index); + 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; } - return true; } - if (type == Add && !lib.contains("insert")) + // patches/ { - return false; - } - - std::shared_ptr library; + // load all, put into map for ordering, apply in the right order - if (lib.value("insert").toString() != "apply" && type == Add) - { - QMutableListIterator > it(m_version->libraries); - while (it.hasNext()) + QMap> files; + for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files)) { - if (it.next()->rawName() == name) + QLOG_INFO() << "Reading" << info.fileName(); + VersionFile file; + if (!read(info, true, &file)) { - it.remove(); + return false; } + files.insert(file.order, qMakePair(info.fileName(), file)); } - } - - if (lib.value("insert").toString() == "apply" && type == Add) - { - library = m_version->libraries[findLibrary(m_version->libraries, name)]; - } - else - { - library.reset(new OneSixLibrary(nameVal.toString())); - } - - applyString(lib, "url", library, &OneSixLibrary::setBaseUrl); - applyString(lib, "MMC-hint", library, &OneSixLibrary::setHint); - applyString(lib, "MMC-absulute_url", library, &OneSixLibrary::setAbsoluteUrl); - applyString(lib, "MMC-absoluteUrl", library, &OneSixLibrary::setAbsoluteUrl); - - auto extractVal = lib.value("extract"); - if (extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if (excludesVal.isArray()) + for (auto order : files.keys()) { - auto excludesList = excludesVal.toArray(); - for (auto excludeVal : excludesList) + QLOG_DEBUG() << "Applying file with order" << order; + auto filePair = files[order]; + bool isError = false; + filePair.second.applyTo(m_version, isError); + if (isError) { - if (excludeVal.isString()) - { - excludes.append(excludeVal.toString()); - } + 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; } - library->extract_excludes = excludes; } } - auto nativesVal = lib.value("natives"); - if (nativesVal.isObject()) + // custom.json + if (!excludeCustom) { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it) + if (QFile::exists(root.absoluteFilePath("custom.json"))) { - auto osType = OpSys_fromString(it.key()); - if (osType == Os_Other) + QLOG_INFO() << "Reading custom.json"; + VersionFile file; + if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file)) { - continue; + return false; } - if (!it.value().isString()) + bool isError = false; + file.applyTo(m_version, isError); + if (isError) { - continue; + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr( + "Error while applying %1. Please check MultiMC-0.log for more info.") + .arg(root.absoluteFilePath("custom.json"))); + return false; } - library->addNative(osType, it.value().toString()); } } - if (lib.contains("rules")) + // some final touches { - library->setRules(rulesFromJsonV4(lib)); - } - library->finalize(); - if (type == Override) - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() != "apply") - { - if (lib.value("insert").toString() == "append") - { - m_version->libraries.append(library); - } - else if (lib.value("insert").toString() == "prepend") + if (m_version->assets.isEmpty()) { - m_version->libraries.prepend(library); + m_version->assets = "legacy"; } - else if (lib.value("insert").isObject()) + if (m_version->minecraftArguments.isEmpty()) { - QJsonObject insertObj = lib.value("insert").toObject(); - if (insertObj.isEmpty()) + QString toCompare = m_version->processArguments.toLower(); + if (toCompare == "legacy") { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("'insert' object empty")); - return false; + m_version->minecraftArguments = " ${auth_player_name} ${auth_session}"; } - const QString key = insertObj.keys().first(); - const QString value = insertObj.value(key).toString(); - const int index = findLibrary(m_version->libraries, value); - if (index >= 0) + else if (toCompare == "username_session") { - if (key == "before") - { - m_version->libraries.insert(index, library); - } - else if (key == "after") - { - m_version->libraries.insert(index + 1, library); - } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } + 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}"; } } - else - { - QMessageBox::critical(m_widgetParent, QObject::tr("Error"), QObject::tr("Invalid value for 'insert': %1").arg(lib.value("insert").toString())); - return false; - } } + + return true; +} + +bool OneSixVersionBuilder::read(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; + } + file.applyTo(m_version, isError); + if (isError) + { + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Error while applying. Please check MultiMC-0.log for more info.")); + return false; + } + return true; } -bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, QJsonObject *out) +bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder, + VersionFile *out) { 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())); + QMessageBox::critical( + m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString())); return false; } 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").arg(file.fileName(), error.errorString()).arg(error.offset)); + QMessageBox::critical(m_widgetParent, QObject::tr("Error"), + QObject::tr("Unable to parse %1: %2 at %3") + .arg(file.fileName(), error.errorString()) + .arg(error.offset)); return false; } - *out = doc.object(); + bool isError = false; + *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError); + 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; } diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h index a3ba331c..ac8c13bf 100644 --- a/logic/OneSixVersionBuilder.h +++ b/logic/OneSixVersionBuilder.h @@ -22,12 +22,13 @@ class OneSixInstance; class QWidget; class QJsonObject; class QFileInfo; +class VersionFile; class OneSixVersionBuilder { OneSixVersionBuilder(); public: - static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent); + static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool excludeCustom); static bool read(OneSixVersion *version, const QJsonObject &obj); private: @@ -35,19 +36,8 @@ private: OneSixInstance *m_instance; QWidget *m_widgetParent; - enum Type - { - Override, - Add, - Remove - }; - - bool build(); + bool build(const bool excludeCustom); bool read(const QJsonObject &obj); - void clear(); - bool apply(const QJsonObject &object); - bool applyLibrary(const QJsonObject &lib, const Type type); - - bool read(const QFileInfo &fileInfo, QJsonObject *out); + bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out); }; -- cgit v1.2.3