NOISSUE Flatten gui and logic libraries into MultiMC
This commit is contained in:
63
launcher/modplatform/flame/FileResolvingTask.cpp
Normal file
63
launcher/modplatform/flame/FileResolvingTask.cpp
Normal 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."));
|
||||
}
|
||||
}
|
32
launcher/modplatform/flame/FileResolvingTask.h
Normal file
32
launcher/modplatform/flame/FileResolvingTask.h
Normal 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;
|
||||
};
|
||||
}
|
92
launcher/modplatform/flame/FlamePackIndex.cpp
Normal file
92
launcher/modplatform/flame/FlamePackIndex.cpp
Normal 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;
|
||||
}
|
41
launcher/modplatform/flame/FlamePackIndex.h
Normal file
41
launcher/modplatform/flame/FlamePackIndex.h
Normal 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)
|
126
launcher/modplatform/flame/PackManifest.cpp
Normal file
126
launcher/modplatform/flame/PackManifest.cpp
Normal 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;
|
||||
}
|
62
launcher/modplatform/flame/PackManifest.h
Normal file
62
launcher/modplatform/flame/PackManifest.h
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user