NOISSUE Flatten gui and logic libraries into MultiMC

This commit is contained in:
Petr Mrázek
2021-07-25 19:11:59 +02:00
parent dd13368085
commit 20b9f2b42a
1113 changed files with 1228 additions and 1401 deletions

View File

@ -0,0 +1,63 @@
#include "FileResolvingTask.h"
#include "Json.h"
namespace {
const char * metabase = "https://cursemeta.dries007.net";
}
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess)
: m_toProcess(toProcess)
{
}
void Flame::FileResolvingTask::executeTask()
{
setStatus(tr("Resolving mod IDs..."));
setProgress(0, m_toProcess.files.size());
m_dljob.reset(new NetJob("Mod id resolver"));
results.resize(m_toProcess.files.size());
int index = 0;
for(auto & file: m_toProcess.files)
{
auto projectIdStr = QString::number(file.projectId);
auto fileIdStr = QString::number(file.fileId);
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
m_dljob->addNetAction(dl);
index ++;
}
connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
m_dljob->start();
}
void Flame::FileResolvingTask::netJobFinished()
{
bool failed = false;
int index = 0;
for(auto & bytes: results)
{
auto & out = m_toProcess.files[index];
try
{
failed &= (!out.parseFromBytes(bytes));
}
catch (const JSONValidationError &e)
{
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
qCritical() << "JSON:";
qCritical() << bytes;
failed = true;
}
index++;
}
if(!failed)
{
emitSucceeded();
}
else
{
emitFailed(tr("Some mod ID resolving tasks failed."));
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include "PackManifest.h"
namespace Flame
{
class FileResolvingTask : public Task
{
Q_OBJECT
public:
explicit FileResolvingTask(Flame::Manifest &toProcess);
virtual ~FileResolvingTask() {};
const Flame::Manifest &getResults() const
{
return m_toProcess;
}
protected:
virtual void executeTask() override;
protected slots:
void netJobFinished();
private: /* data */
Flame::Manifest m_toProcess;
QVector<QByteArray> results;
NetJobPtr m_dljob;
};
}

View File

@ -0,0 +1,92 @@
#include "FlamePackIndex.h"
#include "Json.h"
void Flame::loadIndexedPack(Flame::IndexedPack & pack, QJsonObject & obj)
{
pack.addonId = Json::requireInteger(obj, "id");
pack.name = Json::requireString(obj, "name");
pack.websiteUrl = Json::ensureString(obj, "websiteUrl", "");
pack.description = Json::ensureString(obj, "summary", "");
bool thumbnailFound = false;
auto attachments = Json::requireArray(obj, "attachments");
for(auto attachmentRaw: attachments) {
auto attachmentObj = Json::requireObject(attachmentRaw);
bool isDefault = attachmentObj.value("isDefault").toBool(false);
if(isDefault) {
thumbnailFound = true;
pack.logoName = Json::requireString(attachmentObj, "title");
pack.logoUrl = Json::requireString(attachmentObj, "thumbnailUrl");
break;
}
}
if(!thumbnailFound) {
throw JSONValidationError(QString("Pack without an icon, skipping: %1").arg(pack.name));
}
auto authors = Json::requireArray(obj, "authors");
for(auto authorIter: authors) {
auto author = Json::requireObject(authorIter);
Flame::ModpackAuthor packAuthor;
packAuthor.name = Json::requireString(author, "name");
packAuthor.url = Json::requireString(author, "url");
pack.authors.append(packAuthor);
}
int defaultFileId = Json::requireInteger(obj, "defaultFileId");
bool found = false;
// check if there are some files before adding the pack
auto files = Json::requireArray(obj, "latestFiles");
for(auto fileIter: files) {
auto file = Json::requireObject(fileIter);
int id = Json::requireInteger(file, "id");
// NOTE: for now, ignore everything that's not the default...
if(id != defaultFileId) {
continue;
}
auto versionArray = Json::requireArray(file, "gameVersion");
if(versionArray.size() < 1) {
continue;
}
found = true;
break;
}
if(!found) {
throw JSONValidationError(QString("Pack with no good file, skipping: %1").arg(pack.name));
}
}
void Flame::loadIndexedPackVersions(Flame::IndexedPack & pack, QJsonArray & arr)
{
QVector<Flame::IndexedVersion> unsortedVersions;
for(auto versionIter: arr) {
auto version = Json::requireObject(versionIter);
Flame::IndexedVersion file;
file.addonId = pack.addonId;
file.fileId = Json::requireInteger(version, "id");
auto versionArray = Json::requireArray(version, "gameVersion");
if(versionArray.size() < 1) {
continue;
}
// pick the latest version supported
file.mcVersion = versionArray[0].toString();
file.version = Json::requireString(version, "displayName");
file.downloadUrl = Json::requireString(version, "downloadUrl");
unsortedVersions.append(file);
}
auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
{
return a.fileId > b.fileId;
};
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
pack.versions = unsortedVersions;
pack.versionsLoaded = true;
}

View File

@ -0,0 +1,41 @@
#pragma once
#include <QList>
#include <QMetaType>
#include <QString>
#include <QVector>
namespace Flame {
struct ModpackAuthor {
QString name;
QString url;
};
struct IndexedVersion {
int addonId;
int fileId;
QString version;
QString mcVersion;
QString downloadUrl;
};
struct IndexedPack
{
int addonId;
QString name;
QString description;
QList<ModpackAuthor> authors;
QString logoName;
QString logoUrl;
QString websiteUrl;
bool versionsLoaded = false;
QVector<IndexedVersion> versions;
};
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
void loadIndexedPackVersions(IndexedPack & m, QJsonArray & arr);
}
Q_DECLARE_METATYPE(Flame::IndexedPack)

View File

@ -0,0 +1,126 @@
#include "PackManifest.h"
#include "Json.h"
static void loadFileV1(Flame::File & f, QJsonObject & file)
{
f.projectId = Json::requireInteger(file, "projectID");
f.fileId = Json::requireInteger(file, "fileID");
f.required = Json::ensureBoolean(file, QString("required"), true);
}
static void loadModloaderV1(Flame::Modloader & m, QJsonObject & modLoader)
{
m.id = Json::requireString(modLoader, "id");
m.primary = Json::ensureBoolean(modLoader, QString("primary"), false);
}
static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
{
m.version = Json::requireString(minecraft, "version");
// extra libraries... apparently only used for a custom Minecraft launcher in the 1.2.5 FTB retro pack
// intended use is likely hardcoded in the 'Flame' client, the manifest says nothing
m.libraries = Json::ensureString(minecraft, QString("libraries"), QString());
auto arr = Json::ensureArray(minecraft, "modLoaders", QJsonArray());
for (QJsonValueRef item : arr)
{
auto obj = Json::requireObject(item);
Flame::Modloader loader;
loadModloaderV1(loader, obj);
m.modLoaders.append(loader);
}
}
static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
{
auto mc = Json::requireObject(manifest, "minecraft");
loadMinecraftV1(m.minecraft, mc);
m.name = Json::ensureString(manifest, QString("name"), "Unnamed");
m.version = Json::ensureString(manifest, QString("version"), QString());
m.author = Json::ensureString(manifest, QString("author"), "Anonymous Coward");
auto arr = Json::ensureArray(manifest, "files", QJsonArray());
for (QJsonValueRef item : arr)
{
auto obj = Json::requireObject(item);
Flame::File file;
loadFileV1(file, obj);
m.files.append(file);
}
m.overrides = Json::ensureString(manifest, "overrides", "overrides");
}
void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
{
auto doc = Json::requireDocument(filepath);
auto obj = Json::requireObject(doc);
m.manifestType = Json::requireString(obj, "manifestType");
if(m.manifestType != "minecraftModpack")
{
throw JSONValidationError("Not a modpack manifest!");
}
m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
if(m.manifestVersion != 1)
{
throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
}
loadManifestV1(m, obj);
}
bool Flame::File::parseFromBytes(const QByteArray& bytes)
{
auto doc = Json::requireDocument(bytes);
auto obj = Json::requireObject(doc);
// result code signifies true failure.
if(obj.contains("code"))
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of a negative result:";
qCritical() << bytes;
return false;
}
fileName = Json::requireString(obj, "FileNameOnDisk");
QString rawUrl = Json::requireString(obj, "DownloadURL");
url = QUrl(rawUrl, QUrl::TolerantMode);
if(!url.isValid())
{
throw JSONValidationError(QString("Invalid URL: %1").arg(rawUrl));
}
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
// It is also optional
QJsonObject projObj = Json::ensureObject(obj, "_Project", {});
if(!projObj.isEmpty())
{
QString strType = Json::ensureString(projObj, "PackageType", "mod").toLower();
if(strType == "singlefile")
{
type = File::Type::SingleFile;
}
else if(strType == "ctoc")
{
type = File::Type::Ctoc;
}
else if(strType == "cmod2")
{
type = File::Type::Cmod2;
}
else if(strType == "mod")
{
type = File::Type::Mod;
}
else if(strType == "folder")
{
type = File::Type::Folder;
}
else if(strType == "modpack")
{
type = File::Type::Modpack;
}
else
{
qCritical() << "Resolving of" << projectId << fileId << "failed because of unknown file type:" << strType;
type = File::Type::Unknown;
return false;
}
targetFolder = Json::ensureString(projObj, "Path", "mods");
}
resolved = true;
return true;
}

View File

@ -0,0 +1,62 @@
#pragma once
#include <QString>
#include <QVector>
#include <QUrl>
namespace Flame
{
struct File
{
// NOTE: throws JSONValidationError
bool parseFromBytes(const QByteArray &bytes);
int projectId = 0;
int fileId = 0;
// NOTE: the opposite to 'optional'. This is at the time of writing unused.
bool required = true;
// our
bool resolved = false;
QString fileName;
QUrl url;
QString targetFolder = QLatin1Literal("mods");
enum class Type
{
Unknown,
Folder,
Ctoc,
SingleFile,
Cmod2,
Modpack,
Mod
} type = Type::Mod;
};
struct Modloader
{
QString id;
bool primary = false;
};
struct Minecraft
{
QString version;
QString libraries;
QVector<Flame::Modloader> modLoaders;
};
struct Manifest
{
QString manifestType;
int manifestVersion = 0;
Flame::Minecraft minecraft;
QString name;
QString version;
QString author;
QVector<Flame::File> files;
QString overrides;
};
void loadManifest(Flame::Manifest & m, const QString &filepath);
}