Use non-mod metadata in ModrinthPackExportTask

Signed-off-by: TheKodeToad <TheKodeToad@proton.me>
This commit is contained in:
TheKodeToad 2024-10-25 19:13:41 +01:00
parent 5eff9b0934
commit 13e13ea8fc
No known key found for this signature in database
GPG Key ID: 5E39D70B4C93C38E
8 changed files with 143 additions and 109 deletions

View File

@ -522,7 +522,8 @@ QStringList MinecraftInstance::javaArguments()
if (javaVersion.isModular() && shouldApplyOnlineFixes()) if (javaVersion.isModular() && shouldApplyOnlineFixes())
// allow reflective access to java.net - required by the skin fix // allow reflective access to java.net - required by the skin fix
args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; args << "--add-opens"
<< "java.base/java.net=ALL-UNNAMED";
return args; return args;
} }
@ -796,8 +797,10 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftT
QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin)
{ {
QStringList out; QStringList out;
out << "Main Class:" << " " + getMainClass() << ""; out << "Main Class:"
out << "Native path:" << " " + getNativePath() << ""; << " " + getMainClass() << "";
out << "Native path:"
<< " " + getNativePath() << "";
auto profile = m_components->getProfile(); auto profile = m_components->getProfile();
@ -1227,6 +1230,11 @@ std::shared_ptr<ShaderPackFolderModel> MinecraftInstance::shaderPackList()
return m_shader_pack_list; return m_shader_pack_list;
} }
QList<std::shared_ptr<ResourceFolderModel>> MinecraftInstance::resourceLists()
{
return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList() };
}
std::shared_ptr<WorldList> MinecraftInstance::worldList() std::shared_ptr<WorldList> MinecraftInstance::worldList()
{ {
if (!m_world_list) { if (!m_world_list) {

View File

@ -116,6 +116,7 @@ class MinecraftInstance : public BaseInstance {
std::shared_ptr<ResourcePackFolderModel> resourcePackList(); std::shared_ptr<ResourcePackFolderModel> resourcePackList();
std::shared_ptr<TexturePackFolderModel> texturePackList(); std::shared_ptr<TexturePackFolderModel> texturePackList();
std::shared_ptr<ShaderPackFolderModel> shaderPackList(); std::shared_ptr<ShaderPackFolderModel> shaderPackList();
QList<std::shared_ptr<ResourceFolderModel>> resourceLists();
std::shared_ptr<WorldList> worldList(); std::shared_ptr<WorldList> worldList();
std::shared_ptr<GameOptions> gameOptionsModel(); std::shared_ptr<GameOptions> gameOptionsModel();

View File

@ -115,7 +115,7 @@ QString hash(QIODevice* device, Algorithm type)
QCryptographicHash hash(alg); QCryptographicHash hash(alg);
if (!hash.addData(device)) if (!hash.addData(device))
qCritical() << "Failed to read JAR to create hash!"; qCritical() << "Failed to read file to create hash!";
Q_ASSERT(hash.result().length() == hash.hashLength(alg)); Q_ASSERT(hash.result().length() == hash.hashLength(alg));
auto result = hash.result().toHex(); auto result = hash.result().toHex();

View File

@ -34,22 +34,21 @@
const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" });
const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" });
ModrinthPackExportTask::ModrinthPackExportTask(const QString& name, ModrinthPackExportTask::ModrinthPackExportTask(QString name,
const QString& version, QString version,
const QString& summary, QString summary,
bool optionalFiles, bool optionalFiles,
InstancePtr instance, MinecraftInstancePtr instance,
const QString& output, QString output,
MMCZip::FilterFunction filter) MMCZip::FilterFunction filter)
: name(name) : name(std::move(name))
, version(version) , version(std::move(version))
, summary(summary) , summary(std::move(summary))
, optionalFiles(optionalFiles) , optionalFiles(optionalFiles)
, instance(instance) , instance(std::move(instance))
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get())) , gameRoot(this->instance->gameRoot())
, gameRoot(instance->gameRoot()) , output(std::move(output))
, output(output) , filter(std::move(filter))
, filter(filter)
{} {}
void ModrinthPackExportTask::executeTask() void ModrinthPackExportTask::executeTask()
@ -83,20 +82,86 @@ void ModrinthPackExportTask::collectFiles()
pendingHashes.clear(); pendingHashes.clear();
resolvedFiles.clear(); resolvedFiles.clear();
if (mcInstance) { collectHashes();
mcInstance->loaderModList()->update();
connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &ModrinthPackExportTask::collectHashes);
} else
collectHashes();
} }
void ModrinthPackExportTask::collectHashes() void ModrinthPackExportTask::collectHashes()
{ {
setStatus(tr("Finding file hashes...")); setStatus(tr("Finding file hashes..."));
for (const auto& model : instance->resourceLists()) {
QCoreApplication::processEvents(); // TODO: maybe don't do this?
QEventLoop loop;
connect(model.get(), &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
model->update();
loop.exec();
for (const Resource* resource : model->allResources()) {
QCoreApplication::processEvents();
if (resource->metadata() == nullptr)
continue;
const QUrl& url = resource->metadata()->url;
if (url.isEmpty() || !BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host()))
continue;
const QFileInfo& fileInfo = resource->fileinfo();
const QString relativePath = gameRoot.relativeFilePath(fileInfo.absoluteFilePath());
if (filter(relativePath))
continue;
qDebug() << "Resolving" << relativePath << "from index";
QString sha1;
QString sha512;
qint64 size;
if (resource->metadata()->hash_format == "sha1")
sha1 = resource->metadata()->hash;
else if (resource->metadata()->hash_format == "sha512")
sha512 = resource->metadata()->hash;
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly)) {
qWarning() << "Could not open" << relativePath << "for hashing";
continue;
}
const QByteArray data = file.readAll();
if (file.error() != QFileDevice::NoError) {
qWarning() << "Could not read" << relativePath;
continue;
}
if (sha1.isEmpty())
sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1);
if (sha512.isEmpty())
sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512);
size = file.size();
}
ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), size, resource->metadata()->side };
resolvedFiles[relativePath] = resolvedFile;
}
}
for (const QFileInfo& file : files) { for (const QFileInfo& file : files) {
QCoreApplication::processEvents(); QCoreApplication::processEvents();
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
if (resolvedFiles.contains(relative))
continue;
// require sensible file types // require sensible file types
if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); }))
continue; continue;
@ -105,42 +170,8 @@ void ModrinthPackExportTask::collectHashes()
})) }))
continue; continue;
QFile openFile(file.absoluteFilePath());
if (!openFile.open(QFile::ReadOnly)) {
qWarning() << "Could not open" << file << "for hashing";
continue;
}
const QByteArray data = openFile.readAll();
if (openFile.error() != QFileDevice::NoError) {
qWarning() << "Could not read" << file;
continue;
}
auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512);
auto allMods = mcInstance->loaderModList()->allMods();
if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; });
modIter != allMods.end()) {
const Mod* mod = *modIter;
if (mod->metadata() != nullptr) {
const QUrl& url = mod->metadata()->url;
// ensure the url is permitted on modrinth.com
if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) {
qDebug() << "Resolving" << relative << "from index";
auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1);
ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side };
resolvedFiles[relative] = resolvedFile;
// nice! we've managed to resolve based on local metadata!
// no need to enqueue it
continue;
}
}
}
qDebug() << "Enqueueing" << relative << "for Modrinth query"; qDebug() << "Enqueueing" << relative << "for Modrinth query";
auto sha512 = Hashing::hash(file.absoluteFilePath(), Hashing::Algorithm::Sha512);
pendingHashes[relative] = sha512; pendingHashes[relative] = sha512;
} }
@ -241,30 +272,28 @@ QByteArray ModrinthPackExportTask::generateIndex()
if (!summary.isEmpty()) if (!summary.isEmpty())
out["summary"] = summary; out["summary"] = summary;
if (mcInstance) { auto profile = instance->getPackProfile();
auto profile = mcInstance->getPackProfile(); // collect all supported components
// collect all supported components const ComponentPtr minecraft = profile->getComponent("net.minecraft");
const ComponentPtr minecraft = profile->getComponent("net.minecraft"); const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); const ComponentPtr forge = profile->getComponent("net.minecraftforge");
const ComponentPtr forge = profile->getComponent("net.minecraftforge"); const ComponentPtr neoForge = profile->getComponent("net.neoforged");
const ComponentPtr neoForge = profile->getComponent("net.neoforged");
// convert all available components to mrpack dependencies // convert all available components to mrpack dependencies
QJsonObject dependencies; QJsonObject dependencies;
if (minecraft != nullptr) if (minecraft != nullptr)
dependencies["minecraft"] = minecraft->m_version; dependencies["minecraft"] = minecraft->m_version;
if (quilt != nullptr) if (quilt != nullptr)
dependencies["quilt-loader"] = quilt->m_version; dependencies["quilt-loader"] = quilt->m_version;
if (fabric != nullptr) if (fabric != nullptr)
dependencies["fabric-loader"] = fabric->m_version; dependencies["fabric-loader"] = fabric->m_version;
if (forge != nullptr) if (forge != nullptr)
dependencies["forge"] = forge->m_version; dependencies["forge"] = forge->m_version;
if (neoForge != nullptr) if (neoForge != nullptr)
dependencies["neoforge"] = neoForge->m_version; dependencies["neoforge"] = neoForge->m_version;
out["dependencies"] = dependencies; out["dependencies"] = dependencies;
}
QJsonArray filesOut; QJsonArray filesOut;
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) { for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {

View File

@ -29,12 +29,12 @@
class ModrinthPackExportTask : public Task { class ModrinthPackExportTask : public Task {
Q_OBJECT Q_OBJECT
public: public:
ModrinthPackExportTask(const QString& name, ModrinthPackExportTask(QString name,
const QString& version, QString version,
const QString& summary, QString summary,
bool optionalFiles, bool optionalFiles,
InstancePtr instance, MinecraftInstancePtr instance,
const QString& output, QString output,
MMCZip::FilterFunction filter); MMCZip::FilterFunction filter);
protected: protected:
@ -54,8 +54,7 @@ class ModrinthPackExportTask : public Task {
// inputs // inputs
const QString name, version, summary; const QString name, version, summary;
const bool optionalFiles; const bool optionalFiles;
const InstancePtr instance; const MinecraftInstancePtr instance;
MinecraftInstance* mcInstance;
const QDir gameRoot; const QDir gameRoot;
const QString output; const QString output;
const MMCZip::FilterFunction filter; const MMCZip::FilterFunction filter;

View File

@ -1403,27 +1403,26 @@ void MainWindow::on_actionExportInstanceZip_triggered()
void MainWindow::on_actionExportInstanceMrPack_triggered() void MainWindow::on_actionExportInstanceMrPack_triggered()
{ {
if (m_selectedInstance) { auto instance = std::dynamic_pointer_cast<MinecraftInstance>(m_selectedInstance);
ExportPackDialog dlg(m_selectedInstance, this); if (instance) {
ExportPackDialog dlg(std::move(instance), this);
dlg.exec(); dlg.exec();
} }
} }
void MainWindow::on_actionExportInstanceFlamePack_triggered() void MainWindow::on_actionExportInstanceFlamePack_triggered()
{ {
if (m_selectedInstance) { auto instance = std::dynamic_pointer_cast<MinecraftInstance>(m_selectedInstance);
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get()); if (instance) {
if (instance) { if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft");
if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") {
cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { QMessageBox msgBox(this);
QMessageBox msgBox(this); msgBox.setText("Snapshots are currently not supported by CurseForge modpacks.");
msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); msgBox.exec();
msgBox.exec(); return;
return;
}
ExportPackDialog dlg(m_selectedInstance, this, ModPlatform::ResourceProvider::FLAME);
dlg.exec();
} }
ExportPackDialog dlg(std::move(instance), this, ModPlatform::ResourceProvider::FLAME);
dlg.exec();
} }
} }

View File

@ -34,7 +34,7 @@
#include "MMCZip.h" #include "MMCZip.h"
#include "modplatform/modrinth/ModrinthPackExportTask.h" #include "modplatform/modrinth/ModrinthPackExportTask.h"
ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider) ExportPackDialog::ExportPackDialog(const MinecraftInstancePtr& instance, QWidget* parent, ModPlatform::ResourceProvider provider)
: QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider) : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider)
{ {
Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME); Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME);
@ -86,12 +86,9 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla
proxy->blockedPaths().insert(file); proxy->blockedPaths().insert(file);
} }
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get()); for (auto& resourceModel : instance->resourceLists())
if (mcInstance) { if (resourceModel->indexDir().exists())
const QDir index = mcInstance->loaderModList()->indexDir(); proxy->ignoreFilesWithPath().insert(root.relativeFilePath(resourceModel->indexDir().absolutePath()));
if (index.exists())
proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath()));
}
ui->files->setModel(proxy); ui->files->setModel(proxy);
ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot())));

View File

@ -18,6 +18,7 @@
#pragma once #pragma once
#include <minecraft/MinecraftInstance.h>
#include <QDialog> #include <QDialog>
#include "BaseInstance.h" #include "BaseInstance.h"
#include "FastFileIconProvider.h" #include "FastFileIconProvider.h"
@ -32,7 +33,7 @@ class ExportPackDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit ExportPackDialog(InstancePtr instance, explicit ExportPackDialog(const MinecraftInstancePtr& instance,
QWidget* parent = nullptr, QWidget* parent = nullptr,
ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH); ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH);
~ExportPackDialog(); ~ExportPackDialog();
@ -41,7 +42,7 @@ class ExportPackDialog : public QDialog {
void validate(); void validate();
private: private:
const InstancePtr instance; const MinecraftInstancePtr instance;
Ui::ExportPackDialog* ui; Ui::ExportPackDialog* ui;
FileIgnoreProxy* proxy; FileIgnoreProxy* proxy;
FastFileIconProvider icons; FastFileIconProvider icons;