Merge pull request #1543 from Trial97/packwiz

refactor packwiz file write
This commit is contained in:
Alexandru Ionut Tripon 2023-10-10 16:16:57 +01:00 committed by GitHub
commit 4eb8db16f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 121 additions and 45 deletions

View File

@ -31,6 +31,7 @@ class Mod;
class Metadata { class Metadata {
public: public:
using ModStruct = Packwiz::V1::Mod; using ModStruct = Packwiz::V1::Mod;
using ModSide = Packwiz::V1::Side;
static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct
{ {

View File

@ -122,7 +122,7 @@ void ModFolderLoadTask::getFromMetadata()
auto metadata = Metadata::get(m_index_dir, entry); auto metadata = Metadata::get(m_index_dir, entry);
if (!metadata.isValid()) { if (!metadata.isValid()) {
return; continue;
} }
auto* mod = new Mod(m_mods_dir, metadata); auto* mod = new Mod(m_mods_dir, metadata);

View File

@ -137,6 +137,7 @@ struct IndexedPack {
QString logoName; QString logoName;
QString logoUrl; QString logoUrl;
QString websiteUrl; QString websiteUrl;
QString side;
bool versionsLoaded = false; bool versionsLoaded = false;
QVector<IndexedVersion> versions; QVector<IndexedVersion> versions;

View File

@ -25,6 +25,7 @@
#include "Json.h" #include "Json.h"
#include "MMCZip.h" #include "MMCZip.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
#include "minecraft/mod/MetadataHandler.h"
#include "minecraft/mod/ModFolderModel.h" #include "minecraft/mod/ModFolderModel.h"
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
@ -129,7 +130,8 @@ void ModrinthPackExportTask::collectHashes()
QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1); QCryptographicHash sha1(QCryptographicHash::Algorithm::Sha1);
sha1.addData(data); sha1.addData(data);
ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size() }; ResolvedFile resolvedFile{ sha1.result().toHex(), sha512.result().toHex(), url.toEncoded(), openFile.size(),
mod->metadata()->side };
resolvedFiles[relative] = resolvedFile; resolvedFiles[relative] = resolvedFile;
// nice! we've managed to resolve based on local metadata! // nice! we've managed to resolve based on local metadata!
@ -272,22 +274,33 @@ QByteArray ModrinthPackExportTask::generateIndex()
QString path = iterator.key(); QString path = iterator.key();
const ResolvedFile& value = iterator.value(); const ResolvedFile& value = iterator.value();
if (optionalFiles) { QJsonObject env;
// detect disabled mod
const QFileInfo pathInfo(path); // detect disabled mod
if (pathInfo.suffix() == "disabled") { const QFileInfo pathInfo(path);
// rename it if (optionalFiles && pathInfo.suffix() == "disabled") {
path = pathInfo.dir().filePath(pathInfo.completeBaseName()); // rename it
// ...and make it optional path = pathInfo.dir().filePath(pathInfo.completeBaseName());
QJsonObject env; env["client"] = "optional";
env["client"] = "optional"; env["server"] = "optional";
env["server"] = "optional"; } else {
fileOut["env"] = env; env["client"] = "required";
} env["server"] = "required";
} }
switch (iterator->side) {
case Metadata::ModSide::ClientSide:
env["server"] = "unsupported";
break;
case Metadata::ModSide::ServerSide:
env["client"] = "unsupported";
break;
case Metadata::ModSide::UniversalSide:
break;
}
fileOut["env"] = env;
fileOut["path"] = path; fileOut["path"] = path;
fileOut["downloads"] = QJsonArray{ iterator.value().url }; fileOut["downloads"] = QJsonArray{ iterator->url };
QJsonObject hashes; QJsonObject hashes;
hashes["sha1"] = value.sha1; hashes["sha1"] = value.sha1;

View File

@ -44,6 +44,7 @@ class ModrinthPackExportTask : public Task {
struct ResolvedFile { struct ResolvedFile {
QString sha1, sha512, url; QString sha1, sha512, url;
qint64 size; qint64 size;
Metadata::ModSide side;
}; };
static const QStringList PREFIXES; static const QStringList PREFIXES;

View File

@ -27,6 +27,11 @@
static ModrinthAPI api; static ModrinthAPI api;
static ModPlatform::ProviderCapabilities ProviderCaps; static ModPlatform::ProviderCapabilities ProviderCaps;
bool shouldDownloadOnSide(QString side)
{
return side == "required" || side == "optional";
}
// https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject // https://docs.modrinth.com/api-spec/#tag/projects/operation/getProject
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
{ {
@ -53,6 +58,17 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
modAuthor.url = api.getAuthorURL(modAuthor.name); modAuthor.url = api.getAuthorURL(modAuthor.name);
pack.authors.append(modAuthor); pack.authors.append(modAuthor);
auto client = shouldDownloadOnSide(Json::ensureString(obj, "client_side"));
auto server = shouldDownloadOnSide(Json::ensureString(obj, "server_side"));
if (server && client) {
pack.side = "both";
} else if (server) {
pack.side = "server";
} else if (client) {
pack.side = "client";
}
// Modrinth can have more data than what's provided by the basic search :) // Modrinth can have more data than what's provided by the basic search :)
pack.extraDataLoaded = false; pack.extraDataLoaded = false;
} }

View File

@ -21,6 +21,8 @@
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QObject> #include <QObject>
#include <sstream>
#include <string>
#include "FileSystem.h" #include "FileSystem.h"
#include "StringUtils.h" #include "StringUtils.h"
@ -111,6 +113,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP
mod.provider = mod_pack.provider; mod.provider = mod_pack.provider;
mod.file_id = mod_version.fileId; mod.file_id = mod_version.fileId;
mod.project_id = mod_pack.addonId; mod.project_id = mod_pack.addonId;
mod.side = stringToSide(mod_pack.side);
return mod; return mod;
} }
@ -154,38 +157,52 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
FS::ensureFilePathExists(index_file.fileName()); FS::ensureFilePathExists(index_file.fileName());
} }
toml::table update;
switch (mod.provider) {
case (ModPlatform::ResourceProvider::FLAME):
if (mod.file_id.toInt() == 0 || mod.project_id.toInt() == 0) {
qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname);
return;
}
update = toml::table{
{ "file-id", mod.file_id.toInt() },
{ "project-id", mod.project_id.toInt() },
};
break;
case (ModPlatform::ResourceProvider::MODRINTH):
if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) {
qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname);
return;
}
update = toml::table{
{ "mod-id", mod.mod_id().toString().toStdString() },
{ "version", mod.version().toString().toStdString() },
};
break;
}
if (!index_file.open(QIODevice::ReadWrite)) { if (!index_file.open(QIODevice::ReadWrite)) {
qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); qCritical() << QString("Could not open file %1!").arg(normalized_fname);
return; return;
} }
// Put TOML data into the file // Put TOML data into the file
QTextStream in_stream(&index_file); QTextStream in_stream(&index_file);
auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); };
{ {
addToStream("name", mod.name); auto tbl = toml::table{ { "name", mod.name.toStdString() },
addToStream("filename", mod.filename); { "filename", mod.filename.toStdString() },
addToStream("side", mod.side); { "side", sideToString(mod.side).toStdString() },
{ "download",
in_stream << QString("\n[download]\n"); toml::table{
addToStream("mode", mod.mode); { "mode", mod.mode.toStdString() },
addToStream("url", mod.url.toString()); { "url", mod.url.toString().toStdString() },
addToStream("hash-format", mod.hash_format); { "hash-format", mod.hash_format.toStdString() },
addToStream("hash", mod.hash); { "hash", mod.hash.toStdString() },
} },
in_stream << QString("\n[update]\n"); { "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } };
in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider)); std::stringstream ss;
switch (mod.provider) { ss << tbl;
case (ModPlatform::ResourceProvider::FLAME): in_stream << QString::fromStdString(ss.str());
in_stream << QString("file-id = %1\n").arg(mod.file_id.toString());
in_stream << QString("project-id = %1\n").arg(mod.project_id.toString());
break;
case (ModPlatform::ResourceProvider::MODRINTH):
addToStream("mod-id", mod.mod_id().toString());
addToStream("version", mod.version().toString());
break;
}
} }
index_file.flush(); index_file.flush();
@ -258,7 +275,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
{ // Basic info { // Basic info
mod.name = stringEntry(table, "name"); mod.name = stringEntry(table, "name");
mod.filename = stringEntry(table, "filename"); mod.filename = stringEntry(table, "filename");
mod.side = stringEntry(table, "side"); mod.side = stringToSide(stringEntry(table, "side"));
} }
{ // [download] info { // [download] info
@ -313,4 +330,28 @@ auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod
return {}; return {};
} }
auto V1::sideToString(Side side) -> QString
{
switch (side) {
case Side::ClientSide:
return "client";
case Side::ServerSide:
return "server";
case Side::UniversalSide:
return "both";
}
return {};
}
auto V1::stringToSide(QString side) -> Side
{
if (side == "client")
return Side::ClientSide;
if (side == "server")
return Side::ServerSide;
if (side == "both")
return Side::UniversalSide;
return Side::UniversalSide;
}
} // namespace Packwiz } // namespace Packwiz

View File

@ -35,12 +35,12 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul
class V1 { class V1 {
public: public:
enum class Side { ClientSide = 1 << 0, ServerSide = 1 << 1, UniversalSide = ClientSide | ServerSide };
struct Mod { struct Mod {
QString slug{}; QString slug{};
QString name{}; QString name{};
QString filename{}; QString filename{};
// FIXME: make side an enum Side side{ Side::UniversalSide };
QString side{ "both" };
// [download] // [download]
QString mode{}; QString mode{};
@ -93,6 +93,9 @@ class V1 {
* If the mod doesn't have a metadata, it simply returns an empty Mod object. * If the mod doesn't have a metadata, it simply returns an empty Mod object.
* */ * */
static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod;
static auto sideToString(Side side) -> QString;
static auto stringToSide(QString side) -> Side;
}; };
} // namespace Packwiz } // namespace Packwiz

View File

@ -42,7 +42,7 @@ class PackwizTest : public QObject {
QCOMPARE(metadata.name, "Borderless Mining"); QCOMPARE(metadata.name, "Borderless Mining");
QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar");
QCOMPARE(metadata.side, "client"); QCOMPARE(metadata.side, Packwiz::V1::Side::ClientSide);
QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar"));
QCOMPARE(metadata.hash_format, "sha512"); QCOMPARE(metadata.hash_format, "sha512");
@ -72,7 +72,7 @@ class PackwizTest : public QObject {
QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)");
QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar");
QCOMPARE(metadata.side, "both"); QCOMPARE(metadata.side, Packwiz::V1::Side::UniversalSide);
QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar"));
QCOMPARE(metadata.hash_format, "murmur2"); QCOMPARE(metadata.hash_format, "murmur2");