@ -1,14 +1,9 @@
|
||||
#include "FileResolvingTask.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace {
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
}
|
||||
|
||||
Flame::FileResolvingTask::FileResolvingTask(shared_qobject_ptr<QNetworkAccessManager> network, Flame::Manifest& toProcess)
|
||||
: m_network(network), m_toProcess(toProcess)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
void Flame::FileResolvingTask::executeTask()
|
||||
{
|
||||
@ -17,14 +12,13 @@ void Flame::FileResolvingTask::executeTask()
|
||||
m_dljob = new NetJob("Mod id resolver", m_network);
|
||||
results.resize(m_toProcess.files.size());
|
||||
int index = 0;
|
||||
for(auto & file: m_toProcess.files)
|
||||
{
|
||||
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);
|
||||
QString metaurl = QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(projectIdStr, fileIdStr);
|
||||
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results[index]);
|
||||
m_dljob->addNetAction(dl);
|
||||
index ++;
|
||||
index++;
|
||||
}
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::FileResolvingTask::netJobFinished);
|
||||
m_dljob->start();
|
||||
@ -34,16 +28,11 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
{
|
||||
bool failed = false;
|
||||
int index = 0;
|
||||
for(auto & bytes: results)
|
||||
{
|
||||
auto & out = m_toProcess.files[index];
|
||||
try
|
||||
{
|
||||
for (auto& bytes : results) {
|
||||
auto& out = m_toProcess.files[index];
|
||||
try {
|
||||
failed &= (!out.parseFromBytes(bytes));
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
qCritical() << "Resolving of" << out.projectId << out.fileId << "failed because of a parsing error:";
|
||||
qCritical() << e.cause();
|
||||
qCritical() << "JSON:";
|
||||
@ -52,12 +41,9 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if(!failed)
|
||||
{
|
||||
if (!failed) {
|
||||
emitSucceeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
emitFailed(tr("Some mod ID resolving tasks failed."));
|
||||
}
|
||||
}
|
||||
|
@ -3,33 +3,53 @@
|
||||
#include "modplatform/helpers/NetworkModAPI.h"
|
||||
|
||||
class FlameAPI : public NetworkModAPI {
|
||||
private:
|
||||
inline auto getSortFieldInt(QString sortString) const -> int
|
||||
{
|
||||
return sortString == "Featured" ? 1
|
||||
: sortString == "Popularity" ? 2
|
||||
: sortString == "LastUpdated" ? 3
|
||||
: sortString == "Name" ? 4
|
||||
: sortString == "Author" ? 5
|
||||
: sortString == "TotalDownloads" ? 6
|
||||
: sortString == "Category" ? 7
|
||||
: sortString == "GameVersion" ? 8
|
||||
: 1;
|
||||
}
|
||||
|
||||
private:
|
||||
inline auto getModSearchURL(SearchArgs& args) const -> QString override
|
||||
{
|
||||
auto gameVersionStr = args.versions.size() != 0 ? QString("gameVersion=%1").arg(args.versions.front().toString()) : QString();
|
||||
|
||||
return QString(
|
||||
"https://addons-ecs.forgesvc.net/api/v2/addon/search?"
|
||||
"https://api.curseforge.com/v1/mods/search?"
|
||||
"gameId=432&"
|
||||
"categoryId=0&"
|
||||
"sectionId=6&"
|
||||
"classId=6&"
|
||||
|
||||
"index=%1&"
|
||||
"pageSize=25&"
|
||||
"searchFilter=%2&"
|
||||
"sort=%3&"
|
||||
"sortField=%3&"
|
||||
"sortOrder=desc&"
|
||||
"modLoaderType=%4&"
|
||||
"%5")
|
||||
.arg(args.offset)
|
||||
.arg(args.search)
|
||||
.arg(args.sorting)
|
||||
.arg(getSortFieldInt(args.sorting))
|
||||
.arg(getMappedModLoader(args.mod_loader))
|
||||
.arg(gameVersionStr);
|
||||
};
|
||||
|
||||
inline auto getVersionsURL(VersionSearchArgs& args) const -> QString override
|
||||
{
|
||||
return QString("https://addons-ecs.forgesvc.net/api/v2/addon/%1/files").arg(args.addonId);
|
||||
QString gameVersionQuery = args.mcVersions.size() == 1 ? QString("gameVersion=%1&").arg(args.mcVersions.front().toString()) : "";
|
||||
QString modLoaderQuery = QString("modLoaderType=%1&").arg(getMappedModLoader(args.loader));
|
||||
|
||||
return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&%2%3")
|
||||
.arg(args.addonId)
|
||||
.arg(gameVersionQuery)
|
||||
.arg(modLoaderQuery);
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -10,23 +10,12 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::requireInteger(obj, "id");
|
||||
pack.name = Json::requireString(obj, "name");
|
||||
pack.websiteUrl = Json::ensureString(obj, "websiteUrl", "");
|
||||
pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "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)); }
|
||||
QJsonObject logo = Json::requireObject(obj, "logo");
|
||||
pack.logoName = Json::requireString(logo, "title");
|
||||
pack.logoUrl = Json::requireString(logo, "thumbnailUrl");
|
||||
|
||||
auto authors = Json::requireArray(obj, "authors");
|
||||
for (auto authorIter : authors) {
|
||||
@ -45,18 +34,22 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
{
|
||||
QVector<ModPlatform::IndexedVersion> unsortedVersions;
|
||||
auto profile = (dynamic_cast<MinecraftInstance*>(inst))->getPackProfile();
|
||||
bool hasFabric = FlameAPI::getMappedModLoader(profile->getModLoader()) == ModAPI::Fabric;
|
||||
QString mcVersion = profile->getComponentVersion("net.minecraft");
|
||||
|
||||
for (auto versionIter : arr) {
|
||||
auto obj = versionIter.toObject();
|
||||
|
||||
auto versionArray = Json::requireArray(obj, "gameVersion");
|
||||
if (versionArray.isEmpty()) { continue; }
|
||||
auto versionArray = Json::requireArray(obj, "gameVersions");
|
||||
if (versionArray.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModPlatform::IndexedVersion file;
|
||||
for (auto mcVer : versionArray) {
|
||||
file.mcVersion.append(mcVer.toString());
|
||||
auto str = mcVer.toString();
|
||||
|
||||
if (str.contains('.'))
|
||||
file.mcVersion.append(str);
|
||||
}
|
||||
|
||||
file.addonId = pack.addonId;
|
||||
@ -66,28 +59,9 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,
|
||||
file.downloadUrl = Json::requireString(obj, "downloadUrl");
|
||||
file.fileName = Json::requireString(obj, "fileName");
|
||||
|
||||
auto modules = Json::requireArray(obj, "modules");
|
||||
bool is_valid_fabric_version = false;
|
||||
for (auto m : modules) {
|
||||
auto fname = Json::requireString(m.toObject(), "foldername");
|
||||
// FIXME: This does not work properly when a mod supports more than one mod loader, since
|
||||
// FIXME: This also doesn't deal with Quilt mods at the moment
|
||||
// they bundle the meta files for all of them in the same arquive, even when that version
|
||||
// doesn't support the given mod loader.
|
||||
if (hasFabric) {
|
||||
if (fname == "fabric.mod.json") {
|
||||
is_valid_fabric_version = true;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
// NOTE: Since we're not validating forge versions, we can just skip this loop.
|
||||
}
|
||||
|
||||
if (hasFabric && !is_valid_fabric_version) continue;
|
||||
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
|
@ -2,76 +2,63 @@
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
void Flame::loadIndexedPack(Flame::IndexedPack & pack, QJsonObject & obj)
|
||||
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 logo = Json::requireObject(obj, "logo");
|
||||
pack.logoName = Json::requireString(logo, "title");
|
||||
pack.logoUrl = Json::requireString(logo, "thumbnailUrl");
|
||||
|
||||
auto authors = Json::requireArray(obj, "authors");
|
||||
for(auto authorIter: 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");
|
||||
int defaultFileId = Json::requireInteger(obj, "mainFileId");
|
||||
|
||||
bool found = false;
|
||||
// check if there are some files before adding the pack
|
||||
auto files = Json::requireArray(obj, "latestFiles");
|
||||
for(auto fileIter: files) {
|
||||
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) {
|
||||
if (id != defaultFileId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto versionArray = Json::requireArray(file, "gameVersion");
|
||||
if(versionArray.size() < 1) {
|
||||
auto versionArray = Json::requireArray(file, "gameVersions");
|
||||
if (versionArray.size() < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if(!found) {
|
||||
if (!found) {
|
||||
throw JSONValidationError(QString("Pack with no good file, skipping: %1").arg(pack.name));
|
||||
}
|
||||
}
|
||||
|
||||
void Flame::loadIndexedPackVersions(Flame::IndexedPack & pack, QJsonArray & arr)
|
||||
void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
|
||||
{
|
||||
QVector<Flame::IndexedVersion> unsortedVersions;
|
||||
for(auto versionIter: arr) {
|
||||
for (auto versionIter : arr) {
|
||||
auto version = Json::requireObject(versionIter);
|
||||
Flame::IndexedVersion file;
|
||||
Flame::IndexedVersion file;
|
||||
|
||||
file.addonId = pack.addonId;
|
||||
file.fileId = Json::requireInteger(version, "id");
|
||||
auto versionArray = Json::requireArray(version, "gameVersion");
|
||||
if(versionArray.size() < 1) {
|
||||
auto versionArray = Json::requireArray(version, "gameVersions");
|
||||
if (versionArray.size() < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -82,10 +69,7 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack & pack, QJsonArray & arr)
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const IndexedVersion & a, const IndexedVersion & b) -> bool
|
||||
{
|
||||
return a.fileId > b.fileId;
|
||||
};
|
||||
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;
|
||||
|
@ -1,28 +1,27 @@
|
||||
#include "PackManifest.h"
|
||||
#include "Json.h"
|
||||
|
||||
static void loadFileV1(Flame::File & f, QJsonObject & file)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
{
|
||||
for (QJsonValueRef item : arr) {
|
||||
auto obj = Json::requireObject(item);
|
||||
Flame::Modloader loader;
|
||||
loadModloaderV1(loader, obj);
|
||||
@ -30,16 +29,15 @@ static void loadMinecraftV1(Flame::Minecraft & m, QJsonObject & minecraft)
|
||||
}
|
||||
}
|
||||
|
||||
static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
|
||||
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");
|
||||
m.author = Json::ensureString(manifest, QString("author"), "Anonymous");
|
||||
auto arr = Json::ensureArray(manifest, "files", QJsonArray());
|
||||
for (QJsonValueRef item : arr)
|
||||
{
|
||||
for (QJsonValueRef item : arr) {
|
||||
auto obj = Json::requireObject(item);
|
||||
Flame::File file;
|
||||
loadFileV1(file, obj);
|
||||
@ -48,18 +46,16 @@ static void loadManifestV1(Flame::Manifest & m, QJsonObject & manifest)
|
||||
m.overrides = Json::ensureString(manifest, "overrides", "overrides");
|
||||
}
|
||||
|
||||
void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
|
||||
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")
|
||||
{
|
||||
if (m.manifestType != "minecraftModpack") {
|
||||
throw JSONValidationError("Not a modpack manifest!");
|
||||
}
|
||||
m.manifestVersion = Json::requireInteger(obj, "manifestVersion");
|
||||
if(m.manifestVersion != 1)
|
||||
{
|
||||
if (m.manifestVersion != 1) {
|
||||
throw JSONValidationError(QString("Unknown manifest version (%1)").arg(m.manifestVersion));
|
||||
}
|
||||
loadManifestV1(m, obj);
|
||||
@ -68,59 +64,30 @@ void Flame::loadManifest(Flame::Manifest & m, const QString &filepath)
|
||||
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;
|
||||
if (!doc.isObject()) {
|
||||
throw JSONValidationError(QString("data is not an object? that's not supposed to happen"));
|
||||
}
|
||||
fileName = Json::requireString(obj, "FileNameOnDisk");
|
||||
QString rawUrl = Json::requireString(obj, "DownloadURL");
|
||||
auto obj = Json::ensureObject(doc.object(), "data");
|
||||
|
||||
fileName = Json::requireString(obj, "fileName");
|
||||
|
||||
QString rawUrl = Json::requireString(obj, "downloadUrl");
|
||||
url = QUrl(rawUrl, QUrl::TolerantMode);
|
||||
if(!url.isValid())
|
||||
{
|
||||
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");
|
||||
type = File::Type::SingleFile;
|
||||
|
||||
if (fileName.endsWith(".zip")) {
|
||||
// this is probably a resource pack
|
||||
targetFolder = "resourcepacks";
|
||||
} else {
|
||||
// this is probably a mod, dunno what else could modpacks download
|
||||
targetFolder = "mods";
|
||||
}
|
||||
|
||||
resolved = true;
|
||||
return true;
|
||||
}
|
||||
|
Reference in New Issue
Block a user