mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 01:54:20 +03:00
Merge effa8bedb1 into 9c2c641531
This commit is contained in:
commit
48f4ab6e94
43 changed files with 1277 additions and 1124 deletions
|
|
@ -77,8 +77,8 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
|||
auto action = Net::ApiDownload::makeFile(m_pack_version.downloadUrl, m_pack_model->dir().absoluteFilePath(getFilename()),
|
||||
Net::Download::Option::NoOptions,
|
||||
createModrinthMeta(m_pack_model->instance(), std::move(downloadReason)));
|
||||
if (!m_pack_version.hash_type.isEmpty() && !m_pack_version.hash.isEmpty()) {
|
||||
switch (Hashing::algorithmFromString(m_pack_version.hash_type)) {
|
||||
if (!m_pack_version.hashType.isEmpty() && !m_pack_version.hash.isEmpty()) {
|
||||
switch (Hashing::algorithmFromString(m_pack_version.hashType)) {
|
||||
case Hashing::Algorithm::Md4:
|
||||
action->addValidator(new Net::ChecksumValidator(QCryptographicHash::Algorithm::Md4, m_pack_version.hash));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -21,267 +21,35 @@
|
|||
#include <QDebug>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "Json.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
|
||||
static Version mcVersion(BaseInstance* inst)
|
||||
namespace {
|
||||
Version mcVersion(BaseInstance* inst)
|
||||
{
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion();
|
||||
}
|
||||
|
||||
static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst)
|
||||
{
|
||||
return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getSupportedModLoaders().value();
|
||||
}
|
||||
|
||||
static bool checkDependencies(std::shared_ptr<GetModDependenciesTask::PackDependency> sel,
|
||||
Version mcVersion,
|
||||
bool checkDependencies(const std::shared_ptr<GetModDependenciesTask::PackDependency>& sel,
|
||||
const Version& mcVersion,
|
||||
ModPlatform::ModLoaderTypes loaders)
|
||||
{
|
||||
return (sel->pack->versions.isEmpty() || sel->version.mcVersion.contains(mcVersion.toString())) &&
|
||||
(!loaders || !sel->version.loaders || sel->version.loaders & loaders);
|
||||
}
|
||||
|
||||
GetModDependenciesTask::GetModDependenciesTask(BaseInstance* instance,
|
||||
ModFolderModel* folder,
|
||||
QList<std::shared_ptr<PackDependency>> selected)
|
||||
: SequentialTask(tr("Get dependencies")), m_selected(selected), m_version(mcVersion(instance)), m_loaderType(mcLoaders(instance))
|
||||
{
|
||||
for (auto mod : folder->allMods()) {
|
||||
m_mods_file_names << mod->fileinfo().fileName();
|
||||
if (auto meta = mod->metadata(); meta)
|
||||
m_mods.append(meta);
|
||||
}
|
||||
prepare();
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::prepare()
|
||||
{
|
||||
for (auto sel : m_selected) {
|
||||
if (checkDependencies(sel, m_version, m_loaderType))
|
||||
for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
if (auto isQuilt = (m_loaderType & ModPlatform::Quilt) != 0U; isQuilt || (m_loaderType & ModPlatform::Fabric) != 0U) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](const auto& o) {
|
||||
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
|
||||
});
|
||||
if (over != overide.cend()) {
|
||||
return { .addonId = isQuilt ? over->quilt : over->fabric, .type = dep.type, .version = "" };
|
||||
}
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
||||
QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
QList<ModPlatform::Dependency> c_dependencies;
|
||||
for (auto ver_dep : version.dependencies) {
|
||||
if (ver_dep.type != ModPlatform::DependencyType::REQUIRED) {
|
||||
continue;
|
||||
}
|
||||
ver_dep = getOverride(ver_dep, providerName);
|
||||
auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty();
|
||||
if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(),
|
||||
[&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) {
|
||||
return isOnlyVersion ? i.version == ver_dep.version : i.addonId == ver_dep.addonId;
|
||||
});
|
||||
dep != c_dependencies.end()) {
|
||||
continue; // check the current dependency list
|
||||
}
|
||||
|
||||
if (auto dep = std::find_if(m_selected.begin(), m_selected.end(),
|
||||
[&ver_dep, providerName, isOnlyVersion](const std::shared_ptr<PackDependency>& i) {
|
||||
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.version
|
||||
: i->pack->addonId == ver_dep.addonId);
|
||||
});
|
||||
dep != m_selected.end()) {
|
||||
continue; // check the selected versions
|
||||
}
|
||||
|
||||
if (auto dep = std::find_if(m_mods.begin(), m_mods.end(),
|
||||
[&ver_dep, providerName, isOnlyVersion](const std::shared_ptr<Metadata::ModStruct>& i) {
|
||||
return i->provider == providerName &&
|
||||
(isOnlyVersion ? i->file_id == ver_dep.version : i->project_id == ver_dep.addonId);
|
||||
});
|
||||
dep != m_mods.end()) {
|
||||
continue; // check the existing mods
|
||||
}
|
||||
|
||||
if (auto dep = std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(),
|
||||
[&ver_dep, providerName, isOnlyVersion](const std::shared_ptr<PackDependency>& i) {
|
||||
return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.addonId
|
||||
: i->pack->addonId == ver_dep.addonId);
|
||||
});
|
||||
dep != m_pack_dependencies.end()) { // check loaded dependencies
|
||||
continue;
|
||||
}
|
||||
|
||||
c_dependencies.append(ver_dep);
|
||||
}
|
||||
return c_dependencies;
|
||||
}
|
||||
|
||||
Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep)
|
||||
{
|
||||
auto provider = pDep->pack->provider;
|
||||
auto [info, responseInfo] = getAPI(provider)->getProject(pDep->pack->addonId.toString());
|
||||
connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
qDebug() << *responseInfo;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = provider == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
|
||||
: Json::requireObject(doc);
|
||||
|
||||
getAPI(provider)->loadIndexedPack(*pDep->pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod info:" << e.cause();
|
||||
}
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName,
|
||||
int level)
|
||||
{
|
||||
auto pDep = std::make_shared<PackDependency>();
|
||||
pDep->dependency = dep;
|
||||
pDep->pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pDep->pack->addonId = dep.addonId;
|
||||
pDep->pack->provider = providerName;
|
||||
|
||||
m_pack_dependencies.append(pDep);
|
||||
|
||||
auto provider = providerName;
|
||||
|
||||
auto tasks = makeShared<SequentialTask>(
|
||||
QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString()));
|
||||
|
||||
if (!dep.addonId.toString().isEmpty()) {
|
||||
tasks->addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
|
||||
ResourceAPI::DependencySearchArgs args = {
|
||||
.dependency = dep, .mcVersion = m_version, .loader = m_loaderType, .includeChangelog = true
|
||||
};
|
||||
ResourceAPI::Callback<ModPlatform::IndexedVersion> callbacks;
|
||||
callbacks.on_fail = [](const QString& reason, int) {
|
||||
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
|
||||
};
|
||||
callbacks.on_succeed = [dep, provider, pDep, level, this](auto& pack) {
|
||||
pDep->version = pack;
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||
[dep, provider](const auto& o) { return o.provider == provider && dep.addonId == o.quilt; });
|
||||
if (over != overide.cend()) {
|
||||
removePack(dep.addonId);
|
||||
addTask(prepareDependencyTask({ .addonId = over->fabric, .type = dep.type, .version = "" }, provider, level));
|
||||
return;
|
||||
}
|
||||
}
|
||||
removePack(dep.addonId);
|
||||
return;
|
||||
}
|
||||
pDep->version.is_currently_selected = true;
|
||||
pDep->pack->versions = { pDep->version };
|
||||
pDep->pack->versionsLoaded = true;
|
||||
|
||||
if (level == 0) {
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Dependency cycle exceeded";
|
||||
return;
|
||||
}
|
||||
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||
pDep->pack->addonId = pDep->version.addonId;
|
||||
auto dep_ = getOverride({ .addonId = pDep->version.addonId, .type = pDep->dependency.type, .version = "" }, provider);
|
||||
if (dep_.addonId != pDep->version.addonId) {
|
||||
removePack(pDep->version.addonId);
|
||||
addTask(prepareDependencyTask(dep_, provider, level));
|
||||
} else {
|
||||
addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
}
|
||||
if (isLocalyInstalled(pDep)) {
|
||||
removePack(pDep->version.addonId);
|
||||
return;
|
||||
}
|
||||
for (const auto& dep_ : getDependenciesForVersion(pDep->version, provider)) {
|
||||
addTask(prepareDependencyTask(dep_, provider, level - 1));
|
||||
}
|
||||
};
|
||||
|
||||
auto version = getAPI(provider)->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||
tasks->addTask(version);
|
||||
return tasks;
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::removePack(const QVariant& addonId)
|
||||
{
|
||||
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||
m_pack_dependencies.removeIf(pred);
|
||||
#else
|
||||
for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();)
|
||||
if (pred(*it))
|
||||
it = m_pack_dependencies.erase(it);
|
||||
else
|
||||
++it;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto GetModDependenciesTask::getExtraInfo() -> QHash<QString, PackDependencyExtraInfo>
|
||||
{
|
||||
QHash<QString, PackDependencyExtraInfo> rby;
|
||||
auto fullList = m_selected + m_pack_dependencies;
|
||||
for (auto& mod : fullList) {
|
||||
auto addonId = mod->pack->addonId;
|
||||
auto provider = mod->pack->provider;
|
||||
auto version = mod->version.fileId;
|
||||
auto req = QStringList();
|
||||
for (auto& smod : fullList) {
|
||||
if (provider != smod->pack->provider)
|
||||
continue;
|
||||
auto deps = smod->version.dependencies;
|
||||
if (auto dep = std::find_if(deps.begin(), deps.end(),
|
||||
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||
? version == d.version
|
||||
: d.addonId == addonId);
|
||||
});
|
||||
dep != deps.end()) {
|
||||
req.append(smod->pack->name);
|
||||
}
|
||||
}
|
||||
rby[addonId.toString()] = { maybeInstalled(mod), req };
|
||||
}
|
||||
return rby;
|
||||
(!loaders || !sel->version.loaders || (sel->version.loaders & loaders) != 0U);
|
||||
}
|
||||
|
||||
// super lax compare (but not fuzzy)
|
||||
|
|
@ -289,11 +57,13 @@ auto GetModDependenciesTask::getExtraInfo() -> QHash<QString, PackDependencyExtr
|
|||
// convert all speratores to whitespace
|
||||
// simplify sequence of internal whitespace to a single space
|
||||
// efectivly compare two strings ignoring all separators and case
|
||||
auto laxCompare = [](QString fsfilename, QString metadataFilename, bool excludeDigits = false) {
|
||||
bool laxCompare(const QString& fsfilename, const QString& metadataFilename, bool excludeDigits = false)
|
||||
{
|
||||
// allowed character seperators
|
||||
QList<QChar> allowedSeperators = { '-', '+', '.', '_' };
|
||||
if (excludeDigits)
|
||||
if (excludeDigits) {
|
||||
allowedSeperators.append({ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
|
||||
}
|
||||
|
||||
// copy in lowercase
|
||||
auto fsName = fsfilename.toLower();
|
||||
|
|
@ -312,28 +82,295 @@ auto laxCompare = [](QString fsfilename, QString metadataFilename, bool excludeD
|
|||
return fsName.compare(metaName) == 0;
|
||||
};
|
||||
|
||||
bool GetModDependenciesTask::isLocalyInstalled(std::shared_ptr<PackDependency> pDep)
|
||||
} // namespace
|
||||
|
||||
GetModDependenciesTask::GetModDependenciesTask(BaseInstance* instance,
|
||||
ModFolderModel* folder,
|
||||
QList<std::shared_ptr<PackDependency>> selected)
|
||||
: SequentialTask(tr("Get dependencies"))
|
||||
, m_selected(std::move(selected))
|
||||
, m_version(mcVersion(instance))
|
||||
, m_loaderType(mcLoaders(instance))
|
||||
{
|
||||
for (auto* mod : folder->allMods()) {
|
||||
m_modsFileNames << mod->fileinfo().fileName();
|
||||
if (auto meta = mod->metadata(); meta) {
|
||||
m_mods.append(meta);
|
||||
}
|
||||
}
|
||||
|
||||
auto* mInstance = dynamic_cast<MinecraftInstance*>(instance);
|
||||
if (mInstance) {
|
||||
for (auto* model : mInstance->resourceLists()) {
|
||||
if (model) {
|
||||
for (auto* mod : model->allResources()) { // only append meta
|
||||
if (auto meta = mod->metadata(); meta) {
|
||||
m_mods.append(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::prepare()
|
||||
{
|
||||
for (const auto& sel : m_selected) {
|
||||
if (checkDependencies(sel, m_version, m_loaderType)) {
|
||||
for (const auto& dep : getDependenciesForVersion(sel->version, sel->pack->provider)) {
|
||||
addTask(prepareDependencyTask(dep, sel->pack->provider, 20));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
if (auto isQuilt = (m_loaderType & ModPlatform::Quilt) != 0U; isQuilt || (m_loaderType & ModPlatform::Fabric) != 0U) {
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::ranges::find_if(overide, [dep, providerName, isQuilt](const auto& o) {
|
||||
return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt);
|
||||
});
|
||||
if (over != overide.cend()) {
|
||||
return { .addonId = isQuilt ? over->quilt : over->fabric, .type = dep.type, .version = "" };
|
||||
}
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
||||
QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version,
|
||||
const ModPlatform::ResourceProvider providerName)
|
||||
{
|
||||
QList<ModPlatform::Dependency> cDependencies;
|
||||
for (auto verDep : version.dependencies) {
|
||||
if (verDep.type != ModPlatform::DependencyType::REQUIRED) {
|
||||
continue;
|
||||
}
|
||||
verDep = getOverride(verDep, providerName);
|
||||
auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && verDep.addonId.toString().isEmpty();
|
||||
if (auto dep = std::ranges::find_if(cDependencies,
|
||||
[&verDep, isOnlyVersion](const ModPlatform::Dependency& i) {
|
||||
return isOnlyVersion ? i.version == verDep.version : i.addonId == verDep.addonId;
|
||||
});
|
||||
dep != cDependencies.end()) {
|
||||
continue; // check the current dependency list
|
||||
}
|
||||
|
||||
if (auto dep =
|
||||
std::ranges::find_if(m_selected,
|
||||
[&verDep, providerName, isOnlyVersion](const std::shared_ptr<PackDependency>& i) {
|
||||
return i->pack->provider == providerName &&
|
||||
(isOnlyVersion ? i->version.version == verDep.version : i->pack->addonId == verDep.addonId);
|
||||
});
|
||||
dep != m_selected.end()) {
|
||||
continue; // check the selected versions
|
||||
}
|
||||
|
||||
if (auto dep = std::ranges::find_if(m_mods,
|
||||
[&verDep, providerName, isOnlyVersion](const std::shared_ptr<Metadata::ModStruct>& i) {
|
||||
return i->provider == providerName &&
|
||||
(isOnlyVersion ? i->file_id == verDep.version : i->project_id == verDep.addonId);
|
||||
});
|
||||
dep != m_mods.end()) {
|
||||
continue; // check the existing mods
|
||||
}
|
||||
|
||||
if (auto dep =
|
||||
std::ranges::find_if(m_packDependencies,
|
||||
[&verDep, providerName, isOnlyVersion](const std::shared_ptr<PackDependency>& i) {
|
||||
return i->pack->provider == providerName &&
|
||||
(isOnlyVersion ? i->version.version == verDep.addonId : i->pack->addonId == verDep.addonId);
|
||||
});
|
||||
dep != m_packDependencies.end()) { // check loaded dependencies
|
||||
continue;
|
||||
}
|
||||
|
||||
cDependencies.append(verDep);
|
||||
}
|
||||
return cDependencies;
|
||||
}
|
||||
|
||||
Task::Ptr GetModDependenciesTask::getProjectInfoTask(const std::shared_ptr<PackDependency>& pDep)
|
||||
{
|
||||
auto provider = pDep->pack->provider;
|
||||
auto [info, responseInfo] = getAPI(provider)->getProject(pDep->pack->addonId.toString());
|
||||
connect(info.get(), &NetJob::succeeded, [this, responseInfo, provider, pDep] {
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qWarning() << "Error while parsing JSON response for mod info at" << parseError.offset << "reason:" << parseError.errorString();
|
||||
qDebug() << *responseInfo;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = provider == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data")
|
||||
: Json::requireObject(doc);
|
||||
|
||||
getAPI(provider)->loadIndexedPack(*pDep->pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
removePack(pDep->pack->addonId);
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading mod info:" << e.cause();
|
||||
}
|
||||
});
|
||||
QObject::connect(info.get(), &NetJob::failed, [this, info, pDep] {
|
||||
removePack(pDep->pack->addonId);
|
||||
m_failed.remove(info.get());
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep,
|
||||
const ModPlatform::ResourceProvider providerName,
|
||||
int level)
|
||||
{
|
||||
auto pDep = std::make_shared<PackDependency>();
|
||||
pDep->dependency = dep;
|
||||
pDep->pack = std::make_shared<ModPlatform::IndexedPack>();
|
||||
pDep->pack->addonId = dep.addonId;
|
||||
pDep->pack->provider = providerName;
|
||||
|
||||
m_packDependencies.append(pDep);
|
||||
|
||||
auto provider = providerName;
|
||||
|
||||
auto tasks = makeShared<SequentialTask>(
|
||||
QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString()));
|
||||
|
||||
if (!dep.addonId.toString().isEmpty()) {
|
||||
tasks->addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
|
||||
ResourceAPI::DependencySearchArgs args = {
|
||||
.dependency = dep, .mcVersion = m_version, .loader = m_loaderType, .includeChangelog = true
|
||||
};
|
||||
ResourceAPI::Callback<ModPlatform::IndexedVersion> callbacks;
|
||||
callbacks.onFail = [](const QString& reason, int) {
|
||||
qCritical() << tr("A network error occurred. Could not load project dependencies:%1").arg(reason);
|
||||
};
|
||||
callbacks.onSucceed = [dep, provider, pDep, level, this](auto& pack) {
|
||||
pDep->version = pack;
|
||||
if (!pDep->version.addonId.isValid()) {
|
||||
if (m_loaderType & ModPlatform::Quilt) { // falback for quilt
|
||||
auto overide = ModPlatform::getOverrideDeps();
|
||||
auto over = std::find_if(overide.cbegin(), overide.cend(),
|
||||
[dep, provider](const auto& o) { return o.provider == provider && dep.addonId == o.quilt; });
|
||||
if (over != overide.cend()) {
|
||||
removePack(dep.addonId);
|
||||
addTask(prepareDependencyTask({ .addonId = over->fabric, .type = dep.type, .version = "" }, provider, level));
|
||||
return;
|
||||
}
|
||||
}
|
||||
removePack(dep.addonId);
|
||||
return;
|
||||
}
|
||||
pDep->version.isCurrentlySelected = true;
|
||||
pDep->pack->versions = { pDep->version };
|
||||
pDep->pack->versionsLoaded = true;
|
||||
|
||||
if (level == 0) {
|
||||
removePack(dep.addonId);
|
||||
qWarning() << "Dependency cycle exceeded";
|
||||
return;
|
||||
}
|
||||
if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) {
|
||||
pDep->pack->addonId = pDep->version.addonId;
|
||||
auto overrideDep = getOverride({ .addonId = pDep->version.addonId, .type = pDep->dependency.type, .version = "" }, provider);
|
||||
if (overrideDep.addonId != pDep->version.addonId) {
|
||||
removePack(pDep->version.addonId);
|
||||
addTask(prepareDependencyTask(overrideDep, provider, level));
|
||||
} else {
|
||||
addTask(getProjectInfoTask(pDep));
|
||||
}
|
||||
}
|
||||
if (isLocalyInstalled(pDep)) {
|
||||
removePack(pDep->version.addonId);
|
||||
return;
|
||||
}
|
||||
for (const auto& dependency : getDependenciesForVersion(pDep->version, provider)) {
|
||||
addTask(prepareDependencyTask(dependency, provider, level - 1));
|
||||
}
|
||||
};
|
||||
|
||||
auto version = getAPI(provider)->getDependencyVersion(std::move(args), std::move(callbacks));
|
||||
QObject::connect(version.get(), &NetJob::failed, [this, version, pDep] {
|
||||
removePack(pDep->pack->addonId);
|
||||
m_failed.remove(version.get());
|
||||
});
|
||||
tasks->addTask(version);
|
||||
return tasks;
|
||||
}
|
||||
|
||||
void GetModDependenciesTask::removePack(const QVariant& addonId)
|
||||
{
|
||||
auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; };
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
|
||||
m_packDependencies.removeIf(pred);
|
||||
#else
|
||||
for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();)
|
||||
if (pred(*it))
|
||||
it = m_pack_dependencies.erase(it);
|
||||
else
|
||||
++it;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto GetModDependenciesTask::getExtraInfo() -> QHash<QString, PackDependencyExtraInfo>
|
||||
{
|
||||
QHash<QString, PackDependencyExtraInfo> rby;
|
||||
auto fullList = m_selected + m_packDependencies;
|
||||
for (auto& mod : fullList) {
|
||||
auto addonId = mod->pack->addonId;
|
||||
auto provider = mod->pack->provider;
|
||||
auto version = mod->version.fileId;
|
||||
auto req = QStringList();
|
||||
for (auto& smod : fullList) {
|
||||
if (provider != smod->pack->provider) {
|
||||
continue;
|
||||
}
|
||||
auto deps = smod->version.dependencies;
|
||||
if (auto dep =
|
||||
std::ranges::find_if(deps,
|
||||
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||
? version == d.version
|
||||
: d.addonId == addonId);
|
||||
});
|
||||
dep != deps.end()) {
|
||||
req.append(smod->pack->name);
|
||||
}
|
||||
}
|
||||
rby[addonId.toString()] = { .maybeInstalled = maybeInstalled(mod), .requiredBy = req };
|
||||
}
|
||||
return rby;
|
||||
}
|
||||
|
||||
bool GetModDependenciesTask::isLocalyInstalled(const std::shared_ptr<PackDependency>& pDep)
|
||||
{
|
||||
return pDep->version.fileName.isEmpty() ||
|
||||
|
||||
std::find_if(m_selected.begin(), m_selected.end(),
|
||||
[pDep](std::shared_ptr<PackDependency> i) {
|
||||
std::ranges::find_if(m_selected,
|
||||
[pDep](const std::shared_ptr<PackDependency>& i) {
|
||||
return !i->version.fileName.isEmpty() && laxCompare(i->version.fileName, pDep->version.fileName);
|
||||
}) != m_selected.end() || // check the selected versions
|
||||
|
||||
std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(),
|
||||
[pDep](QString i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName); }) !=
|
||||
m_mods_file_names.end() || // check the existing mods
|
||||
std::ranges::find_if(m_modsFileNames,
|
||||
[pDep](const QString& i) { return !i.isEmpty() && laxCompare(i, pDep->version.fileName); }) !=
|
||||
m_modsFileNames.end() || // check the existing mods
|
||||
|
||||
std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), [pDep](std::shared_ptr<PackDependency> i) {
|
||||
std::ranges::find_if(m_packDependencies, [pDep](const std::shared_ptr<PackDependency>& i) {
|
||||
return pDep->pack->addonId != i->pack->addonId && !i->version.fileName.isEmpty() &&
|
||||
laxCompare(pDep->version.fileName, i->version.fileName);
|
||||
}) != m_pack_dependencies.end(); // check loaded dependencies
|
||||
}) != m_packDependencies.end(); // check loaded dependencies
|
||||
}
|
||||
|
||||
bool GetModDependenciesTask::maybeInstalled(std::shared_ptr<PackDependency> pDep)
|
||||
bool GetModDependenciesTask::maybeInstalled(const std::shared_ptr<PackDependency>& pDep)
|
||||
{
|
||||
return std::find_if(m_mods_file_names.begin(), m_mods_file_names.end(), [pDep](QString i) {
|
||||
return std::ranges::find_if(m_modsFileNames, [pDep](const QString& i) {
|
||||
return !i.isEmpty() && laxCompare(i, pDep->version.fileName, true);
|
||||
}) != m_mods_file_names.end(); // check the existing mods
|
||||
}) != m_modsFileNames.end(); // check the existing mods
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <QDir>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -49,13 +48,13 @@ class GetModDependenciesTask : public SequentialTask {
|
|||
};
|
||||
|
||||
struct PackDependencyExtraInfo {
|
||||
bool maybe_installed{};
|
||||
QStringList required_by;
|
||||
bool maybeInstalled{};
|
||||
QStringList requiredBy;
|
||||
};
|
||||
|
||||
explicit GetModDependenciesTask(BaseInstance* instance, ModFolderModel* folder, QList<std::shared_ptr<PackDependency>> selected);
|
||||
|
||||
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; }
|
||||
auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_packDependencies; }
|
||||
QHash<QString, PackDependencyExtraInfo> getExtraInfo();
|
||||
|
||||
private:
|
||||
|
|
@ -72,18 +71,18 @@ class GetModDependenciesTask : public SequentialTask {
|
|||
QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&,
|
||||
ModPlatform::ResourceProvider providerName);
|
||||
void prepare();
|
||||
Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep);
|
||||
Task::Ptr getProjectInfoTask(const std::shared_ptr<PackDependency>& pDep);
|
||||
ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, ModPlatform::ResourceProvider providerName);
|
||||
void removePack(const QVariant& addonId);
|
||||
bool isLocalyInstalled(const std::shared_ptr<PackDependency>& pDep);
|
||||
|
||||
bool isLocalyInstalled(std::shared_ptr<PackDependency> pDep);
|
||||
bool maybeInstalled(std::shared_ptr<PackDependency> pDep);
|
||||
bool maybeInstalled(const std::shared_ptr<PackDependency>& pDep);
|
||||
|
||||
private:
|
||||
QList<std::shared_ptr<PackDependency>> m_pack_dependencies;
|
||||
QList<std::shared_ptr<PackDependency>> m_packDependencies;
|
||||
QList<std::shared_ptr<Metadata::ModStruct>> m_mods;
|
||||
QList<std::shared_ptr<PackDependency>> m_selected;
|
||||
QStringList m_mods_file_names;
|
||||
QStringList m_modsFileNames;
|
||||
|
||||
Version m_version;
|
||||
ModPlatform::ModLoaderTypes m_loaderType;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "Json.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceUpdateTask.h"
|
||||
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
|
|
@ -15,44 +14,47 @@
|
|||
#include "modplatform/helpers/HashUtils.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
|
||||
static ModrinthAPI modrinth_api;
|
||||
static FlameAPI flame_api;
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(Resource* resource, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(), m_indexDir(dir), m_provider(prov), m_hashingTask(nullptr), m_currentTask(nullptr)
|
||||
EnsureMetadataTask::EnsureMetadataTask(Resource* resource, const QDir& dir, ModPlatform::ResourceProvider prov)
|
||||
: m_indexDir(dir), m_provider(prov), m_hashingTask(nullptr), m_currentTask(nullptr)
|
||||
{
|
||||
auto hashTask = createNewHash(resource);
|
||||
if (!hashTask)
|
||||
if (!hashTask) {
|
||||
return;
|
||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); });
|
||||
}
|
||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, resource](const QString& hash) { m_resources.insert(hash, resource); });
|
||||
connect(hashTask.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); });
|
||||
m_hashingTask = hashTask;
|
||||
}
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(QList<Resource*>& resources, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(), m_indexDir(dir), m_provider(prov), m_currentTask(nullptr)
|
||||
EnsureMetadataTask::EnsureMetadataTask(QList<Resource*>& resources, const QDir& dir, ModPlatform::ResourceProvider prov)
|
||||
: m_indexDir(dir), m_provider(prov), m_currentTask(nullptr)
|
||||
{
|
||||
auto hashTask = makeShared<ConcurrentTask>("MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
m_hashingTask = hashTask;
|
||||
auto cHashTask = makeShared<ConcurrentTask>("MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
m_hashingTask = cHashTask;
|
||||
for (auto* resource : resources) {
|
||||
auto hash_task = createNewHash(resource);
|
||||
if (!hash_task)
|
||||
auto hashTask = createNewHash(resource);
|
||||
if (!hashTask) {
|
||||
continue;
|
||||
connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); });
|
||||
connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); });
|
||||
hashTask->addTask(hash_task);
|
||||
}
|
||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady,
|
||||
[this, resource](const QString& hash) { m_resources.insert(hash, resource); });
|
||||
connect(hashTask.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); });
|
||||
cHashTask->addTask(hashTask);
|
||||
}
|
||||
}
|
||||
|
||||
EnsureMetadataTask::EnsureMetadataTask(QHash<QString, Resource*>& resources, QDir dir, ModPlatform::ResourceProvider prov)
|
||||
: Task(), m_resources(resources), m_indexDir(dir), m_provider(prov), m_currentTask(nullptr)
|
||||
EnsureMetadataTask::EnsureMetadataTask(QHash<QString, Resource*>& resources, const QDir& dir, ModPlatform::ResourceProvider prov)
|
||||
: m_resources(resources), m_indexDir(dir), m_provider(prov), m_currentTask(nullptr)
|
||||
{}
|
||||
|
||||
Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Resource* resource)
|
||||
{
|
||||
if (!resource || !resource->valid() || resource->type() == ResourceType::FOLDER)
|
||||
if (!resource || !resource->valid() || resource->type() == ResourceType::FOLDER) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Hashing::createHasher(resource->fileinfo().absoluteFilePath(), m_provider);
|
||||
}
|
||||
|
|
@ -63,8 +65,9 @@ QString EnsureMetadataTask::getExistingHash(Resource* resource)
|
|||
// (linear on the number of mods vs. linear on the size of the mod's JAR)
|
||||
auto it = m_resources.keyValueBegin();
|
||||
while (it != m_resources.keyValueEnd()) {
|
||||
if ((*it).second == resource)
|
||||
if ((*it).second == resource) {
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
|
|
@ -80,10 +83,11 @@ QString EnsureMetadataTask::getExistingHash(Resource* resource)
|
|||
bool EnsureMetadataTask::abort()
|
||||
{
|
||||
// Prevent sending signals to a dead object
|
||||
disconnect(this, 0, 0, 0);
|
||||
QObject::disconnect(this, nullptr, nullptr, nullptr);
|
||||
|
||||
if (m_currentTask)
|
||||
if (m_currentTask) {
|
||||
return m_currentTask->abort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -111,70 +115,74 @@ void EnsureMetadataTask::executeTask()
|
|||
}
|
||||
}
|
||||
|
||||
Task::Ptr version_task;
|
||||
Task::Ptr versionTask;
|
||||
|
||||
switch (m_provider) {
|
||||
case (ModPlatform::ResourceProvider::MODRINTH):
|
||||
version_task = modrinthVersionsTask();
|
||||
versionTask = modrinthVersionsTask();
|
||||
break;
|
||||
case (ModPlatform::ResourceProvider::FLAME):
|
||||
version_task = flameVersionsTask();
|
||||
versionTask = flameVersionsTask();
|
||||
break;
|
||||
}
|
||||
|
||||
auto invalidade_leftover = [this] {
|
||||
for (auto resource = m_resources.constBegin(); resource != m_resources.constEnd(); resource++)
|
||||
auto invalidadeLeftover = [this] {
|
||||
for (auto resource = m_resources.constBegin(); resource != m_resources.constEnd(); resource++) {
|
||||
emitFail(resource.value(), resource.key(), RemoveFromList::No);
|
||||
}
|
||||
m_resources.clear();
|
||||
|
||||
emitSucceeded();
|
||||
};
|
||||
|
||||
connect(version_task.get(), &Task::finished, this, [this, invalidade_leftover] {
|
||||
Task::Ptr project_task;
|
||||
connect(versionTask.get(), &Task::finished, this, [this, invalidadeLeftover] {
|
||||
Task::Ptr projectTask;
|
||||
|
||||
switch (m_provider) {
|
||||
case (ModPlatform::ResourceProvider::MODRINTH):
|
||||
project_task = modrinthProjectsTask();
|
||||
projectTask = modrinthProjectsTask();
|
||||
break;
|
||||
case (ModPlatform::ResourceProvider::FLAME):
|
||||
project_task = flameProjectsTask();
|
||||
projectTask = flameProjectsTask();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!project_task) {
|
||||
invalidade_leftover();
|
||||
if (!projectTask) {
|
||||
invalidadeLeftover();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(project_task.get(), &Task::finished, this, [this, invalidade_leftover, project_task] {
|
||||
invalidade_leftover();
|
||||
project_task->deleteLater();
|
||||
if (m_currentTask)
|
||||
connect(projectTask.get(), &Task::finished, this, [this, invalidadeLeftover, projectTask] {
|
||||
invalidadeLeftover();
|
||||
projectTask->deleteLater();
|
||||
if (m_currentTask) {
|
||||
m_currentTask.reset();
|
||||
}
|
||||
});
|
||||
connect(project_task.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed);
|
||||
connect(projectTask.get(), &Task::failed, this, &EnsureMetadataTask::emitFailed);
|
||||
|
||||
m_currentTask = project_task;
|
||||
project_task->start();
|
||||
m_currentTask = projectTask;
|
||||
projectTask->start();
|
||||
});
|
||||
|
||||
if (m_resources.size() > 1)
|
||||
if (m_resources.size() > 1) {
|
||||
setStatus(tr("Requesting metadata information from %1...").arg(ModPlatform::ProviderCapabilities::readableName(m_provider)));
|
||||
else if (!m_resources.empty())
|
||||
} else if (!m_resources.empty()) {
|
||||
setStatus(tr("Requesting metadata information from %1 for '%2'...")
|
||||
.arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_resources.begin().value()->name()));
|
||||
}
|
||||
|
||||
m_currentTask = version_task;
|
||||
version_task->start();
|
||||
m_currentTask = versionTask;
|
||||
versionTask->start();
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::emitReady(Resource* resource, QString key, RemoveFromList remove)
|
||||
{
|
||||
if (!resource) {
|
||||
qCritical() << "Tried to mark a null resource as ready.";
|
||||
if (!key.isEmpty())
|
||||
if (!key.isEmpty()) {
|
||||
m_resources.remove(key);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -183,8 +191,9 @@ void EnsureMetadataTask::emitReady(Resource* resource, QString key, RemoveFromLi
|
|||
emit metadataReady(resource);
|
||||
|
||||
if (remove == RemoveFromList::Yes) {
|
||||
if (key.isEmpty())
|
||||
if (key.isEmpty()) {
|
||||
key = getExistingHash(resource);
|
||||
}
|
||||
m_resources.remove(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -193,8 +202,9 @@ void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromLis
|
|||
{
|
||||
if (!resource) {
|
||||
qCritical() << "Tried to mark a null resource as failed.";
|
||||
if (!key.isEmpty())
|
||||
if (!key.isEmpty()) {
|
||||
m_resources.remove(key);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -203,8 +213,9 @@ void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromLis
|
|||
emit metadataFailed(resource);
|
||||
|
||||
if (remove == RemoveFromList::Yes) {
|
||||
if (key.isEmpty())
|
||||
if (key.isEmpty()) {
|
||||
key = getExistingHash(resource);
|
||||
}
|
||||
m_resources.remove(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -213,30 +224,31 @@ void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromLis
|
|||
|
||||
Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
||||
{
|
||||
auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
auto hashType = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first();
|
||||
|
||||
auto [ver_task, response] = modrinth_api.currentVersions(m_resources.keys(), hash_type);
|
||||
auto [verTask, response] = ModrinthAPI::currentVersions(m_resources.keys(), hashType);
|
||||
|
||||
// Prevents unfortunate timings when aborting the task
|
||||
if (!ver_task)
|
||||
if (!verTask) {
|
||||
return Task::Ptr{ nullptr };
|
||||
}
|
||||
|
||||
connect(ver_task.get(), &Task::succeeded, this, [this, response] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
connect(verTask.get(), &Task::succeeded, this, [this, response] {
|
||||
QJsonParseError parseError{};
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
failed(parse_error.errorString());
|
||||
failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto entries = Json::requireObject(doc);
|
||||
for (auto& hash : m_resources.keys()) {
|
||||
auto resource = m_resources.find(hash).value();
|
||||
auto* resource = m_resources.find(hash).value();
|
||||
try {
|
||||
auto entry = Json::requireObject(entries, hash);
|
||||
|
||||
|
|
@ -257,36 +269,38 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask()
|
|||
}
|
||||
});
|
||||
|
||||
return ver_task;
|
||||
return verTask;
|
||||
}
|
||||
|
||||
Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
||||
{
|
||||
QHash<QString, QString> addonIds;
|
||||
for (const auto& data : m_tempVersions)
|
||||
for (const auto& data : m_tempVersions) {
|
||||
addonIds.insert(data.addonId.toString(), data.hash);
|
||||
}
|
||||
|
||||
Task::Ptr proj_task;
|
||||
QByteArray* response;
|
||||
Task::Ptr projTask;
|
||||
QByteArray* response = nullptr;
|
||||
|
||||
if (addonIds.isEmpty()) {
|
||||
qWarning() << "No addonId found!";
|
||||
} else if (addonIds.size() == 1) {
|
||||
std::tie(proj_task, response) = modrinth_api.getProject(*addonIds.keyBegin());
|
||||
std::tie(projTask, response) = ModrinthAPI().getProject(*addonIds.keyBegin());
|
||||
} else {
|
||||
std::tie(proj_task, response) = modrinth_api.getProjects(addonIds.keys());
|
||||
std::tie(projTask, response) = ModrinthAPI().getProjects(addonIds.keys());
|
||||
}
|
||||
|
||||
// Prevents unfortunate timings when aborting the task
|
||||
if (!proj_task)
|
||||
if (!projTask) {
|
||||
return Task::Ptr{ nullptr };
|
||||
}
|
||||
|
||||
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
connect(projTask.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||
QJsonParseError parseError{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
|
@ -294,10 +308,11 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||
QJsonArray entries;
|
||||
|
||||
try {
|
||||
if (addonIds.size() == 1)
|
||||
if (addonIds.size() == 1) {
|
||||
entries = { doc.object() };
|
||||
else
|
||||
} else {
|
||||
entries = Json::requireArray(doc);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << doc;
|
||||
|
|
@ -307,9 +322,9 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||
ModPlatform::IndexedPack pack;
|
||||
|
||||
try {
|
||||
auto entry_obj = Json::requireObject(entry);
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
|
||||
Modrinth::loadIndexedPack(pack, entry_obj);
|
||||
Modrinth::loadIndexedPack(pack, entryObj);
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << doc;
|
||||
|
|
@ -320,13 +335,13 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||
|
||||
auto hash = addonIds.find(pack.addonId.toString()).value();
|
||||
|
||||
auto resource_iter = m_resources.find(hash);
|
||||
if (resource_iter == m_resources.end()) {
|
||||
auto resourceIter = m_resources.find(hash);
|
||||
if (resourceIter == m_resources.end()) {
|
||||
qWarning() << "Invalid project id from the API response.";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* resource = resource_iter.value();
|
||||
auto* resource = resourceIter.value();
|
||||
|
||||
setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name()));
|
||||
|
||||
|
|
@ -334,7 +349,7 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask()
|
|||
}
|
||||
});
|
||||
|
||||
return proj_task;
|
||||
return projTask;
|
||||
}
|
||||
|
||||
// Flame
|
||||
|
|
@ -345,42 +360,42 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
|||
fingerprints.push_back(murmur.toUInt());
|
||||
}
|
||||
|
||||
auto [ver_task, response] = flame_api.matchFingerprints(fingerprints);
|
||||
auto [verTask, response] = FlameAPI::matchFingerprints(fingerprints);
|
||||
|
||||
connect(ver_task.get(), &Task::succeeded, this, [this, response] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::CurrentVersions at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
connect(verTask.get(), &Task::succeeded, this, [this, response] {
|
||||
QJsonParseError parseError{};
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::CurrentVersions at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
failed(parse_error.errorString());
|
||||
failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto doc_obj = Json::requireObject(doc);
|
||||
auto data_obj = Json::requireObject(doc_obj, "data");
|
||||
auto data_arr = Json::requireArray(data_obj, "exactMatches");
|
||||
auto docObj = Json::requireObject(doc);
|
||||
auto dataObj = Json::requireObject(docObj, "data");
|
||||
auto dataArr = Json::requireArray(dataObj, "exactMatches");
|
||||
|
||||
if (data_arr.isEmpty()) {
|
||||
if (dataArr.isEmpty()) {
|
||||
qWarning() << "No matches found for fingerprint search!";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto match : data_arr) {
|
||||
auto match_obj = match.toObject();
|
||||
auto file_obj = match_obj["file"].toObject();
|
||||
for (auto match : dataArr) {
|
||||
auto matchObj = match.toObject();
|
||||
auto fileObj = matchObj["file"].toObject();
|
||||
|
||||
if (match_obj.isEmpty() || file_obj.isEmpty()) {
|
||||
if (matchObj.isEmpty() || fileObj.isEmpty()) {
|
||||
qWarning() << "Fingerprint match is empty!";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto fingerprint = QString::number(file_obj["fileFingerprint"].toInteger());
|
||||
auto fingerprint = QString::number(fileObj["fileFingerprint"].toInteger());
|
||||
auto resource = m_resources.find(fingerprint);
|
||||
if (resource == m_resources.end()) {
|
||||
qWarning() << "Invalid fingerprint from the API response.";
|
||||
|
|
@ -389,7 +404,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
|||
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*resource)->name()));
|
||||
|
||||
m_tempVersions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj));
|
||||
m_tempVersions.insert(fingerprint, FlameMod::loadIndexedPackVersion(fileObj));
|
||||
}
|
||||
|
||||
} catch (Json::JsonException& e) {
|
||||
|
|
@ -398,7 +413,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask()
|
|||
}
|
||||
});
|
||||
|
||||
return ver_task;
|
||||
return verTask;
|
||||
}
|
||||
|
||||
Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
||||
|
|
@ -408,56 +423,59 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
|||
if (m_tempVersions.contains(hash)) {
|
||||
auto data = m_tempVersions.find(hash).value();
|
||||
|
||||
auto id_str = data.addonId.toString();
|
||||
if (!id_str.isEmpty())
|
||||
auto idStr = data.addonId.toString();
|
||||
if (!idStr.isEmpty()) {
|
||||
addonIds.insert(data.addonId.toString(), hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task::Ptr proj_task;
|
||||
QByteArray* response;
|
||||
Task::Ptr projTask;
|
||||
QByteArray* response = nullptr;
|
||||
|
||||
if (addonIds.isEmpty()) {
|
||||
qWarning() << "No addonId found!";
|
||||
} else if (addonIds.size() == 1) {
|
||||
std::tie(proj_task, response) = flame_api.getProject(*addonIds.keyBegin());
|
||||
std::tie(projTask, response) = FlameAPI().getProject(*addonIds.keyBegin());
|
||||
} else {
|
||||
std::tie(proj_task, response) = flame_api.getProjects(addonIds.keys());
|
||||
std::tie(projTask, response) = FlameAPI().getProjects(addonIds.keys());
|
||||
}
|
||||
|
||||
// Prevents unfortunate timings when aborting the task
|
||||
if (!proj_task)
|
||||
if (!projTask) {
|
||||
return Task::Ptr{ nullptr };
|
||||
}
|
||||
|
||||
connect(proj_task.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame projects task at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
connect(projTask.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||
QJsonParseError parseError{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame projects task at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
QJsonArray entries;
|
||||
if (addonIds.size() == 1)
|
||||
if (addonIds.size() == 1) {
|
||||
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||
else
|
||||
} else {
|
||||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
}
|
||||
|
||||
for (auto entry : entries) {
|
||||
auto entry_obj = Json::requireObject(entry);
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
|
||||
auto id = QString::number(Json::requireInteger(entry_obj, "id"));
|
||||
auto id = QString::number(Json::requireInteger(entryObj, "id"));
|
||||
auto hash = addonIds.find(id).value();
|
||||
auto resource = m_resources.find(hash).value();
|
||||
auto* resource = m_resources.find(hash).value();
|
||||
|
||||
ModPlatform::IndexedPack pack;
|
||||
try {
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(resource->name()));
|
||||
|
||||
FlameMod::loadIndexedPack(pack, entry_obj);
|
||||
FlameMod::loadIndexedPack(pack, entryObj);
|
||||
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
|
|
@ -473,7 +491,7 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask()
|
|||
}
|
||||
});
|
||||
|
||||
return proj_task;
|
||||
return projTask;
|
||||
}
|
||||
|
||||
void EnsureMetadataTask::updateMetadata(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource)
|
||||
|
|
@ -481,8 +499,9 @@ void EnsureMetadataTask::updateMetadata(ModPlatform::IndexedPack& pack, ModPlatf
|
|||
try {
|
||||
// Prevent file name mismatch
|
||||
ver.fileName = resource->fileinfo().fileName();
|
||||
if (ver.fileName.endsWith(".disabled"))
|
||||
if (ver.fileName.endsWith(".disabled")) {
|
||||
ver.fileName.chop(9);
|
||||
}
|
||||
|
||||
auto task = makeShared<LocalResourceUpdateTask>(m_indexDir, pack, ver);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "ModIndex.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
|
||||
#include "minecraft/mod/Resource.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
#include <QDir>
|
||||
|
||||
class Mod;
|
||||
class QDir;
|
||||
#include "minecraft/mod/Resource.h"
|
||||
|
||||
class EnsureMetadataTask : public Task {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EnsureMetadataTask(Resource*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QList<Resource*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QHash<QString, Resource*>&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(Resource*, const QDir&, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QList<Resource*>&, const QDir&, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
EnsureMetadataTask(QHash<QString, Resource*>&, const QDir&, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH);
|
||||
|
||||
~EnsureMetadataTask() = default;
|
||||
~EnsureMetadataTask() override = default;
|
||||
|
||||
Task::Ptr getHashingTask() { return m_hashingTask; }
|
||||
|
||||
|
|
@ -37,7 +34,7 @@ class EnsureMetadataTask : public Task {
|
|||
Task::Ptr flameProjectsTask();
|
||||
|
||||
// Helpers
|
||||
enum class RemoveFromList { Yes, No };
|
||||
enum class RemoveFromList : std::uint8_t { Yes, No };
|
||||
void emitReady(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
void emitFail(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <compare>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "modplatform/ResourceType.h"
|
||||
|
||||
class QIODevice;
|
||||
|
||||
|
|
@ -113,27 +114,27 @@ struct IndexedVersion {
|
|||
QVariant addonId;
|
||||
QVariant fileId;
|
||||
QString version;
|
||||
QString version_number;
|
||||
IndexedVersionType version_type;
|
||||
QString versionNumber;
|
||||
IndexedVersionType versionType;
|
||||
QStringList mcVersion;
|
||||
QString downloadUrl;
|
||||
QString date;
|
||||
QString fileName;
|
||||
ModLoaderTypes loaders;
|
||||
QString hash_type;
|
||||
QString hashType;
|
||||
QString hash;
|
||||
bool is_preferred = true;
|
||||
bool isPreferred = true;
|
||||
QString changelog;
|
||||
QList<Dependency> dependencies;
|
||||
Side side = Side::NoSide; // this is for flame API
|
||||
|
||||
// For internal use, not provided by APIs
|
||||
bool is_currently_selected = false;
|
||||
bool isCurrentlySelected = false;
|
||||
|
||||
QString getVersionDisplayString() const
|
||||
{
|
||||
auto release_type = version_type.isValid() ? QString(" [%1]").arg(version_type.toString()) : "";
|
||||
auto versionStr = !version.contains(version_number) ? version_number : "";
|
||||
auto releaseType = versionType.isValid() ? QString(" [%1]").arg(versionType.toString()) : "";
|
||||
auto versionStr = !version.contains(versionNumber) ? versionNumber : "";
|
||||
QString gameVersion = "";
|
||||
for (const auto& v : mcVersion) {
|
||||
if (version.contains(v)) {
|
||||
|
|
@ -144,7 +145,7 @@ struct IndexedVersion {
|
|||
gameVersion = QObject::tr(" for %1").arg(v);
|
||||
}
|
||||
}
|
||||
return QString("%1%2 — %3%4").arg(version, gameVersion, versionStr, release_type);
|
||||
return QString("%1%2 — %3%4").arg(version, gameVersion, versionStr, releaseType);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -182,6 +183,8 @@ struct IndexedPack {
|
|||
bool extraDataLoaded = true;
|
||||
ExtraPackData extraData;
|
||||
|
||||
ResourceType resourceType = ResourceType::Unknown;
|
||||
|
||||
// For internal use, not provided by APIs
|
||||
bool isVersionSelected(int index) const
|
||||
{
|
||||
|
|
@ -189,7 +192,7 @@ struct IndexedPack {
|
|||
return false;
|
||||
}
|
||||
|
||||
return versions.at(index).is_currently_selected;
|
||||
return versions.at(index).isCurrentlySelected;
|
||||
}
|
||||
bool isAnyVersionSelected() const
|
||||
{
|
||||
|
|
@ -197,7 +200,7 @@ struct IndexedPack {
|
|||
return false;
|
||||
}
|
||||
|
||||
return std::any_of(versions.constBegin(), versions.constEnd(), [](const auto& v) { return v.is_currently_selected; });
|
||||
return std::any_of(versions.constBegin(), versions.constEnd(), [](const auto& v) { return v.isCurrentlySelected; });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "net/NetJob.h"
|
||||
|
|
@ -8,30 +10,30 @@
|
|||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback<QList<ModPlatform::IndexedPack::Ptr>>&& callbacks) const
|
||||
Task::Ptr ResourceAPI::searchProjects(const SearchArgs& args, const Callback<QList<ModPlatform::IndexedPack::Ptr>>& callbacks) const
|
||||
{
|
||||
auto search_url_optional = getSearchURL(args);
|
||||
if (!search_url_optional.has_value()) {
|
||||
callbacks.on_fail("Failed to create search URL", -1);
|
||||
auto searchUrlOptional = getSearchURL(args);
|
||||
if (!searchUrlOptional.has_value()) {
|
||||
callbacks.onFail("Failed to create search URL", -1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto search_url = search_url_optional.value();
|
||||
const auto& searchUrl = searchUrlOptional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Search").arg(debugName()), APPLICATION->network());
|
||||
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(search_url));
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(searchUrl));
|
||||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from" << debugName() << "at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from" << debugName() << "at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
callbacks.on_fail(parse_error.errorString(), -1);
|
||||
callbacks.onFail(parseError.errorString(), -1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -52,7 +54,7 @@ Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback<QList<ModPlatf
|
|||
}
|
||||
}
|
||||
|
||||
callbacks.on_succeed(newList);
|
||||
callbacks.onSucceed(newList);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
|
|
@ -60,40 +62,44 @@ Task::Ptr ResourceAPI::searchProjects(SearchArgs&& args, Callback<QList<ModPlatf
|
|||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = netJob.toWeakRef();
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
int networkErrorCode = -1;
|
||||
if (auto netJob = weak.lock()) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action)
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
if (auto* failedAction = netJob->getFailedActions().at(0); failedAction) {
|
||||
networkErrorCode = failedAction->replyStatusCode();
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
}
|
||||
callbacks.onFail(reason, networkErrorCode);
|
||||
});
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
if (callbacks.onAbort != nullptr) {
|
||||
callbacks.onAbort();
|
||||
}
|
||||
});
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const
|
||||
Task::Ptr ResourceAPI::getProjectVersions(const VersionSearchArgs& args,
|
||||
const Callback<QVector<ModPlatform::IndexedVersion>>& callbacks) const
|
||||
{
|
||||
auto versions_url_optional = getVersionsURL(args);
|
||||
if (!versions_url_optional.has_value())
|
||||
auto versionsUrlOptional = getVersionsURL(args);
|
||||
if (!versionsUrlOptional.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto versions_url = versions_url_optional.value();
|
||||
const auto& versionsUrl = versionsUrlOptional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Versions").arg(args.pack->name), APPLICATION->network());
|
||||
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(versions_url);
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(versionsUrl);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for getting versions at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for getting versions at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
|
@ -119,13 +125,13 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
|
|||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
std::ranges::sort(unsortedVersions, orderSortPredicate);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading" << debugName() << "resource version:" << e.cause();
|
||||
}
|
||||
|
||||
callbacks.on_succeed(unsortedVersions);
|
||||
callbacks.onSucceed(unsortedVersions);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
|
|
@ -133,87 +139,93 @@ Task::Ptr ResourceAPI::getProjectVersions(VersionSearchArgs&& args, Callback<QVe
|
|||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = netJob.toWeakRef();
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
int networkErrorCode = -1;
|
||||
if (auto netJob = weak.lock()) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action)
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
if (auto* failedAction = netJob->getFailedActions().at(0); failedAction) {
|
||||
networkErrorCode = failedAction->replyStatusCode();
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
}
|
||||
callbacks.onFail(reason, networkErrorCode);
|
||||
});
|
||||
QObject::connect(netJob.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
if (callbacks.onAbort != nullptr) {
|
||||
callbacks.onAbort();
|
||||
}
|
||||
});
|
||||
|
||||
return netJob;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getProjectInfo(ProjectInfoArgs&& args, Callback<ModPlatform::IndexedPack::Ptr>&& callbacks, bool askRetry) const
|
||||
Task::Ptr ResourceAPI::getProjectInfo(const ProjectInfoArgs& args,
|
||||
const Callback<ModPlatform::IndexedPack::Ptr>& callbacks,
|
||||
bool askRetry) const
|
||||
{
|
||||
auto [job, response] = getProject(args.pack->addonId.toString(), askRetry);
|
||||
|
||||
QObject::connect(job.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
auto pack = args.pack;
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for mod info at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for mod info at" << parseError.offset << "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto obj = Json::requireObject(doc);
|
||||
if (obj.contains("data"))
|
||||
if (obj.contains("data")) {
|
||||
obj = Json::requireObject(obj, "data");
|
||||
}
|
||||
loadIndexedPack(*pack, obj);
|
||||
loadExtraPackInfo(*pack, obj);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << doc;
|
||||
qWarning() << "Error while reading" << debugName() << "resource info:" << e.cause();
|
||||
}
|
||||
callbacks.on_succeed(pack);
|
||||
callbacks.onSucceed(pack);
|
||||
});
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
// This prevents the lambda from extending the lifetime of the shared resource,
|
||||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = job.toWeakRef();
|
||||
QObject::connect(job.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
int networkErrorCode = -1;
|
||||
if (auto job = weak.lock()) {
|
||||
if (auto netJob = qSharedPointerDynamicCast<NetJob>(job)) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action) {
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
if (auto* failedAction = netJob->getFailedActions().at(0); failedAction) {
|
||||
networkErrorCode = failedAction->replyStatusCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
callbacks.onFail(reason, networkErrorCode);
|
||||
});
|
||||
QObject::connect(job.get(), &NetJob::aborted, [callbacks] {
|
||||
if (callbacks.on_abort != nullptr)
|
||||
callbacks.on_abort();
|
||||
if (callbacks.onAbort != nullptr) {
|
||||
callbacks.onAbort();
|
||||
}
|
||||
});
|
||||
return job;
|
||||
}
|
||||
|
||||
Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callback<ModPlatform::IndexedVersion>&& callbacks) const
|
||||
Task::Ptr ResourceAPI::getDependencyVersion(const DependencySearchArgs& args, const Callback<ModPlatform::IndexedVersion>& callbacks) const
|
||||
{
|
||||
auto versions_url_optional = getDependencyURL(args);
|
||||
if (!versions_url_optional.has_value())
|
||||
auto versionsUrlOptional = getDependencyURL(args);
|
||||
if (!versionsUrlOptional.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto versions_url = versions_url_optional.value();
|
||||
const auto& versionsUrl = versionsUrlOptional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network());
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(versions_url);
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(versionsUrl);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [this, response, callbacks, args] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for getting dependency version at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response for getting dependency version at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
|
@ -230,21 +242,23 @@ Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callbac
|
|||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj, ModPlatform::ResourceType::Mod);
|
||||
if (!file.addonId.isValid())
|
||||
if (!file.addonId.isValid()) {
|
||||
file.addonId = args.dependency.addonId;
|
||||
}
|
||||
|
||||
if (file.fileId.isValid() &&
|
||||
(!file.loaders || args.loader & file.loaders)) // Heuristic to check if the returned value is valid
|
||||
(!file.loaders || args.loader & file.loaders)) { // Heuristic to check if the returned value is valid
|
||||
versions.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool {
|
||||
// dates are in RFC 3339 format
|
||||
return a.date > b.date;
|
||||
};
|
||||
std::sort(versions.begin(), versions.end(), orderSortPredicate);
|
||||
std::ranges::sort(versions, orderSortPredicate);
|
||||
auto bestMatch = versions.size() != 0 ? versions.front() : ModPlatform::IndexedVersion();
|
||||
callbacks.on_succeed(bestMatch);
|
||||
callbacks.onSucceed(bestMatch);
|
||||
});
|
||||
|
||||
// Capture a weak_ptr instead of a shared_ptr to avoid circular dependency issues.
|
||||
|
|
@ -252,50 +266,52 @@ Task::Ptr ResourceAPI::getDependencyVersion(DependencySearchArgs&& args, Callbac
|
|||
// as it only temporarily locks the resource when needed.
|
||||
auto weak = netJob.toWeakRef();
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [weak, callbacks](const QString& reason) {
|
||||
int network_error_code = -1;
|
||||
int networkErrorCode = -1;
|
||||
if (auto netJob = weak.lock()) {
|
||||
if (auto* failed_action = netJob->getFailedActions().at(0); failed_action)
|
||||
network_error_code = failed_action->replyStatusCode();
|
||||
if (auto* failedAction = netJob->getFailedActions().at(0); failedAction) {
|
||||
networkErrorCode = failedAction->replyStatusCode();
|
||||
}
|
||||
callbacks.on_fail(reason, network_error_code);
|
||||
}
|
||||
callbacks.onFail(reason, networkErrorCode);
|
||||
});
|
||||
return netJob;
|
||||
}
|
||||
|
||||
QString ResourceAPI::getGameVersionsString(std::vector<Version> mcVersions) const
|
||||
QString ResourceAPI::getGameVersionsString(const std::vector<Version>& mcVersions)
|
||||
{
|
||||
QString s;
|
||||
for (auto& ver : mcVersions) {
|
||||
for (const auto& ver : mcVersions) {
|
||||
s += QString("\"%1\",").arg(mapMCVersionToModrinth(ver));
|
||||
}
|
||||
s.remove(s.length() - 1, 1); // remove last comma
|
||||
return s;
|
||||
}
|
||||
|
||||
QString ResourceAPI::mapMCVersionToModrinth(Version v) const
|
||||
QString ResourceAPI::mapMCVersionToModrinth(const Version& v)
|
||||
{
|
||||
static const QString preString = " Pre-Release ";
|
||||
static const QString s_preString = " Pre-Release ";
|
||||
auto verStr = v.toString();
|
||||
|
||||
if (verStr.contains(preString)) {
|
||||
verStr.replace(preString, "-pre");
|
||||
if (verStr.contains(s_preString)) {
|
||||
verStr.replace(s_preString, "-pre");
|
||||
}
|
||||
verStr.replace(" ", "-");
|
||||
return verStr;
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ResourceAPI::getProject(QString addonId, bool askRetry) const
|
||||
std::pair<Task::Ptr, QByteArray*> ResourceAPI::getProject(const QString& addonId, bool askRetry) const
|
||||
{
|
||||
auto project_url_optional = getInfoURL(addonId);
|
||||
if (!project_url_optional.has_value())
|
||||
auto projectUrlOptional = getInfoURL(addonId);
|
||||
if (!projectUrlOptional.has_value()) {
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
|
||||
auto project_url = project_url_optional.value();
|
||||
const auto& projectUrl = projectUrlOptional.value();
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("%1::GetProject").arg(addonId), APPLICATION->network());
|
||||
netJob->setAskRetry(askRetry);
|
||||
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(project_url));
|
||||
auto [action, response] = Net::ApiDownload::makeByteArray(QUrl(projectUrl));
|
||||
netJob->addNetAction(action);
|
||||
|
||||
return { netJob, response };
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -65,14 +64,14 @@ class ResourceAPI {
|
|||
// Used by Modrinth in the API request.
|
||||
QString name;
|
||||
// The human-readable name of the sorting, used for display in the UI.
|
||||
QString readable_name;
|
||||
QString readableName;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Callback {
|
||||
std::function<void(T&)> on_succeed;
|
||||
std::function<void(const QString& reason, int network_error_code)> on_fail;
|
||||
std::function<void()> on_abort;
|
||||
std::function<void(T&)> onSucceed;
|
||||
std::function<void(const QString& reason, int networkErrorCode)> onFail;
|
||||
std::function<void()> onAbort;
|
||||
};
|
||||
|
||||
struct SearchArgs {
|
||||
|
|
@ -113,21 +112,21 @@ class ResourceAPI {
|
|||
virtual auto getSortingMethods() const -> QList<SortingMethod> = 0;
|
||||
|
||||
public slots:
|
||||
virtual Task::Ptr searchProjects(SearchArgs&&, Callback<QList<ModPlatform::IndexedPack::Ptr>>&&) const;
|
||||
virtual Task::Ptr searchProjects(const SearchArgs&, const Callback<QList<ModPlatform::IndexedPack::Ptr>>&) const;
|
||||
|
||||
virtual std::pair<Task::Ptr, QByteArray*> getProject(QString addonId, bool askRetry = true) const;
|
||||
virtual std::pair<Task::Ptr, QByteArray*> getProject(const QString& addonId, bool askRetry = true) const;
|
||||
virtual std::pair<Task::Ptr, QByteArray*> getProjects(QStringList addonIds) const = 0;
|
||||
|
||||
virtual Task::Ptr getProjectInfo(ProjectInfoArgs&&, Callback<ModPlatform::IndexedPack::Ptr>&&, bool askRetry = true) const;
|
||||
Task::Ptr getProjectVersions(VersionSearchArgs&& args, Callback<QVector<ModPlatform::IndexedVersion>>&& callbacks) const;
|
||||
virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, Callback<ModPlatform::IndexedVersion>&&) const;
|
||||
virtual Task::Ptr getProjectInfo(const ProjectInfoArgs&, const Callback<ModPlatform::IndexedPack::Ptr>&, bool askRetry = true) const;
|
||||
Task::Ptr getProjectVersions(const VersionSearchArgs& args, const Callback<QVector<ModPlatform::IndexedVersion>>& callbacks) const;
|
||||
virtual Task::Ptr getDependencyVersion(const DependencySearchArgs&, const Callback<ModPlatform::IndexedVersion>&) const;
|
||||
|
||||
protected:
|
||||
inline QString debugName() const { return "External resource API"; }
|
||||
virtual QString debugName() const { return "External resource API"; }
|
||||
|
||||
QString mapMCVersionToModrinth(Version v) const;
|
||||
static QString mapMCVersionToModrinth(const Version& v);
|
||||
|
||||
QString getGameVersionsString(std::vector<Version> mcVersions) const;
|
||||
static QString getGameVersionsString(const std::vector<Version>& mcVersions);
|
||||
|
||||
public:
|
||||
virtual auto getSearchURL(const SearchArgs& args) const -> std::optional<QString> = 0;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "ResourceType.h"
|
||||
|
||||
namespace ModPlatform {
|
||||
static const QMap<ResourceType, QString> s_packedTypeNames = { { ResourceType::ResourcePack, QObject::tr("resource pack") },
|
||||
static const QMap<ResourceType, QString> g_packedTypeNames = { { ResourceType::ResourcePack, QObject::tr("resource pack") },
|
||||
{ ResourceType::TexturePack, QObject::tr("texture pack") },
|
||||
{ ResourceType::DataPack, QObject::tr("data pack") },
|
||||
{ ResourceType::ShaderPack, QObject::tr("shader pack") },
|
||||
|
|
@ -34,7 +34,7 @@ namespace ResourceTypeUtils {
|
|||
|
||||
QString getName(ResourceType type)
|
||||
{
|
||||
return s_packedTypeNames.constFind(type).value();
|
||||
return g_packedTypeNames.constFind(type).value();
|
||||
}
|
||||
|
||||
} // namespace ResourceTypeUtils
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@
|
|||
|
||||
namespace ModPlatform {
|
||||
|
||||
enum class ResourceType { Mod, ResourcePack, ShaderPack, Modpack, DataPack, World, Screenshots, TexturePack, Unknown };
|
||||
enum class ResourceType : std::uint8_t { Mod, ResourcePack, ShaderPack, Modpack, DataPack, World, Screenshots, TexturePack, Unknown };
|
||||
|
||||
namespace ResourceTypeUtils {
|
||||
static const std::set<ResourceType> VALID_RESOURCES = { ResourceType::DataPack, ResourceType::ResourcePack, ResourceType::TexturePack,
|
||||
static const std::set<ResourceType> g_VALID_RESOURCES = { ResourceType::DataPack, ResourceType::ResourcePack, ResourceType::TexturePack,
|
||||
ResourceType::ShaderPack, ResourceType::World, ResourceType::Mod };
|
||||
QString getName(ResourceType type);
|
||||
} // namespace ResourceTypeUtils
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "FileResolvingTask.h"
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "Json.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
|
@ -27,13 +28,11 @@
|
|||
|
||||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
static const FlameAPI flameAPI;
|
||||
static ModrinthAPI modrinthAPI;
|
||||
|
||||
Flame::FileResolvingTask::FileResolvingTask(Flame::Manifest& toProcess) : m_manifest(toProcess) {}
|
||||
|
||||
bool Flame::FileResolvingTask::abort()
|
||||
|
|
@ -55,61 +54,37 @@ void Flame::FileResolvingTask::executeTask()
|
|||
setProgress(0, 3);
|
||||
|
||||
QStringList fileIds;
|
||||
for (auto file : m_manifest.files) {
|
||||
for (const auto& file : m_manifest.files) {
|
||||
fileIds.push_back(QString::number(file.fileId));
|
||||
}
|
||||
auto [task, response] = flameAPI.getFiles(fileIds);
|
||||
auto [task, response] = FlameAPI::getFiles(fileIds);
|
||||
m_task = task;
|
||||
|
||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, response, step_progress]() {
|
||||
step_progress->state = TaskStepState::Succeeded;
|
||||
stepProgress(*step_progress);
|
||||
auto stepProgress2 = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, response, stepProgress2]() {
|
||||
stepProgress2->state = TaskStepState::Succeeded;
|
||||
stepProgress(*stepProgress2);
|
||||
netJobFinished(response);
|
||||
});
|
||||
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||
step_progress->state = TaskStepState::Failed;
|
||||
stepProgress(*step_progress);
|
||||
emitFailed(reason);
|
||||
connect(m_task.get(), &Task::failed, this, [this, stepProgress2](QString reason) {
|
||||
stepProgress2->state = TaskStepState::Failed;
|
||||
stepProgress(*stepProgress2);
|
||||
emitFailed(std::move(reason));
|
||||
});
|
||||
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
connect(m_task.get(), &Task::progress, this, [this, stepProgress2](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
stepProgress(*step_progress);
|
||||
stepProgress2->update(current, total);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||
step_progress->status = status;
|
||||
stepProgress(*step_progress);
|
||||
connect(m_task.get(), &Task::status, this, [this, stepProgress2](QString status) {
|
||||
stepProgress2->status = std::move(status);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
ModPlatform::ResourceType getResourceType(int classId)
|
||||
{
|
||||
switch (classId) {
|
||||
case 17: // Worlds
|
||||
return ModPlatform::ResourceType::World;
|
||||
case 6: // Mods
|
||||
return ModPlatform::ResourceType::Mod;
|
||||
case 12: // Resource Packs
|
||||
// return ModPlatform::ResourceType::ResourcePack; // not really a resourcepack
|
||||
/* fallthrough */
|
||||
case 4546: // Customization
|
||||
// return ModPlatform::ResourceType::ShaderPack; // not really a shaderPack
|
||||
/* fallthrough */
|
||||
case 4471: // Modpacks
|
||||
/* fallthrough */
|
||||
case 5: // Bukkit Plugins
|
||||
/* fallthrough */
|
||||
case 4559: // Addons
|
||||
/* fallthrough */
|
||||
default:
|
||||
return ModPlatform::ResourceType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void Flame::FileResolvingTask::netJobFinished(QByteArray* response)
|
||||
{
|
||||
setProgress(1, 3);
|
||||
|
|
@ -139,7 +114,7 @@ void Flame::FileResolvingTask::netJobFinished(QByteArray* response)
|
|||
Q_ASSERT(m_manifest.files.contains(fileid));
|
||||
m_manifest.files[fileid].version = version;
|
||||
auto url = QUrl(version.downloadUrl, QUrl::TolerantMode);
|
||||
if (!url.isValid() && "sha1" == version.hash_type && !version.hash.isEmpty()) {
|
||||
if (!url.isValid() && "sha1" == version.hashType && !version.hash.isEmpty()) {
|
||||
hashes.push_back(version.hash);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
|
|
@ -155,18 +130,18 @@ void Flame::FileResolvingTask::netJobFinished(QByteArray* response)
|
|||
getFlameProjects();
|
||||
return;
|
||||
}
|
||||
auto [modrinthTask, modrinthResponse] = modrinthAPI.currentVersions(hashes, "sha1");
|
||||
auto [modrinthTask, modrinthResponse] = ModrinthAPI::currentVersions(hashes, "sha1");
|
||||
m_task = modrinthTask;
|
||||
(dynamic_cast<NetJob*>(m_task.get()))->setAskRetry(false);
|
||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, modrinthResponse, step_progress]() {
|
||||
step_progress->state = TaskStepState::Succeeded;
|
||||
stepProgress(*step_progress);
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*modrinthResponse, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
auto stepProgress2 = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, modrinthResponse, stepProgress2]() {
|
||||
stepProgress2->state = TaskStepState::Succeeded;
|
||||
stepProgress(*stepProgress2);
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*modrinthResponse, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth::CurrentVersions at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *modrinthResponse;
|
||||
|
||||
getFlameProjects();
|
||||
|
|
@ -177,7 +152,7 @@ void Flame::FileResolvingTask::netJobFinished(QByteArray* response)
|
|||
auto entries = Json::requireObject(doc);
|
||||
for (auto& out : m_manifest.files) {
|
||||
auto url = QUrl(out.version.downloadUrl, QUrl::TolerantMode);
|
||||
if (!url.isValid() && "sha1" == out.version.hash_type && !out.version.hash.isEmpty()) {
|
||||
if (!url.isValid() && "sha1" == out.version.hashType && !out.version.hash.isEmpty()) {
|
||||
try {
|
||||
auto entry = Json::requireObject(entries, out.version.hash);
|
||||
|
||||
|
|
@ -198,20 +173,20 @@ void Flame::FileResolvingTask::netJobFinished(QByteArray* response)
|
|||
}
|
||||
getFlameProjects();
|
||||
});
|
||||
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||
step_progress->state = TaskStepState::Failed;
|
||||
stepProgress(*step_progress);
|
||||
connect(m_task.get(), &Task::failed, this, [this, stepProgress2](const QString& /*reason*/) {
|
||||
stepProgress2->state = TaskStepState::Failed;
|
||||
stepProgress(*stepProgress2);
|
||||
getFlameProjects();
|
||||
});
|
||||
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
connect(m_task.get(), &Task::progress, this, [this, stepProgress2](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
stepProgress(*step_progress);
|
||||
stepProgress2->update(current, total);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||
step_progress->status = status;
|
||||
stepProgress(*step_progress);
|
||||
connect(m_task.get(), &Task::status, this, [this, stepProgress2](QString status) {
|
||||
stepProgress2->status = std::move(status);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
m_task->start();
|
||||
}
|
||||
|
|
@ -220,20 +195,20 @@ void Flame::FileResolvingTask::getFlameProjects()
|
|||
{
|
||||
setProgress(2, 3);
|
||||
QStringList addonIds;
|
||||
for (auto file : m_manifest.files) {
|
||||
for (const auto& file : m_manifest.files) {
|
||||
addonIds.push_back(QString::number(file.projectId));
|
||||
}
|
||||
|
||||
auto [task, response] = flameAPI.getProjects(addonIds);
|
||||
auto [task, response] = FlameAPI().getProjects(addonIds);
|
||||
m_task = task;
|
||||
|
||||
auto step_progress = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, response, step_progress] {
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
auto stepProgress2 = std::make_shared<TaskStepProgress>();
|
||||
connect(m_task.get(), &Task::succeeded, this, [this, response, stepProgress2] {
|
||||
QJsonParseError parseError{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Modrinth projects task at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
|
@ -243,8 +218,8 @@ void Flame::FileResolvingTask::getFlameProjects()
|
|||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
|
||||
for (auto entry : entries) {
|
||||
auto entry_obj = Json::requireObject(entry);
|
||||
auto id = Json::requireInteger(entry_obj, "id");
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
auto id = Json::requireInteger(entryObj, "id");
|
||||
auto file = std::find_if(m_manifest.files.begin(), m_manifest.files.end(),
|
||||
[id](const Flame::File& file) { return file.projectId == id; });
|
||||
if (file == m_manifest.files.end()) {
|
||||
|
|
@ -252,9 +227,8 @@ void Flame::FileResolvingTask::getFlameProjects()
|
|||
}
|
||||
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(file->version.fileName));
|
||||
FlameMod::loadIndexedPack(file->pack, entry_obj);
|
||||
file->resourceType = getResourceType(Json::requireInteger(entry_obj, "classId", "modClassId"));
|
||||
if (file->resourceType == ModPlatform::ResourceType::World) {
|
||||
FlameMod::loadIndexedPack(file->pack, entryObj);
|
||||
if (file->pack.resourceType == ModPlatform::ResourceType::World) {
|
||||
file->targetFolder = "saves";
|
||||
}
|
||||
}
|
||||
|
|
@ -262,25 +236,25 @@ void Flame::FileResolvingTask::getFlameProjects()
|
|||
qDebug() << e.cause();
|
||||
qDebug() << doc;
|
||||
}
|
||||
step_progress->state = TaskStepState::Succeeded;
|
||||
stepProgress(*step_progress);
|
||||
stepProgress2->state = TaskStepState::Succeeded;
|
||||
stepProgress(*stepProgress2);
|
||||
emitSucceeded();
|
||||
});
|
||||
|
||||
connect(m_task.get(), &Task::failed, this, [this, step_progress](QString reason) {
|
||||
step_progress->state = TaskStepState::Failed;
|
||||
stepProgress(*step_progress);
|
||||
emitFailed(reason);
|
||||
connect(m_task.get(), &Task::failed, this, [this, stepProgress2](QString reason) {
|
||||
stepProgress2->state = TaskStepState::Failed;
|
||||
stepProgress(*stepProgress2);
|
||||
emitFailed(std::move(reason));
|
||||
});
|
||||
connect(m_task.get(), &Task::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_task.get(), &Task::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
connect(m_task.get(), &Task::progress, this, [this, stepProgress2](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
stepProgress(*step_progress);
|
||||
stepProgress2->update(current, total);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
connect(m_task.get(), &Task::status, this, [this, step_progress](QString status) {
|
||||
step_progress->status = status;
|
||||
stepProgress(*step_progress);
|
||||
connect(m_task.get(), &Task::status, this, [this, stepProgress2](QString status) {
|
||||
stepProgress2->status = std::move(status);
|
||||
stepProgress(*stepProgress2);
|
||||
});
|
||||
|
||||
m_task->start();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class FileResolvingTask : public Task {
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit FileResolvingTask(Flame::Manifest& toProcess);
|
||||
virtual ~FileResolvingTask() = default;
|
||||
~FileResolvingTask() override = default;
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
|
@ -33,7 +33,7 @@ class FileResolvingTask : public Task {
|
|||
const Flame::Manifest& getResults() const { return m_manifest; }
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
void executeTask() override;
|
||||
|
||||
protected slots:
|
||||
void netJobFinished(QByteArray* response);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "FlameAPI.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "BuildConfig.h"
|
||||
#include "FlameModIndex.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
|
|
@ -19,17 +17,17 @@ std::pair<Task::Ptr, QByteArray*> FlameAPI::matchFingerprints(const QList<uint>&
|
|||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Flame::MatchFingerprints"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonArray fingerprints_arr;
|
||||
for (auto& fp : fingerprints) {
|
||||
fingerprints_arr.append(QString("%1").arg(fp));
|
||||
QJsonObject bodyObj;
|
||||
QJsonArray fingerprintsArr;
|
||||
for (const auto& fp : fingerprints) {
|
||||
fingerprintsArr.append(QString("%1").arg(fp));
|
||||
}
|
||||
|
||||
body_obj["fingerprints"] = fingerprints_arr;
|
||||
bodyObj["fingerprints"] = fingerprintsArr;
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/fingerprints"), body_raw);
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/fingerprints"), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
return { netJob, response };
|
||||
|
|
@ -47,14 +45,14 @@ QString FlameAPI::getModFileChangelog(int modId, int fileId)
|
|||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &changelog] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::FileChangelog at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::FileChangelog at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
netJob->failed(parse_error.errorString());
|
||||
netJob->failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -80,14 +78,14 @@ QString FlameAPI::getModDescription(int modId)
|
|||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, [&netJob, response, &description] {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::ModDescription at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame::ModDescription at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
netJob->failed(parse_error.errorString());
|
||||
netJob->failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -106,48 +104,48 @@ std::pair<Task::Ptr, QByteArray*> FlameAPI::getProjects(QStringList addonIds) co
|
|||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Flame::GetProjects"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonArray addons_arr;
|
||||
QJsonObject bodyObj;
|
||||
QJsonArray addonsArr;
|
||||
for (auto& addonId : addonIds) {
|
||||
addons_arr.append(addonId);
|
||||
addonsArr.append(addonId);
|
||||
}
|
||||
|
||||
body_obj["modIds"] = addons_arr;
|
||||
bodyObj["modIds"] = addonsArr;
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods"), body_raw);
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods"), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [bodyRaw] { qDebug() << bodyRaw; });
|
||||
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> FlameAPI::getFiles(const QStringList& fileIds) const
|
||||
std::pair<Task::Ptr, QByteArray*> FlameAPI::getFiles(const QStringList& fileIds)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Flame::GetFiles"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonArray files_arr;
|
||||
for (auto& fileId : fileIds) {
|
||||
files_arr.append(fileId);
|
||||
QJsonObject bodyObj;
|
||||
QJsonArray filesArr;
|
||||
for (const auto& fileId : fileIds) {
|
||||
filesArr.append(fileId);
|
||||
}
|
||||
|
||||
body_obj["fileIds"] = files_arr;
|
||||
bodyObj["fileIds"] = filesArr;
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods/files"), body_raw);
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.FLAME_BASE_URL + "/mods/files"), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [body_raw] { qDebug() << body_raw; });
|
||||
QObject::connect(netJob.get(), &NetJob::failed, [bodyRaw] { qDebug() << bodyRaw; });
|
||||
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> FlameAPI::getFile(const QString& addonId, const QString& fileId) const
|
||||
std::pair<Task::Ptr, QByteArray*> FlameAPI::getFile(const QString& addonId, const QString& fileId)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network());
|
||||
auto [action, response] =
|
||||
|
|
@ -162,14 +160,41 @@ std::pair<Task::Ptr, QByteArray*> FlameAPI::getFile(const QString& addonId, cons
|
|||
QList<ResourceAPI::SortingMethod> FlameAPI::getSortingMethods() const
|
||||
{
|
||||
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
||||
return { { 1, "Featured", QObject::tr("Sort by Featured") },
|
||||
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
||||
{ 3, "LastUpdated", QObject::tr("Sort by Last Updated") },
|
||||
{ 4, "Name", QObject::tr("Sort by Name") },
|
||||
{ 5, "Author", QObject::tr("Sort by Author") },
|
||||
{ 6, "TotalDownloads", QObject::tr("Sort by Downloads") },
|
||||
{ 7, "Category", QObject::tr("Sort by Category") },
|
||||
{ 8, "GameVersion", QObject::tr("Sort by Game Version") } };
|
||||
return { { .index = 1, .name = "Featured", .readableName = QObject::tr("Sort by Featured") },
|
||||
{ .index = 2, .name = "Popularity", .readableName = QObject::tr("Sort by Popularity") },
|
||||
{ .index = 3, .name = "LastUpdated", .readableName = QObject::tr("Sort by Last Updated") },
|
||||
{ .index = 4, .name = "Name", .readableName = QObject::tr("Sort by Name") },
|
||||
{ .index = 5, .name = "Author", .readableName = QObject::tr("Sort by Author") },
|
||||
{ .index = 6, .name = "TotalDownloads", .readableName = QObject::tr("Sort by Downloads") },
|
||||
{ .index = 7, .name = "Category", .readableName = QObject::tr("Sort by Category") },
|
||||
{ .index = 8, .name = "GameVersion", .readableName = QObject::tr("Sort by Game Version") } };
|
||||
}
|
||||
|
||||
namespace {
|
||||
const auto g_classIDMappings = std::array{
|
||||
std::pair{ ModPlatform::ResourceType::Mod, 6 }, std::pair{ ModPlatform::ResourceType::ResourcePack, 12 },
|
||||
std::pair{ ModPlatform::ResourceType::World, 17 }, std::pair{ ModPlatform::ResourceType::ShaderPack, 6552 },
|
||||
std::pair{ ModPlatform::ResourceType::Modpack, 4471 }, std::pair{ ModPlatform::ResourceType::DataPack, 6945 },
|
||||
};
|
||||
}
|
||||
|
||||
int FlameAPI::getClassId(ModPlatform::ResourceType type)
|
||||
{
|
||||
for (auto&& [e, classId] : g_classIDMappings) {
|
||||
if (e == type) {
|
||||
return classId;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ModPlatform::ResourceType FlameAPI::getResourceType(int classId)
|
||||
{
|
||||
for (auto&& [type, c] : g_classIDMappings) {
|
||||
if (c == classId) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return ModPlatform::ResourceType::Unknown;
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> FlameAPI::getCategories(ModPlatform::ResourceType type)
|
||||
|
|
@ -178,7 +203,7 @@ std::pair<Task::Ptr, QByteArray*> FlameAPI::getCategories(ModPlatform::ResourceT
|
|||
auto [action, response] = Net::ApiDownload::makeByteArray(
|
||||
QUrl(QString(BuildConfig.FLAME_BASE_URL + "/categories?gameId=432&classId=%1").arg(getClassId(type))));
|
||||
netJob->addNetAction(action);
|
||||
QObject::connect(netJob.get(), &Task::failed, [](QString msg) { qDebug() << "Flame failed to get categories:" << msg; });
|
||||
QObject::connect(netJob.get(), &Task::failed, [](const QString& msg) { qDebug() << "Flame failed to get categories:" << msg; });
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
|
|
@ -190,11 +215,10 @@ std::pair<Task::Ptr, QByteArray*> FlameAPI::getModCategories()
|
|||
QList<ModPlatform::Category> FlameAPI::loadModCategories(const QByteArray& response)
|
||||
{
|
||||
QList<ModPlatform::Category> categories;
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from categories at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from categories at" << parseError.offset << "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return categories;
|
||||
}
|
||||
|
|
@ -218,17 +242,17 @@ QList<ModPlatform::Category> FlameAPI::loadModCategories(const QByteArray& respo
|
|||
return categories;
|
||||
};
|
||||
|
||||
std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(QList<ModPlatform::IndexedVersion> versions,
|
||||
QList<ModPlatform::ModLoaderType> instanceLoaders,
|
||||
ModPlatform::ModLoaderTypes modLoaders,
|
||||
std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(const QList<ModPlatform::IndexedVersion>& versions,
|
||||
const QList<ModPlatform::ModLoaderType>& instanceLoaders,
|
||||
ModPlatform::ModLoaderTypes fallback,
|
||||
bool checkLoaders)
|
||||
{
|
||||
static const auto noLoader = ModPlatform::ModLoaderType(0);
|
||||
static const auto s_noLoader = ModPlatform::ModLoaderType(0);
|
||||
if (!checkLoaders) {
|
||||
std::optional<ModPlatform::IndexedVersion> ver;
|
||||
for (auto file_tmp : versions) {
|
||||
if (!ver.has_value() || file_tmp.date > ver->date) {
|
||||
ver = file_tmp;
|
||||
for (const auto& fileTmp : versions) {
|
||||
if (!ver.has_value() || fileTmp.date > ver->date) {
|
||||
ver = fileTmp;
|
||||
}
|
||||
}
|
||||
return ver;
|
||||
|
|
@ -244,26 +268,26 @@ std::optional<ModPlatform::IndexedVersion> FlameAPI::getLatestVersion(QList<ModP
|
|||
bestMatch[loader] = version;
|
||||
}
|
||||
};
|
||||
for (auto file_tmp : versions) {
|
||||
auto loaders = ModPlatform::modLoaderTypesToList(file_tmp.loaders);
|
||||
for (const auto& fileTmp : versions) {
|
||||
auto loaders = ModPlatform::modLoaderTypesToList(fileTmp.loaders);
|
||||
if (loaders.isEmpty()) {
|
||||
checkVersion(file_tmp, noLoader);
|
||||
checkVersion(fileTmp, s_noLoader);
|
||||
} else {
|
||||
for (auto loader : loaders) {
|
||||
checkVersion(file_tmp, loader);
|
||||
checkVersion(fileTmp, loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
// edge case: mod has installed for forge but the instance is fabric => fabric version will be prioritizated on update
|
||||
auto currentLoaders = instanceLoaders + ModPlatform::modLoaderTypesToList(modLoaders);
|
||||
currentLoaders.append(noLoader); // add a fallback in case the versions do not define a loader
|
||||
auto currentLoaders = instanceLoaders + ModPlatform::modLoaderTypesToList(fallback);
|
||||
currentLoaders.append(s_noLoader); // add a fallback in case the versions do not define a loader
|
||||
|
||||
for (auto loader : currentLoaders) {
|
||||
if (bestMatch.contains(loader)) {
|
||||
auto bestForLoader = bestMatch.value(loader);
|
||||
// awkward case where the mod has only two loaders and one of them is not specified
|
||||
if (loader != noLoader && bestMatch.contains(noLoader) && bestMatch.size() == 2) {
|
||||
auto bestForNoLoader = bestMatch.value(noLoader);
|
||||
if (loader != s_noLoader && bestMatch.contains(s_noLoader) && bestMatch.size() == 2) {
|
||||
auto bestForNoLoader = bestMatch.value(s_noLoader);
|
||||
if (bestForNoLoader.date > bestForLoader.date) {
|
||||
return bestForNoLoader;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <cstdint>
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
#include "Version.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
|
@ -15,18 +17,18 @@
|
|||
|
||||
class FlameAPI : public ResourceAPI {
|
||||
public:
|
||||
QString getModFileChangelog(int modId, int fileId);
|
||||
QString getModDescription(int modId);
|
||||
static QString getModFileChangelog(int modId, int fileId);
|
||||
static QString getModDescription(int modId);
|
||||
|
||||
std::optional<ModPlatform::IndexedVersion> getLatestVersion(QList<ModPlatform::IndexedVersion> versions,
|
||||
QList<ModPlatform::ModLoaderType> instanceLoaders,
|
||||
static std::optional<ModPlatform::IndexedVersion> getLatestVersion(const QList<ModPlatform::IndexedVersion>& versions,
|
||||
const QList<ModPlatform::ModLoaderType>& instanceLoaders,
|
||||
ModPlatform::ModLoaderTypes fallback,
|
||||
bool checkLoaders);
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> getProjects(QStringList addonIds) const override;
|
||||
std::pair<Task::Ptr, QByteArray*> matchFingerprints(const QList<uint>& fingerprints);
|
||||
std::pair<Task::Ptr, QByteArray*> getFiles(const QStringList& fileIds) const;
|
||||
std::pair<Task::Ptr, QByteArray*> getFile(const QString& addonId, const QString& fileId) const;
|
||||
static std::pair<Task::Ptr, QByteArray*> matchFingerprints(const QList<uint>& fingerprints);
|
||||
static std::pair<Task::Ptr, QByteArray*> getFiles(const QStringList& fileIds);
|
||||
static std::pair<Task::Ptr, QByteArray*> getFile(const QString& addonId, const QString& fileId);
|
||||
|
||||
static std::pair<Task::Ptr, QByteArray*> getCategories(ModPlatform::ResourceType type);
|
||||
static std::pair<Task::Ptr, QByteArray*> getModCategories();
|
||||
|
|
@ -34,28 +36,15 @@ class FlameAPI : public ResourceAPI {
|
|||
|
||||
QList<ResourceAPI::SortingMethod> getSortingMethods() const override;
|
||||
|
||||
static inline bool validateModLoaders(ModPlatform::ModLoaderTypes loaders)
|
||||
static bool validateModLoaders(ModPlatform::ModLoaderTypes loaders)
|
||||
{
|
||||
return loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt);
|
||||
return (loaders & (ModPlatform::NeoForge | ModPlatform::Forge | ModPlatform::Fabric | ModPlatform::Quilt)) != 0;
|
||||
}
|
||||
|
||||
static ModPlatform::ResourceType getResourceType(int classId);
|
||||
|
||||
private:
|
||||
static int getClassId(ModPlatform::ResourceType type)
|
||||
{
|
||||
switch (type) {
|
||||
default:
|
||||
case ModPlatform::ResourceType::Mod:
|
||||
return 6;
|
||||
case ModPlatform::ResourceType::ResourcePack:
|
||||
return 12;
|
||||
case ModPlatform::ResourceType::ShaderPack:
|
||||
return 6552;
|
||||
case ModPlatform::ResourceType::Modpack:
|
||||
return 4471;
|
||||
case ModPlatform::ResourceType::DataPack:
|
||||
return 6945;
|
||||
}
|
||||
}
|
||||
static int getClassId(ModPlatform::ResourceType type);
|
||||
|
||||
static int getMappedModLoader(ModPlatform::ModLoaderType loaders)
|
||||
{
|
||||
|
|
@ -85,44 +74,49 @@ class FlameAPI : public ResourceAPI {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const QStringList getModLoaderStrings(const ModPlatform::ModLoaderTypes types)
|
||||
static QStringList getModLoaderStrings(const ModPlatform::ModLoaderTypes types)
|
||||
{
|
||||
QStringList l;
|
||||
for (auto loader : { ModPlatform::NeoForge, ModPlatform::Forge, ModPlatform::Fabric, ModPlatform::Quilt }) {
|
||||
if (types & loader) {
|
||||
if ((types & loader) != 0) {
|
||||
l << QString::number(getMappedModLoader(loader));
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static const QString getModLoaderFilters(ModPlatform::ModLoaderTypes types) { return "[" + getModLoaderStrings(types).join(',') + "]"; }
|
||||
static QString getModLoaderFilters(ModPlatform::ModLoaderTypes types) { return "[" + getModLoaderStrings(types).join(',') + "]"; }
|
||||
|
||||
public:
|
||||
std::optional<QString> getSearchURL(const SearchArgs& args) const override
|
||||
{
|
||||
QStringList get_arguments;
|
||||
get_arguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
||||
get_arguments.append(QString("index=%1").arg(args.offset));
|
||||
get_arguments.append("pageSize=25");
|
||||
if (args.search.has_value())
|
||||
get_arguments.append(QString("searchFilter=%1").arg(args.search.value()));
|
||||
if (args.sorting.has_value())
|
||||
get_arguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
||||
get_arguments.append("sortOrder=desc");
|
||||
QStringList getArguments;
|
||||
getArguments.append(QString("classId=%1").arg(getClassId(args.type)));
|
||||
getArguments.append(QString("index=%1").arg(args.offset));
|
||||
getArguments.append("pageSize=25");
|
||||
if (args.search.has_value()) {
|
||||
getArguments.append(QString("searchFilter=%1").arg(args.search.value()));
|
||||
}
|
||||
if (args.sorting.has_value()) {
|
||||
getArguments.append(QString("sortField=%1").arg(args.sorting.value().index));
|
||||
}
|
||||
getArguments.append("sortOrder=desc");
|
||||
if (args.loaders.has_value()) {
|
||||
ModPlatform::ModLoaderTypes loaders = args.loaders.value();
|
||||
loaders &= ~static_cast<std::uint16_t>(ModPlatform::ModLoaderType::DataPack);
|
||||
if (loaders != 0)
|
||||
get_arguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(loaders)));
|
||||
if (loaders != 0) {
|
||||
getArguments.append(QString("modLoaderTypes=%1").arg(getModLoaderFilters(loaders)));
|
||||
}
|
||||
}
|
||||
if (args.categoryIds.has_value() && !args.categoryIds->empty()) {
|
||||
getArguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(",")));
|
||||
}
|
||||
if (args.categoryIds.has_value() && !args.categoryIds->empty())
|
||||
get_arguments.append(QString("categoryIds=[%1]").arg(args.categoryIds->join(",")));
|
||||
|
||||
if (args.versions.has_value() && !args.versions.value().empty())
|
||||
get_arguments.append(QString("gameVersion=%1").arg(args.versions.value().front().toString()));
|
||||
if (args.versions.has_value() && !args.versions.value().empty()) {
|
||||
getArguments.append(QString("gameVersion=%1").arg(args.versions.value().front().toString()));
|
||||
}
|
||||
|
||||
return BuildConfig.FLAME_BASE_URL + "/mods/search?gameId=432&" + get_arguments.join('&');
|
||||
return BuildConfig.FLAME_BASE_URL + "/mods/search?gameId=432&" + getArguments.join('&');
|
||||
}
|
||||
|
||||
std::optional<QString> getVersionsURL(const VersionSearchArgs& args) const override
|
||||
|
|
@ -130,8 +124,9 @@ class FlameAPI : public ResourceAPI {
|
|||
auto addonId = args.pack->addonId.toString();
|
||||
QString url = QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000").arg(addonId);
|
||||
|
||||
if (args.mcVersions.has_value())
|
||||
if (args.mcVersions.has_value()) {
|
||||
url += QString("&gameVersion=%1").arg(args.mcVersions.value().front().toString());
|
||||
}
|
||||
|
||||
if (args.loaders.has_value() && args.loaders.value() != ModPlatform::ModLoaderType::DataPack &&
|
||||
ModPlatform::hasSingleModLoaderSelected(args.loaders.value())) {
|
||||
|
|
@ -150,15 +145,15 @@ class FlameAPI : public ResourceAPI {
|
|||
return arr;
|
||||
}
|
||||
// FIXME: Client-side version filtering. This won't take into account any user-selected filtering.
|
||||
const auto& mc_versions = arr.mcVersion;
|
||||
const auto& mcVersions = arr.mcVersion;
|
||||
|
||||
if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(),
|
||||
[](const auto& mc_version) { return Version(mc_version) <= Version("1.6"); })) {
|
||||
if (std::any_of(mcVersions.constBegin(), mcVersions.constEnd(),
|
||||
[](const auto& mcVersion) { return Version(mcVersion) <= Version("1.6"); })) {
|
||||
return arr;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject&) const override { FlameMod::loadBody(m); }
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, [[maybe_unused]] QJsonObject& /*unused*/) const override { FlameMod::loadBody(m); }
|
||||
|
||||
private:
|
||||
std::optional<QString> getInfoURL(const QString& id) const override { return QString(BuildConfig.FLAME_BASE_URL + "/mods/%1").arg(id); }
|
||||
|
|
@ -167,7 +162,7 @@ class FlameAPI : public ResourceAPI {
|
|||
auto addonId = args.dependency.addonId.toString();
|
||||
auto url =
|
||||
QString(BuildConfig.FLAME_BASE_URL + "/mods/%1/files?pageSize=10000&gameVersion=%2").arg(addonId, args.mcVersion.toString());
|
||||
if (args.loader && ModPlatform::hasSingleModLoaderSelected(args.loader)) {
|
||||
if ((args.loader != 0U) && ModPlatform::hasSingleModLoaderSelected(args.loader)) {
|
||||
int mappedModLoader = getMappedModLoader(static_cast<ModPlatform::ModLoaderType>(static_cast<int>(args.loader)));
|
||||
url += QString("&modLoaderType=%1").arg(mappedModLoader);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ void FlameCheckUpdate::getLatestVersionCallback(Resource* resource, QByteArray*
|
|||
}
|
||||
|
||||
auto downloadTask = makeShared<ResourceDownloadTask>(pack, latestVer.value(), m_resourceModel, true, "update");
|
||||
m_updates.emplace_back(pack->name, resource->metadata()->hash, oldVersion, latestVer->version, latestVer->version_type,
|
||||
m_updates.emplace_back(pack->name, resource->metadata()->hash, oldVersion, latestVer->version, latestVer->versionType,
|
||||
FlameAPI().getModFileChangelog(latestVer->addonId.toInt(), latestVer->fileId.toInt()),
|
||||
ModPlatform::ResourceProvider::FLAME, downloadTask, resource->enabled());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@
|
|||
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
#include "SysInfo.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
#include "ui/dialogs/BlockedModsDialog.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <algorithm>
|
||||
|
||||
#include "HardwareInfo.h"
|
||||
#include "meta/Index.h"
|
||||
|
|
@ -70,163 +70,170 @@
|
|||
#include "net/ApiDownload.h"
|
||||
#include "ui/pages/modplatform/OptionalModDialog.h"
|
||||
|
||||
static const FlameAPI api;
|
||||
|
||||
bool FlameCreationTask::abort()
|
||||
{
|
||||
if (!canAbort())
|
||||
if (!canAbort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_processUpdateFileInfoJob)
|
||||
if (m_processUpdateFileInfoJob) {
|
||||
m_processUpdateFileInfoJob->abort();
|
||||
if (m_filesJob)
|
||||
}
|
||||
if (m_filesJob) {
|
||||
m_filesJob->abort();
|
||||
if (m_modIdResolver)
|
||||
}
|
||||
if (m_modIdResolver) {
|
||||
m_modIdResolver->abort();
|
||||
}
|
||||
|
||||
return InstanceCreationTask::abort();
|
||||
}
|
||||
|
||||
bool FlameCreationTask::updateInstance()
|
||||
{
|
||||
auto instance_list = APPLICATION->instances();
|
||||
auto* instanceList = APPLICATION->instances();
|
||||
|
||||
// FIXME: How to handle situations when there's more than one install already for a given modpack?
|
||||
BaseInstance* inst;
|
||||
if (auto original_id = originalInstanceID(); !original_id.isEmpty()) {
|
||||
inst = instance_list->getInstanceById(original_id);
|
||||
BaseInstance* inst = nullptr;
|
||||
if (auto originalId = originalInstanceID(); !originalId.isEmpty()) {
|
||||
inst = instanceList->getInstanceById(originalId);
|
||||
Q_ASSERT(inst);
|
||||
} else {
|
||||
inst = instance_list->getInstanceByManagedName(originalName());
|
||||
inst = instanceList->getInstanceByManagedName(originalName());
|
||||
|
||||
if (!inst) {
|
||||
inst = instance_list->getInstanceById(originalName());
|
||||
inst = instanceList->getInstanceById(originalName());
|
||||
|
||||
if (!inst)
|
||||
if (!inst) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
QString indexPath(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
|
||||
try {
|
||||
Flame::loadManifest(m_pack, index_path);
|
||||
Flame::loadManifest(m_pack, indexPath);
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto version_id = inst->getManagedPackVersionName();
|
||||
auto version_str = !version_id.isEmpty() ? tr(" (version %1)").arg(version_id) : "";
|
||||
auto versionId = inst->getManagedPackVersionName();
|
||||
auto versionStr = !versionId.isEmpty() ? tr(" (version %1)").arg(versionId) : "";
|
||||
|
||||
if (shouldConfirmUpdate()) {
|
||||
auto should_update = askIfShouldUpdate(m_parent, version_str);
|
||||
if (should_update == ShouldUpdate::SkipUpdating)
|
||||
auto shouldUpdate = askIfShouldUpdate(m_parent, versionStr);
|
||||
if (shouldUpdate == ShouldUpdate::SkipUpdating) {
|
||||
return false;
|
||||
if (should_update == ShouldUpdate::Cancel) {
|
||||
}
|
||||
if (shouldUpdate == ShouldUpdate::Cancel) {
|
||||
m_abort = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QDir old_inst_dir(inst->instanceRoot());
|
||||
QDir oldInstDir(inst->instanceRoot());
|
||||
|
||||
QString old_index_folder(FS::PathCombine(old_inst_dir.absolutePath(), "flame"));
|
||||
QString old_index_path(FS::PathCombine(old_index_folder, "manifest.json"));
|
||||
QString oldIndexFolder(FS::PathCombine(oldInstDir.absolutePath(), "flame"));
|
||||
QString oldIndexPath(FS::PathCombine(oldIndexFolder, "manifest.json"));
|
||||
|
||||
QFileInfo old_index_file(old_index_path);
|
||||
if (old_index_file.exists()) {
|
||||
Flame::Manifest old_pack;
|
||||
Flame::loadManifest(old_pack, old_index_path);
|
||||
QFileInfo oldIndexFile(oldIndexPath);
|
||||
if (oldIndexFile.exists()) {
|
||||
Flame::Manifest oldPack;
|
||||
Flame::loadManifest(oldPack, oldIndexPath);
|
||||
|
||||
auto& old_files = old_pack.files;
|
||||
auto& oldFiles = oldPack.files;
|
||||
|
||||
auto& files = m_pack.files;
|
||||
|
||||
// Remove repeated files, we don't need to download them!
|
||||
auto files_iterator = files.begin();
|
||||
while (files_iterator != files.end()) {
|
||||
auto const& file = files_iterator;
|
||||
auto filesIterator = files.begin();
|
||||
while (filesIterator != files.end()) {
|
||||
const auto& file = filesIterator;
|
||||
|
||||
auto old_file = old_files.find(file.key());
|
||||
if (old_file != old_files.end()) {
|
||||
auto oldFile = oldFiles.find(file.key());
|
||||
if (oldFile != oldFiles.end()) {
|
||||
// We found a match, but is it a different version?
|
||||
if (old_file->fileId == file->fileId) {
|
||||
if (oldFile->fileId == file->fileId) {
|
||||
qDebug() << "Removed file at" << file->targetFolder << "with id" << file->fileId << "from list of downloads";
|
||||
|
||||
old_files.remove(file.key());
|
||||
files_iterator = files.erase(files_iterator);
|
||||
oldFiles.remove(file.key());
|
||||
filesIterator = files.erase(filesIterator);
|
||||
|
||||
if (files_iterator != files.begin())
|
||||
files_iterator--;
|
||||
if (filesIterator != files.begin()) {
|
||||
filesIterator--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files_iterator++;
|
||||
filesIterator++;
|
||||
}
|
||||
|
||||
QDir old_minecraft_dir(inst->gameRoot());
|
||||
QDir oldMinecraftDir(inst->gameRoot());
|
||||
|
||||
// We will remove all the previous overrides, to prevent duplicate files!
|
||||
// TODO: Currently 'overrides' will always override the stuff on update. How do we preserve unchanged overrides?
|
||||
// FIXME: We may want to do something about disabled mods.
|
||||
auto old_overrides = Override::readOverrides("overrides", old_index_folder);
|
||||
for (const auto& entry : old_overrides) {
|
||||
scheduleToDelete(m_parent, old_minecraft_dir, entry);
|
||||
auto oldOverrides = Override::readOverrides("overrides", oldIndexFolder);
|
||||
for (const auto& entry : oldOverrides) {
|
||||
scheduleToDelete(m_parent, oldMinecraftDir, entry);
|
||||
}
|
||||
|
||||
// Remove remaining old files (we need to do an API request to know which ids are which files...)
|
||||
QStringList fileIds;
|
||||
|
||||
for (auto& file : old_files) {
|
||||
for (auto& file : oldFiles) {
|
||||
fileIds.append(QString::number(file.fileId));
|
||||
}
|
||||
|
||||
auto [job, raw_response] = api.getFiles(fileIds);
|
||||
auto [job, rawResponse] = FlameAPI::getFiles(fileIds);
|
||||
|
||||
QEventLoop loop;
|
||||
|
||||
connect(job.get(), &Task::succeeded, this, [this, raw_response, fileIds, old_inst_dir, &old_files, old_minecraft_dir] {
|
||||
connect(job.get(), &Task::succeeded, this, [this, rawResponse, fileIds, oldInstDir, &oldFiles, oldMinecraftDir] {
|
||||
// Parse the API response
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*raw_response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame files task at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
qWarning() << *raw_response;
|
||||
QJsonParseError parseError{};
|
||||
auto doc = QJsonDocument::fromJson(*rawResponse, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Flame files task at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
qWarning() << *rawResponse;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
QJsonArray entries;
|
||||
if (fileIds.size() == 1)
|
||||
if (fileIds.size() == 1) {
|
||||
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||
else
|
||||
} else {
|
||||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
}
|
||||
|
||||
for (auto entry : entries) {
|
||||
auto entry_obj = Json::requireObject(entry);
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
|
||||
Flame::File file;
|
||||
// We don't care about blocked mods, we just need local data to delete the file
|
||||
file.version = FlameMod::loadIndexedPackVersion(entry_obj);
|
||||
auto id = Json::requireInteger(entry_obj, "id");
|
||||
old_files.insert(id, file);
|
||||
file.version = FlameMod::loadIndexedPackVersion(entryObj);
|
||||
auto id = Json::requireInteger(entryObj, "id");
|
||||
oldFiles.insert(id, file);
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
qCritical() << e.cause() << e.what();
|
||||
}
|
||||
|
||||
// Delete the files
|
||||
for (auto& file : old_files) {
|
||||
if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty())
|
||||
for (auto& file : oldFiles) {
|
||||
if (file.version.fileName.isEmpty() || file.targetFolder.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString relative_path(FS::PathCombine(file.targetFolder, file.version.fileName));
|
||||
scheduleToDelete(m_parent, old_minecraft_dir, relative_path, true);
|
||||
QString relativePath(FS::PathCombine(file.targetFolder, file.version.fileName));
|
||||
scheduleToDelete(m_parent, oldMinecraftDir, relativePath, true);
|
||||
}
|
||||
});
|
||||
connect(job.get(), &Task::failed, this, [](QString reason) { qCritical() << "Failed to get files:" << reason; });
|
||||
connect(job.get(), &Task::failed, this, [](const QString& reason) { qCritical() << "Failed to get files:" << reason; });
|
||||
connect(job.get(), &Task::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
m_processUpdateFileInfoJob = job;
|
||||
|
|
@ -237,7 +244,7 @@ bool FlameCreationTask::updateInstance()
|
|||
m_processUpdateFileInfoJob = nullptr;
|
||||
} else {
|
||||
// We don't have an old index file, so we may duplicate stuff!
|
||||
auto dialog = CustomMessageBox::selectable(m_parent, tr("No index file."),
|
||||
auto* dialog = CustomMessageBox::selectable(m_parent, tr("No index file."),
|
||||
tr("We couldn't find a suitable index file for the older version. This may cause some "
|
||||
"of the files to be duplicated. Do you want to continue?"),
|
||||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Cancel);
|
||||
|
|
@ -257,7 +264,10 @@ bool FlameCreationTask::updateInstance()
|
|||
return false;
|
||||
}
|
||||
|
||||
QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion)
|
||||
QString FlameCreationTask::getVersionForLoader(const QString& uid,
|
||||
const QString& loaderType,
|
||||
QString loaderVersion,
|
||||
const QString& mcVersion)
|
||||
{
|
||||
if (loaderVersion == "recommended") {
|
||||
auto vlist = APPLICATION->metadataIndex()->get(uid);
|
||||
|
|
@ -270,28 +280,30 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
|
|||
QEventLoop loadVersionLoop;
|
||||
auto task = vlist->getLoadTask();
|
||||
connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||
if (!task->isRunning())
|
||||
if (!task->isRunning()) {
|
||||
task->start();
|
||||
}
|
||||
|
||||
loadVersionLoop.exec();
|
||||
}
|
||||
|
||||
for (auto version : vlist->versions()) {
|
||||
for (const auto& version : vlist->versions()) {
|
||||
// first recommended build we find, we use.
|
||||
if (!version->isRecommended())
|
||||
if (!version->isRecommended()) {
|
||||
continue;
|
||||
}
|
||||
auto reqs = version->requiredSet();
|
||||
|
||||
// filter by minecraft version, if the loader depends on a certain version.
|
||||
// not all mod loaders depend on a given Minecraft version, so we won't do this
|
||||
// filtering for those loaders.
|
||||
if (loaderType == "forge" || loaderType == "neoforge") {
|
||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
|
||||
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
|
||||
});
|
||||
if (iter == reqs.end())
|
||||
auto iter = std::ranges::find_if(
|
||||
reqs, [mcVersion](const Meta::Require& req) { return req.uid == "net.minecraft" && req.equalsVersion == mcVersion; });
|
||||
if (iter == reqs.end()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return version->descriptor();
|
||||
}
|
||||
|
||||
|
|
@ -311,17 +323,18 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
{
|
||||
QEventLoop loop;
|
||||
|
||||
QString parent_folder(FS::PathCombine(m_stagingPath, "flame"));
|
||||
QString parentFolder(FS::PathCombine(m_stagingPath, "flame"));
|
||||
|
||||
try {
|
||||
QString index_path(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
if (!m_pack.is_loaded)
|
||||
Flame::loadManifest(m_pack, index_path);
|
||||
QString indexPath(FS::PathCombine(m_stagingPath, "manifest.json"));
|
||||
if (!m_pack.isLoaded) {
|
||||
Flame::loadManifest(m_pack, indexPath);
|
||||
}
|
||||
|
||||
// Keep index file in case we need it some other time (like when changing versions)
|
||||
QString new_index_place(FS::PathCombine(parent_folder, "manifest.json"));
|
||||
FS::ensureFilePathExists(new_index_place);
|
||||
FS::move(index_path, new_index_place);
|
||||
QString newIndexPlace(FS::PathCombine(parentFolder, "manifest.json"));
|
||||
FS::ensureFilePathExists(newIndexPlace);
|
||||
FS::move(indexPath, newIndexPlace);
|
||||
|
||||
} catch (const JSONValidationError& e) {
|
||||
setError(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
|
|
@ -332,7 +345,7 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
QString overridePath = FS::PathCombine(m_stagingPath, m_pack.overrides);
|
||||
if (QFile::exists(overridePath)) {
|
||||
// Create a list of overrides in "overrides.txt" inside flame/
|
||||
Override::createOverrides("overrides", parent_folder, overridePath);
|
||||
Override::createOverrides("overrides", parentFolder, overridePath);
|
||||
|
||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||
if (!FS::move(overridePath, mcPath)) {
|
||||
|
|
@ -353,8 +366,9 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
auto id = loader.id;
|
||||
if (id.startsWith("neoforge-")) {
|
||||
id.remove("neoforge-");
|
||||
if (id.startsWith("1.20.1-"))
|
||||
if (id.startsWith("1.20.1-")) {
|
||||
id.remove("1.20.1-"); // this is a mess for curseforge
|
||||
}
|
||||
loaderType = "neoforge";
|
||||
loaderUid = "net.neoforged";
|
||||
} else if (id.startsWith("forge-")) {
|
||||
|
|
@ -388,13 +402,14 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
||||
}
|
||||
|
||||
auto components = instance->getPackProfile();
|
||||
auto* components = instance->getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if (!loaderType.isEmpty()) {
|
||||
auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion);
|
||||
if (version.isEmpty())
|
||||
if (version.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
components->setComponentVersion(loaderUid, version);
|
||||
}
|
||||
|
||||
|
|
@ -415,13 +430,13 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
// only set memory if this is a fresh instance
|
||||
if (!m_instance && recommendedRAM > 0) {
|
||||
const uint64_t sysMiB = HardwareInfo::totalRamMiB();
|
||||
const uint64_t max = sysMiB * 0.9;
|
||||
const auto max = sysMiB * 9 / 10;
|
||||
|
||||
if (static_cast<uint64_t>(recommendedRAM) > max) {
|
||||
logWarning(tr("The recommended memory of the modpack exceeds 90% of your system RAM—reducing it from %1 MiB to %2 MiB!")
|
||||
.arg(recommendedRAM)
|
||||
.arg(max));
|
||||
recommendedRAM = max;
|
||||
recommendedRAM = static_cast<int>(max);
|
||||
}
|
||||
|
||||
instance->settings()->set("OverrideMemory", true);
|
||||
|
|
@ -439,23 +454,24 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
qDebug() << info.fileName();
|
||||
jarMods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
auto profile = instance->getPackProfile();
|
||||
auto* profile = instance->getPackProfile();
|
||||
profile->installJarMods(jarMods);
|
||||
// nuke the original files
|
||||
FS::deletePath(jarmodsPath);
|
||||
}
|
||||
|
||||
// Don't add managed info to packs without an ID (most likely imported from ZIP)
|
||||
if (!m_managedId.isEmpty())
|
||||
if (!m_managedId.isEmpty()) {
|
||||
instance->setManagedPack("flame", m_managedId, m_pack.name, m_managedVersionId, m_pack.version);
|
||||
else
|
||||
} else {
|
||||
instance->setManagedPack("flame", "", name(), "", "");
|
||||
}
|
||||
|
||||
instance->setName(name());
|
||||
|
||||
m_modIdResolver.reset(new Flame::FileResolvingTask(m_pack));
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, this, [this, &loop] { idResolverSucceeded(loop); });
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this, &loop](QString reason) {
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [this, &loop](const QString& reason) {
|
||||
m_modIdResolver.reset();
|
||||
setError(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
loop.quit();
|
||||
|
|
@ -469,17 +485,17 @@ std::unique_ptr<MinecraftInstance> FlameCreationTask::createInstance()
|
|||
|
||||
loop.exec();
|
||||
|
||||
bool did_succeed = getError().isEmpty();
|
||||
bool didSucceed = getError().isEmpty();
|
||||
|
||||
// Update information of the already installed instance, if any.
|
||||
if (m_instance && did_succeed) {
|
||||
if (m_instance && didSucceed) {
|
||||
setAbortable(false);
|
||||
auto inst = m_instance.value();
|
||||
auto* inst = *m_instance;
|
||||
|
||||
inst->copyManagedPack(*instance);
|
||||
}
|
||||
|
||||
if (did_succeed) {
|
||||
if (didSucceed) {
|
||||
return instance;
|
||||
}
|
||||
return nullptr;
|
||||
|
|
@ -508,28 +524,28 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
}
|
||||
|
||||
// first check for blocked mods
|
||||
QList<BlockedMod> blocked_mods;
|
||||
QList<BlockedMod> blockedMods;
|
||||
auto anyBlocked = false;
|
||||
for (const auto& result : results.values()) {
|
||||
if (result.resourceType != ModPlatform::ResourceType::Mod) {
|
||||
if (result.pack.resourceType != ModPlatform::ResourceType::Mod) {
|
||||
m_otherResources.append(std::make_pair(result.version.fileName, result.targetFolder));
|
||||
}
|
||||
|
||||
// skip optional mods that were not selected
|
||||
if (result.version.downloadUrl.isEmpty()) {
|
||||
BlockedMod blocked_mod;
|
||||
blocked_mod.name = result.version.fileName;
|
||||
blocked_mod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId));
|
||||
blocked_mod.hash = result.version.hash;
|
||||
blocked_mod.matched = false;
|
||||
blocked_mod.localPath = "";
|
||||
blocked_mod.targetFolder = result.targetFolder;
|
||||
BlockedMod blockedMod;
|
||||
blockedMod.name = result.version.fileName;
|
||||
blockedMod.websiteUrl = QString("%1/download/%2").arg(result.pack.websiteUrl, QString::number(result.fileId));
|
||||
blockedMod.hash = result.version.hash;
|
||||
blockedMod.matched = false;
|
||||
blockedMod.localPath = "";
|
||||
blockedMod.targetFolder = result.targetFolder;
|
||||
auto fileName = result.version.fileName;
|
||||
fileName = FS::RemoveInvalidPathChars(fileName);
|
||||
auto relpath = FS::PathCombine(result.targetFolder, fileName);
|
||||
blocked_mod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath);
|
||||
blockedMod.disabled = !result.required && !m_selectedOptionalMods.contains(relpath);
|
||||
|
||||
blocked_mods.append(blocked_mod);
|
||||
blockedMods.append(blockedMod);
|
||||
|
||||
anyBlocked = true;
|
||||
}
|
||||
|
|
@ -537,16 +553,16 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||
if (anyBlocked) {
|
||||
qWarning() << "Blocked mods found, displaying mod list";
|
||||
|
||||
BlockedModsDialog message_dialog(m_parent, tr("Blocked mods found"),
|
||||
BlockedModsDialog messageDialog(m_parent, tr("Blocked mods found"),
|
||||
tr("The following files are not available for download in third party launchers.<br/>"
|
||||
"You will need to manually download them and add them to the instance."),
|
||||
blocked_mods);
|
||||
blockedMods);
|
||||
|
||||
message_dialog.setModal(true);
|
||||
messageDialog.setModal(true);
|
||||
|
||||
if (message_dialog.exec()) {
|
||||
qDebug() << "Post dialog blocked mods list:" << blocked_mods;
|
||||
copyBlockedMods(blocked_mods);
|
||||
if (messageDialog.exec() != 0) {
|
||||
qDebug() << "Post dialog blocked mods list:" << blockedMods;
|
||||
copyBlockedMods(blockedMods);
|
||||
setupDownloadJob(loop);
|
||||
} else {
|
||||
m_modIdResolver.reset();
|
||||
|
|
@ -586,7 +602,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||
m_filesJob.reset();
|
||||
validateOtherResources(loop);
|
||||
});
|
||||
connect(m_filesJob.get(), &NetJob::failed, [this](QString reason) {
|
||||
connect(m_filesJob.get(), &NetJob::failed, [this](const QString& reason) {
|
||||
m_filesJob.reset();
|
||||
setError(reason);
|
||||
});
|
||||
|
|
@ -602,22 +618,23 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||
|
||||
/// @brief copy the matched blocked mods to the instance staging area
|
||||
/// @param blocked_mods list of the blocked mods and their matched paths
|
||||
void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||
void FlameCreationTask::copyBlockedMods(const QList<BlockedMod>& blockedMods)
|
||||
{
|
||||
setStatus(tr("Copying Blocked Mods..."));
|
||||
setAbortable(false);
|
||||
int i = 0;
|
||||
int total = blocked_mods.length();
|
||||
auto total = blockedMods.length();
|
||||
setProgress(i, total);
|
||||
for (auto const& mod : blocked_mods) {
|
||||
for (const auto& mod : blockedMods) {
|
||||
if (!mod.matched) {
|
||||
qDebug() << mod.name << "was not matched to a local file, skipping copy";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto destPath = FS::PathCombine(m_stagingPath, "minecraft", mod.targetFolder, mod.name);
|
||||
if (mod.disabled)
|
||||
if (mod.disabled) {
|
||||
destPath += ".disabled";
|
||||
}
|
||||
|
||||
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
|
||||
|
||||
|
|
@ -644,13 +661,13 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
|||
{
|
||||
qDebug() << "Validating whether other resources are in the right place";
|
||||
QStringList zipMods;
|
||||
for (auto [fileName, targetFolder] : m_otherResources) {
|
||||
for (const auto& [fileName, targetFolder] : m_otherResources) {
|
||||
qDebug() << "Checking" << fileName << "...";
|
||||
auto localPath = FS::PathCombine(m_stagingPath, "minecraft", targetFolder, fileName);
|
||||
|
||||
/// @brief check the target and move the the file
|
||||
/// @return path where file can now be found
|
||||
auto validatePath = [&localPath, this](QString fileName, QString targetFolder, QString realTarget) {
|
||||
auto validatePath = [&localPath, this](const QString& fileName, const QString& targetFolder, const QString& realTarget) {
|
||||
if (targetFolder != realTarget) {
|
||||
qDebug() << "Target folder of" << fileName << "is incorrect, it belongs in" << realTarget;
|
||||
auto destPath = FS::PathCombine(m_stagingPath, "minecraft", realTarget, fileName);
|
||||
|
|
@ -664,7 +681,7 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
|||
return localPath;
|
||||
};
|
||||
|
||||
auto installWorld = [this](QString worldPath) {
|
||||
auto installWorld = [this](const QString& worldPath) {
|
||||
qDebug() << "Installing World from" << worldPath;
|
||||
QFileInfo worldFileInfo(worldPath);
|
||||
World w(worldFileInfo);
|
||||
|
|
@ -714,7 +731,7 @@ void FlameCreationTask::validateOtherResources(QEventLoop& loop)
|
|||
auto task = makeShared<ConcurrentTask>("CreateModMetadata", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt());
|
||||
auto results = m_modIdResolver->getResults().files;
|
||||
auto folder = FS::PathCombine(m_stagingPath, "minecraft", "mods", ".index");
|
||||
for (auto file : results) {
|
||||
for (const auto& file : results) {
|
||||
if (file.targetFolder != "mods" || (file.version.fileName.endsWith(".zip") && !zipMods.contains(file.version.fileName))) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,18 +51,18 @@ class FlameCreationTask final : public InstanceCreationTask {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCreationTask(const QString& staging_path,
|
||||
SettingsObject* global_settings,
|
||||
FlameCreationTask(const QString& stagingPath,
|
||||
SettingsObject* globalSettings,
|
||||
QWidget* parent,
|
||||
QString id,
|
||||
QString version_id,
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask(), m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id))
|
||||
QString versionId,
|
||||
QString originalInstanceId = {})
|
||||
: m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(versionId))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
setStagingPath(stagingPath);
|
||||
setParentSettings(globalSettings);
|
||||
|
||||
m_original_instance_id = std::move(original_instance_id);
|
||||
m_original_instance_id = std::move(originalInstanceId);
|
||||
}
|
||||
|
||||
bool abort() override;
|
||||
|
|
@ -73,9 +73,9 @@ class FlameCreationTask final : public InstanceCreationTask {
|
|||
private slots:
|
||||
void idResolverSucceeded(QEventLoop&);
|
||||
void setupDownloadJob(QEventLoop&);
|
||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||
void copyBlockedMods(const QList<BlockedMod>& blockedMods);
|
||||
void validateOtherResources(QEventLoop& loop);
|
||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||
QString getVersionForLoader(const QString& uid, const QString& loaderType, QString version, const QString& mcVersion);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
#include "FlameModIndex.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
|
||||
static FlameAPI api;
|
||||
|
||||
void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.addonId = Json::requireInteger(obj, "id");
|
||||
|
|
@ -37,41 +35,48 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
}
|
||||
}
|
||||
|
||||
pack.resourceType = FlameAPI::getResourceType(obj["classId"].toInt(0));
|
||||
pack.extraDataLoaded = false;
|
||||
loadURLs(pack, obj);
|
||||
}
|
||||
|
||||
void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
auto links_obj = obj["links"].toObject();
|
||||
auto linksObj = obj["links"].toObject();
|
||||
|
||||
pack.extraData.issuesUrl = links_obj["issuesUrl"].toString();
|
||||
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||
pack.extraData.issuesUrl = linksObj["issuesUrl"].toString();
|
||||
if (pack.extraData.issuesUrl.endsWith('/')) {
|
||||
pack.extraData.issuesUrl.chop(1);
|
||||
}
|
||||
|
||||
pack.extraData.sourceUrl = links_obj["sourceUrl"].toString();
|
||||
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||
pack.extraData.sourceUrl = linksObj["sourceUrl"].toString();
|
||||
if (pack.extraData.sourceUrl.endsWith('/')) {
|
||||
pack.extraData.sourceUrl.chop(1);
|
||||
}
|
||||
|
||||
pack.extraData.wikiUrl = links_obj["wikiUrl"].toString();
|
||||
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||
pack.extraData.wikiUrl = linksObj["wikiUrl"].toString();
|
||||
if (pack.extraData.wikiUrl.endsWith('/')) {
|
||||
pack.extraData.wikiUrl.chop(1);
|
||||
}
|
||||
|
||||
if (!pack.extraData.body.isEmpty())
|
||||
if (!pack.extraData.body.isEmpty()) {
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FlameMod::loadBody(ModPlatform::IndexedPack& pack)
|
||||
{
|
||||
pack.extraData.body = api.getModDescription(pack.addonId.toInt());
|
||||
pack.extraData.body = FlameAPI::getModDescription(pack.addonId.toInt());
|
||||
|
||||
if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty())
|
||||
if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty()) {
|
||||
pack.extraDataLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
static QString enumToString(int hash_algorithm)
|
||||
namespace {
|
||||
QString enumToString(int hashAlgorithm)
|
||||
{
|
||||
switch (hash_algorithm) {
|
||||
switch (hashAlgorithm) {
|
||||
default:
|
||||
case 1:
|
||||
return "sha1";
|
||||
|
|
@ -79,6 +84,7 @@ static QString enumToString(int hash_algorithm)
|
|||
return "md5";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr)
|
||||
{
|
||||
|
|
@ -87,23 +93,25 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArra
|
|||
auto obj = versionIter.toObject();
|
||||
|
||||
auto file = loadIndexedPackVersion(obj);
|
||||
if (!file.addonId.isValid())
|
||||
if (!file.addonId.isValid()) {
|
||||
file.addonId = pack.addonId;
|
||||
}
|
||||
|
||||
if (file.fileId.isValid()) // Heuristic to check if the returned value is valid
|
||||
if (file.fileId.isValid()) { // Heuristic to check if the returned value is valid
|
||||
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;
|
||||
};
|
||||
std::sort(unsortedVersions.begin(), unsortedVersions.end(), orderSortPredicate);
|
||||
std::ranges::sort(unsortedVersions, orderSortPredicate);
|
||||
pack.versions = unsortedVersions;
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> ModPlatform::IndexedVersion
|
||||
auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool loadChangelog) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
auto versionArray = Json::requireArray(obj, "gameVersions");
|
||||
|
||||
|
|
@ -111,29 +119,31 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
for (auto mcVer : versionArray) {
|
||||
auto str = mcVer.toString();
|
||||
|
||||
if (str.contains('.'))
|
||||
if (str.contains('.')) {
|
||||
file.mcVersion.append(str);
|
||||
}
|
||||
|
||||
file.side = ModPlatform::Side::NoSide;
|
||||
if (auto loader = str.toLower(); loader == "neoforge")
|
||||
if (auto loader = str.toLower(); loader == "neoforge") {
|
||||
file.loaders |= ModPlatform::NeoForge;
|
||||
else if (loader == "forge")
|
||||
} else if (loader == "forge") {
|
||||
file.loaders |= ModPlatform::Forge;
|
||||
else if (loader == "cauldron")
|
||||
} else if (loader == "cauldron") {
|
||||
file.loaders |= ModPlatform::Cauldron;
|
||||
else if (loader == "liteloader")
|
||||
} else if (loader == "liteloader") {
|
||||
file.loaders |= ModPlatform::LiteLoader;
|
||||
else if (loader == "fabric")
|
||||
} else if (loader == "fabric") {
|
||||
file.loaders |= ModPlatform::Fabric;
|
||||
else if (loader == "quilt")
|
||||
} else if (loader == "quilt") {
|
||||
file.loaders |= ModPlatform::Quilt;
|
||||
else if (loader == "server" || loader == "client") {
|
||||
if (file.side == ModPlatform::Side::NoSide)
|
||||
} else if (loader == "server" || loader == "client") {
|
||||
if (file.side == ModPlatform::Side::NoSide) {
|
||||
file.side = ModPlatform::SideUtils::fromString(loader);
|
||||
else if (file.side != ModPlatform::SideUtils::fromString(loader))
|
||||
} else if (file.side != ModPlatform::SideUtils::fromString(loader)) {
|
||||
file.side = ModPlatform::Side::UniversalSide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.addonId = Json::requireInteger(obj, "modId");
|
||||
file.fileId = Json::requireInteger(obj, "id");
|
||||
|
|
@ -143,31 +153,31 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
file.fileName = Json::requireString(obj, "fileName");
|
||||
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||
|
||||
ModPlatform::IndexedVersionType ver_type;
|
||||
ModPlatform::IndexedVersionType verType;
|
||||
switch (Json::requireInteger(obj, "releaseType")) {
|
||||
case 1:
|
||||
ver_type = ModPlatform::IndexedVersionType::Release;
|
||||
verType = ModPlatform::IndexedVersionType::Release;
|
||||
break;
|
||||
case 2:
|
||||
ver_type = ModPlatform::IndexedVersionType::Beta;
|
||||
verType = ModPlatform::IndexedVersionType::Beta;
|
||||
break;
|
||||
case 3:
|
||||
ver_type = ModPlatform::IndexedVersionType::Alpha;
|
||||
verType = ModPlatform::IndexedVersionType::Alpha;
|
||||
break;
|
||||
default:
|
||||
ver_type = ModPlatform::IndexedVersionType::Unknown;
|
||||
verType = ModPlatform::IndexedVersionType::Unknown;
|
||||
break;
|
||||
}
|
||||
file.version_type = ver_type;
|
||||
file.versionType = verType;
|
||||
|
||||
auto hash_list = obj["hashes"].toArray();
|
||||
for (auto h : hash_list) {
|
||||
auto hash_entry = h.toObject();
|
||||
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME);
|
||||
auto hash_algo = enumToString(hash_entry["algo"].toInt(1));
|
||||
if (hash_types.contains(hash_algo)) {
|
||||
file.hash = Json::requireString(hash_entry, "value");
|
||||
file.hash_type = hash_algo;
|
||||
auto hashList = obj["hashes"].toArray();
|
||||
for (auto h : hashList) {
|
||||
auto hashEntry = h.toObject();
|
||||
auto hashTypes = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME);
|
||||
auto hashAlgo = enumToString(hashEntry["algo"].toInt(1));
|
||||
if (hashTypes.contains(hashAlgo)) {
|
||||
file.hash = Json::requireString(hashEntry, "value");
|
||||
file.hashType = hashAlgo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -203,8 +213,9 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->
|
|||
file.dependencies.append(dependency);
|
||||
}
|
||||
|
||||
if (load_changelog)
|
||||
file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());
|
||||
if (loadChangelog) {
|
||||
file.changelog = FlameAPI::getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,11 @@
|
|||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
namespace FlameMod {
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadURLs(ModPlatform::IndexedPack& m, QJsonObject& obj);
|
||||
void loadBody(ModPlatform::IndexedPack& m);
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj);
|
||||
void loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj);
|
||||
void loadBody(ModPlatform::IndexedPack& pack);
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr);
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false);
|
||||
ModPlatform::IndexedVersion loadIndexedPackVersion(QJsonObject& obj, bool loadChangelog = false);
|
||||
} // namespace FlameMod
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
#include "PackManifest.h"
|
||||
#include "Json.h"
|
||||
|
||||
static void loadFileV1(Flame::File& f, QJsonObject& file)
|
||||
namespace {
|
||||
void loadFileV1(Flame::File& f, QJsonObject& file)
|
||||
{
|
||||
f.projectId = Json::requireInteger(file, "projectID");
|
||||
f.fileId = Json::requireInteger(file, "fileID");
|
||||
f.required = file["required"].toBool(true);
|
||||
}
|
||||
|
||||
static void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
|
||||
void loadModloaderV1(Flame::Modloader& m, QJsonObject& modLoader)
|
||||
{
|
||||
m.id = Json::requireString(modLoader, "id");
|
||||
m.primary = modLoader["primary"].toBool();
|
||||
}
|
||||
|
||||
static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
||||
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
|
||||
|
|
@ -30,7 +31,7 @@ static void loadMinecraftV1(Flame::Minecraft& m, QJsonObject& minecraft)
|
|||
m.recommendedRAM = minecraft["recommendedRam"].toInt();
|
||||
}
|
||||
|
||||
static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
{
|
||||
auto mc = Json::requireObject(manifest, "minecraft");
|
||||
|
||||
|
|
@ -52,8 +53,9 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
|||
|
||||
pack.overrides = manifest["overrides"].toString("overrides");
|
||||
|
||||
pack.is_loaded = true;
|
||||
pack.isLoaded = true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Flame::loadManifest(Flame::Manifest& m, const QString& filepath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/ResourceType.h"
|
||||
|
||||
namespace Flame {
|
||||
struct File {
|
||||
|
|
@ -55,7 +54,6 @@ struct File {
|
|||
|
||||
// our
|
||||
QString targetFolder = QStringLiteral("mods");
|
||||
ModPlatform::ResourceType resourceType;
|
||||
};
|
||||
|
||||
struct Modloader {
|
||||
|
|
@ -81,7 +79,7 @@ struct Manifest {
|
|||
QMap<int, Flame::File> files;
|
||||
QString overrides;
|
||||
|
||||
bool is_loaded = false;
|
||||
bool isLoaded = false;
|
||||
};
|
||||
|
||||
void loadManifest(Flame::Manifest& m, const QString& filepath);
|
||||
|
|
|
|||
|
|
@ -3,100 +3,102 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "ModrinthAPI.h"
|
||||
#include <array>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "modplatform/ResourceType.h"
|
||||
#include "net/ApiDownload.h"
|
||||
#include "net/ApiUpload.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::currentVersion(const QString& hash, const QString& hash_format) const
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::currentVersion(const QString& hash, const QString& hashFormat)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersion"), APPLICATION->network());
|
||||
|
||||
auto [action, response] =
|
||||
Net::ApiDownload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hash_format));
|
||||
Net::ApiDownload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1?algorithm=%2").arg(hash, hashFormat));
|
||||
netJob->addNetAction(action);
|
||||
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::currentVersions(const QStringList& hashes, QString hash_format) const
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::currentVersions(const QStringList& hashes, const QString& hashFormat)
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetCurrentVersions"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonObject bodyObj;
|
||||
|
||||
Json::writeStringList(body_obj, "hashes", hashes);
|
||||
Json::writeString(body_obj, "algorithm", hash_format);
|
||||
Json::writeStringList(bodyObj, "hashes", hashes);
|
||||
Json::writeString(bodyObj, "algorithm", hashFormat);
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), body_raw);
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files"), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
netJob->setAskRetry(false);
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::latestVersion(const QString& hash,
|
||||
const QString& hash_format,
|
||||
const QString& hashFormat,
|
||||
std::optional<std::vector<Version>> mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersion"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonObject bodyObj;
|
||||
|
||||
if (loaders.has_value()) {
|
||||
Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value()));
|
||||
Json::writeStringList(bodyObj, "loaders", getModLoaderStrings(loaders.value()));
|
||||
}
|
||||
|
||||
if (mcVersions.has_value()) {
|
||||
QStringList game_versions;
|
||||
QStringList gameVersions;
|
||||
for (auto& ver : mcVersions.value()) {
|
||||
game_versions.append(mapMCVersionToModrinth(ver));
|
||||
gameVersions.append(mapMCVersionToModrinth(ver));
|
||||
}
|
||||
Json::writeStringList(body_obj, "game_versions", game_versions);
|
||||
Json::writeStringList(bodyObj, "game_versions", gameVersions);
|
||||
}
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(
|
||||
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hash_format), body_raw);
|
||||
QString(BuildConfig.MODRINTH_PROD_URL + "/version_file/%1/update?algorithm=%2").arg(hash, hashFormat), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
return { netJob, response };
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::latestVersions(const QStringList& hashes,
|
||||
const QString& hash_format,
|
||||
const QString& hashFormat,
|
||||
std::optional<std::vector<Version>> mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const
|
||||
{
|
||||
auto netJob = makeShared<NetJob>(QString("Modrinth::GetLatestVersions"), APPLICATION->network());
|
||||
|
||||
QJsonObject body_obj;
|
||||
QJsonObject bodyObj;
|
||||
|
||||
Json::writeStringList(body_obj, "hashes", hashes);
|
||||
Json::writeString(body_obj, "algorithm", hash_format);
|
||||
Json::writeStringList(bodyObj, "hashes", hashes);
|
||||
Json::writeString(bodyObj, "algorithm", hashFormat);
|
||||
|
||||
if (loaders.has_value()) {
|
||||
Json::writeStringList(body_obj, "loaders", getModLoaderStrings(loaders.value()));
|
||||
Json::writeStringList(bodyObj, "loaders", getModLoaderStrings(loaders.value()));
|
||||
}
|
||||
|
||||
if (mcVersions.has_value()) {
|
||||
QStringList game_versions;
|
||||
QStringList gameVersions;
|
||||
for (auto& ver : mcVersions.value()) {
|
||||
game_versions.append(mapMCVersionToModrinth(ver));
|
||||
gameVersions.append(mapMCVersionToModrinth(ver));
|
||||
}
|
||||
Json::writeStringList(body_obj, "game_versions", game_versions);
|
||||
Json::writeStringList(bodyObj, "game_versions", gameVersions);
|
||||
}
|
||||
|
||||
QJsonDocument body(body_obj);
|
||||
auto body_raw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), body_raw);
|
||||
QJsonDocument body(bodyObj);
|
||||
auto bodyRaw = body.toJson();
|
||||
auto [action, response] = Net::ApiUpload::makeByteArray(QString(BuildConfig.MODRINTH_PROD_URL + "/version_files/update"), bodyRaw);
|
||||
netJob->addNetAction(action);
|
||||
|
||||
return { netJob, response };
|
||||
|
|
@ -116,11 +118,42 @@ std::pair<Task::Ptr, QByteArray*> ModrinthAPI::getProjects(QStringList addonIds)
|
|||
QList<ResourceAPI::SortingMethod> ModrinthAPI::getSortingMethods() const
|
||||
{
|
||||
// https://docs.modrinth.com/api-spec/#tag/projects/operation/searchProjects
|
||||
return { { .index = 1, .name = "relevance", .readable_name = QObject::tr("Sort by Relevance") },
|
||||
{ .index = 2, .name = "downloads", .readable_name = QObject::tr("Sort by Downloads") },
|
||||
{ .index = 3, .name = "follows", .readable_name = QObject::tr("Sort by Follows") },
|
||||
{ .index = 4, .name = "newest", .readable_name = QObject::tr("Sort by Newest") },
|
||||
{ .index = 5, .name = "updated", .readable_name = QObject::tr("Sort by Last Updated") } };
|
||||
return { { .index = 1, .name = "relevance", .readableName = QObject::tr("Sort by Relevance") },
|
||||
{ .index = 2, .name = "downloads", .readableName = QObject::tr("Sort by Downloads") },
|
||||
{ .index = 3, .name = "follows", .readableName = QObject::tr("Sort by Follows") },
|
||||
{ .index = 4, .name = "newest", .readableName = QObject::tr("Sort by Newest") },
|
||||
{ .index = 5, .name = "updated", .readableName = QObject::tr("Sort by Last Updated") } };
|
||||
}
|
||||
namespace {
|
||||
const auto g_resourceTypeMap = std::array{
|
||||
std::pair{ ModPlatform::ResourceType::Mod, "mod" }, std::pair{ ModPlatform::ResourceType::ResourcePack, "resourcepack" },
|
||||
std::pair{ ModPlatform::ResourceType::ShaderPack, "shader" }, std::pair{ ModPlatform::ResourceType::DataPack, "datapack" },
|
||||
std::pair{ ModPlatform::ResourceType::Modpack, "modpack" },
|
||||
};
|
||||
}
|
||||
|
||||
ModPlatform::ResourceType ModrinthAPI::getResourceType(const QString& param)
|
||||
{
|
||||
for (const auto& [key, value] : g_resourceTypeMap) {
|
||||
if (value == param) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Invalid resource type for Modrinth API!" << param;
|
||||
return ModPlatform::ResourceType::Unknown;
|
||||
}
|
||||
|
||||
QString ModrinthAPI::resourceTypeParameter(ModPlatform::ResourceType type)
|
||||
{
|
||||
for (const auto& [key, value] : g_resourceTypeMap) {
|
||||
if (key == type) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Invalid resource type for Modrinth API!" << static_cast<std::uint8_t>(type);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> ModrinthAPI::getModCategories()
|
||||
|
|
@ -136,11 +169,10 @@ std::pair<Task::Ptr, QByteArray*> ModrinthAPI::getModCategories()
|
|||
QList<ModPlatform::Category> ModrinthAPI::loadCategories(const QByteArray& response, const QString& projectType)
|
||||
{
|
||||
QList<ModPlatform::Category> categories;
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from categories at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from categories at" << parseError.offset << "reason:" << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
return categories;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,24 @@
|
|||
#include "modplatform/modrinth/ModrinthPackIndex.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <utility>
|
||||
|
||||
class ModrinthAPI : public ResourceAPI {
|
||||
public:
|
||||
std::pair<Task::Ptr, QByteArray*> currentVersion(const QString& hash, const QString& hash_format) const;
|
||||
static std::pair<Task::Ptr, QByteArray*> currentVersion(const QString& hash, const QString& hashFormat);
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> currentVersions(const QStringList& hashes, QString hash_format) const;
|
||||
static std::pair<Task::Ptr, QByteArray*> currentVersions(const QStringList& hashes, const QString& hashFormat);
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> latestVersion(const QString& hash,
|
||||
const QString& hash_format,
|
||||
const QString& hashFormat,
|
||||
std::optional<std::vector<Version>> mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const;
|
||||
|
||||
std::pair<Task::Ptr, QByteArray*> latestVersions(const QStringList& hashes,
|
||||
const QString& hash_format,
|
||||
const QString& hashFormat,
|
||||
std::optional<std::vector<Version>> mcVersions,
|
||||
std::optional<ModPlatform::ModLoaderTypes> loaders) const;
|
||||
|
||||
|
|
@ -100,55 +103,37 @@ class ModrinthAPI : public ResourceAPI {
|
|||
}
|
||||
return v;
|
||||
}
|
||||
static ModPlatform::ResourceType getResourceType(const QString& param);
|
||||
|
||||
private:
|
||||
static QString resourceTypeParameter(ModPlatform::ResourceType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ModPlatform::ResourceType::Mod:
|
||||
return "mod";
|
||||
case ModPlatform::ResourceType::ResourcePack:
|
||||
return "resourcepack";
|
||||
case ModPlatform::ResourceType::ShaderPack:
|
||||
return "shader";
|
||||
case ModPlatform::ResourceType::DataPack:
|
||||
return "datapack";
|
||||
case ModPlatform::ResourceType::Modpack:
|
||||
return "modpack";
|
||||
default:
|
||||
qWarning() << "Invalid resource type for Modrinth API!";
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
static QString resourceTypeParameter(ModPlatform::ResourceType type);
|
||||
|
||||
QString createFacets(const SearchArgs& args) const
|
||||
{
|
||||
QStringList facets_list;
|
||||
QStringList facetsList;
|
||||
|
||||
if (args.loaders.has_value() && args.loaders.value() != 0) {
|
||||
facets_list.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
||||
facetsList.append(QString("[%1]").arg(getModLoaderFilters(args.loaders.value())));
|
||||
}
|
||||
if (args.versions.has_value() && !args.versions.value().empty()) {
|
||||
facets_list.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
||||
facetsList.append(QString("[%1]").arg(getGameVersionsArray(args.versions.value())));
|
||||
}
|
||||
if (args.side.has_value()) {
|
||||
auto side = getSideFilters(args.side.value());
|
||||
if (!side.isEmpty()) {
|
||||
facets_list.append(QString("[%1]").arg(side));
|
||||
facetsList.append(QString("[%1]").arg(side));
|
||||
}
|
||||
}
|
||||
if (args.categoryIds.has_value() && !args.categoryIds->empty()) {
|
||||
facets_list.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value())));
|
||||
facetsList.append(QString("[%1]").arg(getCategoriesFilters(args.categoryIds.value())));
|
||||
}
|
||||
if (args.openSource) {
|
||||
facets_list.append("[\"open_source:true\"]");
|
||||
facetsList.append("[\"open_source:true\"]");
|
||||
}
|
||||
|
||||
facets_list.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type)));
|
||||
facetsList.append(QString("[\"project_type:%1\"]").arg(resourceTypeParameter(args.type)));
|
||||
|
||||
return QString("[%1]").arg(facets_list.join(','));
|
||||
return QString("[%1]").arg(facetsList.join(','));
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -161,18 +146,18 @@ class ModrinthAPI : public ResourceAPI {
|
|||
}
|
||||
}
|
||||
|
||||
QStringList get_arguments;
|
||||
get_arguments.append(QString("offset=%1").arg(args.offset));
|
||||
get_arguments.append(QString("limit=25"));
|
||||
QStringList getArguments;
|
||||
getArguments.append(QString("offset=%1").arg(args.offset));
|
||||
getArguments.append(QString("limit=25"));
|
||||
if (args.search.has_value()) {
|
||||
get_arguments.append(QString("query=%1").arg(args.search.value()));
|
||||
getArguments.append(QString("query=%1").arg(args.search.value()));
|
||||
}
|
||||
if (args.sorting.has_value()) {
|
||||
get_arguments.append(QString("index=%1").arg(args.sorting.value().name));
|
||||
getArguments.append(QString("index=%1").arg(args.sorting.value().name));
|
||||
}
|
||||
get_arguments.append(QString("facets=%1").arg(createFacets(args)));
|
||||
getArguments.append(QString("facets=%1").arg(createFacets(args)));
|
||||
|
||||
return BuildConfig.MODRINTH_PROD_URL + "/search?" + get_arguments.join('&');
|
||||
return BuildConfig.MODRINTH_PROD_URL + "/search?" + getArguments.join('&');
|
||||
};
|
||||
|
||||
auto getInfoURL(const QString& id) const -> std::optional<QString> override
|
||||
|
|
@ -180,24 +165,24 @@ class ModrinthAPI : public ResourceAPI {
|
|||
return BuildConfig.MODRINTH_PROD_URL + "/project/" + id;
|
||||
};
|
||||
|
||||
auto getMultipleModInfoURL(const QStringList& ids) const -> QString
|
||||
static auto getMultipleModInfoURL(const QStringList& ids) -> QString
|
||||
{
|
||||
return BuildConfig.MODRINTH_PROD_URL + QString("/projects?ids=[\"%1\"]").arg(ids.join("\",\""));
|
||||
};
|
||||
|
||||
auto getVersionsURL(const VersionSearchArgs& args) const -> std::optional<QString> override
|
||||
{
|
||||
QStringList get_arguments;
|
||||
QStringList getArguments;
|
||||
if (args.mcVersions.has_value()) {
|
||||
get_arguments.append(QString("game_versions=[%1]").arg(getGameVersionsString(args.mcVersions.value())));
|
||||
getArguments.append(QString("game_versions=[%1]").arg(getGameVersionsString(args.mcVersions.value())));
|
||||
}
|
||||
if (args.loaders.has_value()) {
|
||||
get_arguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\"")));
|
||||
getArguments.append(QString("loaders=[\"%1\"]").arg(getModLoaderStrings(args.loaders.value()).join("\",\"")));
|
||||
}
|
||||
get_arguments.append(QString("include_changelog=%1").arg(args.includeChangelog ? "true" : "false"));
|
||||
getArguments.append(QString("include_changelog=%1").arg(args.includeChangelog ? "true" : "false"));
|
||||
|
||||
return QString("%1/project/%2/version%3%4")
|
||||
.arg(BuildConfig.MODRINTH_PROD_URL, args.pack->addonId.toString(), get_arguments.isEmpty() ? "" : "?", get_arguments.join('&'));
|
||||
.arg(BuildConfig.MODRINTH_PROD_URL, args.pack->addonId.toString(), getArguments.isEmpty() ? "" : "?", getArguments.join('&'));
|
||||
};
|
||||
|
||||
QString getGameVersionsArray(const std::vector<Version>& mcVersions) const
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(QByteArray* response, std::optio
|
|||
pack->slug = resource->metadata()->slug;
|
||||
pack->addonId = resource->metadata()->project_id;
|
||||
pack->provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
if ((projectVer.hash != hash && projectVer.is_preferred) || (resource->status() == ResourceStatus::NotInstalled)) {
|
||||
if ((projectVer.hash != hash && projectVer.isPreferred) || (resource->status() == ResourceStatus::NotInstalled)) {
|
||||
auto downloadTask = makeShared<ResourceDownloadTask>(pack, projectVer, m_resourceModel, true, "update");
|
||||
|
||||
QString oldVersion = resource->metadata()->version_number;
|
||||
|
|
@ -190,8 +190,8 @@ void ModrinthCheckUpdate::checkVersionsResponse(QByteArray* response, std::optio
|
|||
}
|
||||
}
|
||||
|
||||
m_updates.emplace_back(pack->name, hash, oldVersion, projectVer.version_number, projectVer.version_type,
|
||||
projectVer.changelog, ModPlatform::ResourceProvider::MODRINTH, downloadTask, resource->enabled());
|
||||
m_updates.emplace_back(pack->name, hash, oldVersion, projectVer.versionNumber, projectVer.versionType, projectVer.changelog,
|
||||
ModPlatform::ResourceProvider::MODRINTH, downloadTask, resource->enabled());
|
||||
}
|
||||
m_deps.append(std::make_shared<GetModDependenciesTask::PackDependency>(pack, projectVer));
|
||||
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@
|
|||
#include "ModrinthAPI.h"
|
||||
|
||||
#include "Json.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
namespace {
|
||||
bool shouldDownloadOnSide(const QString& side)
|
||||
{
|
||||
return side == "required" || side == "optional";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// https://docs.modrinth.com/api/operations/getproject/
|
||||
void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
|
|
@ -41,6 +41,7 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
|
||||
pack.provider = ModPlatform::ResourceProvider::MODRINTH;
|
||||
pack.name = Json::requireString(obj, "title");
|
||||
pack.resourceType = ModrinthAPI::getResourceType(obj["project_type"].toString());
|
||||
|
||||
pack.slug = obj["slug"].toString("");
|
||||
if (!pack.slug.isEmpty()) {
|
||||
|
|
@ -79,31 +80,34 @@ void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
|||
void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj)
|
||||
{
|
||||
pack.extraData.issuesUrl = obj["issues_url"].toString();
|
||||
if (pack.extraData.issuesUrl.endsWith('/'))
|
||||
if (pack.extraData.issuesUrl.endsWith('/')) {
|
||||
pack.extraData.issuesUrl.chop(1);
|
||||
}
|
||||
|
||||
pack.extraData.sourceUrl = obj["source_url"].toString();
|
||||
if (pack.extraData.sourceUrl.endsWith('/'))
|
||||
if (pack.extraData.sourceUrl.endsWith('/')) {
|
||||
pack.extraData.sourceUrl.chop(1);
|
||||
}
|
||||
|
||||
pack.extraData.wikiUrl = obj["wiki_url"].toString();
|
||||
if (pack.extraData.wikiUrl.endsWith('/'))
|
||||
if (pack.extraData.wikiUrl.endsWith('/')) {
|
||||
pack.extraData.wikiUrl.chop(1);
|
||||
}
|
||||
|
||||
pack.extraData.discordUrl = obj["discord_url"].toString();
|
||||
if (pack.extraData.discordUrl.endsWith('/')) {
|
||||
pack.extraData.discordUrl.chop(1);
|
||||
}
|
||||
|
||||
auto donate_arr = obj["donation_urls"].toArray();
|
||||
for (auto d : donate_arr) {
|
||||
auto d_obj = Json::requireObject(d);
|
||||
auto donateArr = obj["donation_urls"].toArray();
|
||||
for (auto d : donateArr) {
|
||||
auto dObj = Json::requireObject(d);
|
||||
|
||||
ModPlatform::DonationData donate;
|
||||
|
||||
donate.id = d_obj["id"].toString();
|
||||
donate.platform = d_obj["platform"].toString();
|
||||
donate.url = d_obj["url"].toString();
|
||||
donate.id = dObj["id"].toString();
|
||||
donate.platform = dObj["platform"].toString();
|
||||
donate.url = dObj["url"].toString();
|
||||
|
||||
pack.extraData.donate.append(donate);
|
||||
}
|
||||
|
|
@ -116,8 +120,8 @@ void Modrinth::loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& ob
|
|||
}
|
||||
|
||||
ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
||||
const QString& preferred_hash_type,
|
||||
const QString& preferred_file_name)
|
||||
const QString& preferredHashType,
|
||||
const QString& preferredFileName)
|
||||
{
|
||||
ModPlatform::IndexedVersion file;
|
||||
|
||||
|
|
@ -149,8 +153,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
|||
}
|
||||
}
|
||||
file.version = Json::requireString(obj, "name");
|
||||
file.version_number = Json::requireString(obj, "version_number");
|
||||
file.version_type = ModPlatform::IndexedVersionType::fromString(Json::requireString(obj, "version_type"));
|
||||
file.versionNumber = Json::requireString(obj, "version_number");
|
||||
file.versionType = ModPlatform::IndexedVersionType::fromString(Json::requireString(obj, "version_type"));
|
||||
|
||||
if (obj.contains("changelog")) {
|
||||
file.changelog = Json::requireString(obj, "changelog");
|
||||
|
|
@ -196,8 +200,8 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
|||
auto parent = files[i].toObject();
|
||||
auto fileName = Json::requireString(parent, "filename");
|
||||
|
||||
if (!preferred_file_name.isEmpty() && fileName.contains(preferred_file_name)) {
|
||||
file.is_preferred = true;
|
||||
if (!preferredFileName.isEmpty() && fileName.contains(preferredFileName)) {
|
||||
file.isPreferred = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -214,18 +218,18 @@ ModPlatform::IndexedVersion Modrinth::loadIndexedPackVersion(QJsonObject& obj,
|
|||
file.downloadUrl = Json::requireString(parent, "url");
|
||||
file.fileName = Json::requireString(parent, "filename");
|
||||
file.fileName = FS::RemoveInvalidPathChars(file.fileName);
|
||||
file.is_preferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
|
||||
auto hash_list = Json::requireObject(parent, "hashes");
|
||||
file.isPreferred = Json::requireBoolean(parent, "primary") || (files.count() == 1);
|
||||
auto hashList = Json::requireObject(parent, "hashes");
|
||||
|
||||
if (hash_list.contains(preferred_hash_type)) {
|
||||
file.hash = Json::requireString(hash_list, preferred_hash_type);
|
||||
file.hash_type = preferred_hash_type;
|
||||
if (hashList.contains(preferredHashType)) {
|
||||
file.hash = Json::requireString(hashList, preferredHashType);
|
||||
file.hashType = preferredHashType;
|
||||
} else {
|
||||
auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH);
|
||||
for (auto& hash_type : hash_types) {
|
||||
if (hash_list.contains(hash_type)) {
|
||||
file.hash = Json::requireString(hash_list, hash_type);
|
||||
file.hash_type = hash_type;
|
||||
auto hashTypes = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH);
|
||||
for (auto& hashType : hashTypes) {
|
||||
if (hashList.contains(hashType)) {
|
||||
file.hash = Json::requireString(hashList, hashType);
|
||||
file.hashType = hashType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@
|
|||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj);
|
||||
void loadExtraPackData(ModPlatform::IndexedPack& pack, QJsonObject& obj);
|
||||
auto loadIndexedPackVersion(QJsonObject& obj, const QString& preferred_hash_type = "sha512", const QString& preferred_file_name = "")
|
||||
auto loadIndexedPackVersion(QJsonObject& obj, const QString& preferredHashType = "sha512", const QString& preferredFileName = "")
|
||||
-> ModPlatform::IndexedVersion;
|
||||
|
||||
} // namespace Modrinth
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir,
|
|||
mod.url = mod_version.downloadUrl;
|
||||
}
|
||||
|
||||
mod.hash_format = mod_version.hash_type;
|
||||
mod.hash_format = mod_version.hashType;
|
||||
mod.hash = mod_version.hash;
|
||||
|
||||
mod.provider = mod_pack.provider;
|
||||
|
|
@ -133,9 +133,9 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir,
|
|||
mod.mcVersions = mod_version.mcVersion;
|
||||
mod.mcVersions.removeDuplicates();
|
||||
std::ranges::sort(mod.mcVersions, sortMCVersions);
|
||||
mod.releaseType = mod_version.version_type;
|
||||
mod.releaseType = mod_version.versionType;
|
||||
|
||||
mod.version_number = mod_version.version_number;
|
||||
mod.version_number = mod_version.versionNumber;
|
||||
if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a version number
|
||||
mod.version_number = mod_version.version;
|
||||
|
||||
|
|
|
|||
|
|
@ -1126,7 +1126,7 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||
|
||||
auto type = ResourceUtils::identify(localFileInfo);
|
||||
|
||||
if (ModPlatform::ResourceTypeUtils::VALID_RESOURCES.count(type) == 0) { // probably instance/modpack
|
||||
if (ModPlatform::ResourceTypeUtils::g_VALID_RESOURCES.count(type) == 0) { // probably instance/modpack
|
||||
addInstance(localFileName, extra_info);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "ResourceDownloadTask.h"
|
||||
|
||||
#include "minecraft/PackProfile.h"
|
||||
|
|
@ -50,12 +51,16 @@
|
|||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent, ResourceFolderModel* baseModel, bool suppressInitialSearch)
|
||||
ResourceDownloadDialog::ResourceDownloadDialog(QWidget* parent,
|
||||
ResourceFolderModel* baseModel,
|
||||
BaseInstance* inst,
|
||||
bool suppressInitialSearch)
|
||||
: QDialog(parent)
|
||||
, m_base_model(baseModel)
|
||||
, m_baseModel(baseModel)
|
||||
, m_buttons(QDialogButtonBox::Help | QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
|
||||
, m_vertical_layout(this)
|
||||
, m_verticalLayout(this)
|
||||
, m_suppressInitialSearch(suppressInitialSearch)
|
||||
, m_instance(inst)
|
||||
{
|
||||
setObjectName(QStringLiteral("ResourceDownloadDialog"));
|
||||
|
||||
|
|
@ -130,7 +135,7 @@ void ResourceDownloadDialog::initializeContainer()
|
|||
m_container = new PageContainer(this, {}, this);
|
||||
m_container->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Expanding);
|
||||
m_container->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_vertical_layout.addWidget(m_container);
|
||||
m_verticalLayout.addWidget(m_container);
|
||||
|
||||
m_container->addButtons(&m_buttons);
|
||||
|
||||
|
|
@ -200,9 +205,9 @@ void ResourceDownloadDialog::confirm()
|
|||
confirmDialog->appendResource({ .name = task->getName(),
|
||||
.filename = task->getFilename(),
|
||||
.provider = ModPlatform::ProviderCapabilities::name(task->getProvider()),
|
||||
.required_by = extraInfo.required_by,
|
||||
.version_type = task->getVersion().version_type.toString(),
|
||||
.enabled = !extraInfo.maybe_installed });
|
||||
.required_by = extraInfo.requiredBy,
|
||||
.version_type = task->getVersion().versionType.toString(),
|
||||
.enabled = !extraInfo.maybeInstalled });
|
||||
}
|
||||
|
||||
if (confirmDialog->exec() != 0) {
|
||||
|
|
@ -237,7 +242,32 @@ ResourcePage* ResourceDownloadDialog::selectedPage()
|
|||
void ResourceDownloadDialog::addResource(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, QString downloadReason)
|
||||
{
|
||||
removeResource(pack->name);
|
||||
selectedPage()->addResourceToPage(pack, ver, getBaseModel(), std::move(downloadReason));
|
||||
auto* model = getBaseModel();
|
||||
auto* instance = dynamic_cast<MinecraftInstance*>(m_instance);
|
||||
if (instance) {
|
||||
switch (pack->resourceType) {
|
||||
case ModPlatform::ResourceType::Mod:
|
||||
model = instance->loaderModList();
|
||||
break;
|
||||
case ModPlatform::ResourceType::ResourcePack:
|
||||
model = instance->resourcePackList();
|
||||
break;
|
||||
case ModPlatform::ResourceType::ShaderPack:
|
||||
model = instance->shaderPackList();
|
||||
break;
|
||||
case ModPlatform::ResourceType::DataPack:
|
||||
model = instance->dataPackList();
|
||||
break;
|
||||
// case ModPlatform::ResourceType::World:
|
||||
// model = instance->worldList();
|
||||
case ModPlatform::ResourceType::TexturePack:
|
||||
model = instance->texturePackList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
selectedPage()->addResourceToPage(pack, ver, model, std::move(downloadReason));
|
||||
setButtonStatus();
|
||||
}
|
||||
|
||||
|
|
@ -289,13 +319,25 @@ void ResourceDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* s
|
|||
}
|
||||
|
||||
ModDownloadDialog::ModDownloadDialog(QWidget* parent, ModFolderModel* mods, BaseInstance* instance, bool suppressInitialSearch)
|
||||
: ResourceDownloadDialog(parent, mods, suppressInitialSearch), m_instance(instance)
|
||||
: ResourceDownloadDialog(parent, mods, instance, suppressInitialSearch), m_instance(instance)
|
||||
{
|
||||
setWindowTitle(dialogTitle());
|
||||
|
||||
initializeContainer();
|
||||
connectButtons();
|
||||
|
||||
// need to load all resources for dependency task
|
||||
auto* mInstance = dynamic_cast<MinecraftInstance*>(instance);
|
||||
if (mInstance) {
|
||||
for (auto* model : mInstance->resourceLists()) {
|
||||
if (model) {
|
||||
if (model->empty()) {
|
||||
model->startWatching();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!geometrySaveKey().isEmpty()) {
|
||||
restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get(geometrySaveKey()).toString().toUtf8()));
|
||||
}
|
||||
|
|
@ -340,7 +382,7 @@ ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,
|
|||
ResourcePackFolderModel* resourcePacks,
|
||||
BaseInstance* instance,
|
||||
bool suppressInitialSearch)
|
||||
: ResourceDownloadDialog(parent, resourcePacks, suppressInitialSearch), m_instance(instance)
|
||||
: ResourceDownloadDialog(parent, resourcePacks, instance, suppressInitialSearch), m_instance(instance)
|
||||
{
|
||||
setWindowTitle(dialogTitle());
|
||||
|
||||
|
|
@ -372,7 +414,7 @@ TexturePackDownloadDialog::TexturePackDownloadDialog(QWidget* parent,
|
|||
TexturePackFolderModel* resourcePacks,
|
||||
BaseInstance* instance,
|
||||
bool suppressInitialSearch)
|
||||
: ResourceDownloadDialog(parent, resourcePacks, suppressInitialSearch), m_instance(instance)
|
||||
: ResourceDownloadDialog(parent, resourcePacks, instance, suppressInitialSearch), m_instance(instance)
|
||||
{
|
||||
setWindowTitle(dialogTitle());
|
||||
|
||||
|
|
@ -404,7 +446,7 @@ ShaderPackDownloadDialog::ShaderPackDownloadDialog(QWidget* parent,
|
|||
ShaderPackFolderModel* shaders,
|
||||
BaseInstance* instance,
|
||||
bool suppressInitialSearch)
|
||||
: ResourceDownloadDialog(parent, shaders, suppressInitialSearch), m_instance(instance)
|
||||
: ResourceDownloadDialog(parent, shaders, instance, suppressInitialSearch), m_instance(instance)
|
||||
{
|
||||
setWindowTitle(dialogTitle());
|
||||
|
||||
|
|
@ -452,7 +494,7 @@ DataPackDownloadDialog::DataPackDownloadDialog(QWidget* parent,
|
|||
DataPackFolderModel* dataPacks,
|
||||
BaseInstance* instance,
|
||||
bool suppressInitialSearch)
|
||||
: ResourceDownloadDialog(parent, dataPacks, suppressInitialSearch), m_instance(instance)
|
||||
: ResourceDownloadDialog(parent, dataPacks, instance, suppressInitialSearch), m_instance(instance)
|
||||
{
|
||||
setWindowTitle(dialogTitle());
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
|||
public:
|
||||
using DownloadTaskPtr = shared_qobject_ptr<ResourceDownloadTask>;
|
||||
|
||||
ResourceDownloadDialog(QWidget* parent, ResourceFolderModel* baseModel, bool suppressInitialSearch = false);
|
||||
ResourceDownloadDialog(QWidget* parent, ResourceFolderModel* baseModel, BaseInstance* instance, bool suppressInitialSearch = false);
|
||||
|
||||
void initializeContainer();
|
||||
void connectButtons();
|
||||
|
|
@ -68,7 +68,7 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
|||
void removeResource(const QString&);
|
||||
|
||||
QList<DownloadTaskPtr> getTasks();
|
||||
ResourceFolderModel* getBaseModel() const { return m_base_model; }
|
||||
ResourceFolderModel* getBaseModel() const { return m_baseModel; }
|
||||
|
||||
void setResourceMetadata(const std::shared_ptr<Metadata::ModStruct>& meta);
|
||||
|
||||
|
|
@ -88,15 +88,15 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
|||
virtual GetModDependenciesTask::Ptr getModDependenciesTask() { return nullptr; }
|
||||
|
||||
protected:
|
||||
ResourceFolderModel* m_base_model;
|
||||
ResourceFolderModel* m_baseModel;
|
||||
|
||||
PageContainer* m_container = nullptr;
|
||||
|
||||
QDialogButtonBox m_buttons;
|
||||
QVBoxLayout m_vertical_layout;
|
||||
QVBoxLayout m_verticalLayout;
|
||||
|
||||
protected:
|
||||
bool m_suppressInitialSearch = false;
|
||||
BaseInstance* m_instance = nullptr;
|
||||
};
|
||||
|
||||
class ModDownloadDialog final : public ResourceDownloadDialog {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ void ResourceUpdateDialog::checkCandidates()
|
|||
ScrollMessageBox messageDialog(m_parent, tr("Failed to check for updates"),
|
||||
tr("Could not check or get the following resources for updates:<br>"
|
||||
"Do you wish to proceed without those resources?"),
|
||||
text, "Disable unavailable mods");
|
||||
text, tr("Disable unavailable mods"));
|
||||
messageDialog.setModal(true);
|
||||
if (messageDialog.exec() == QDialog::Rejected) {
|
||||
m_aborted = true;
|
||||
|
|
@ -243,11 +243,11 @@ void ResourceUpdateDialog::checkCandidates()
|
|||
auto downloadTask = makeShared<ResourceDownloadTask>(dep->pack, dep->version, m_resourceModel, true, "dependency");
|
||||
auto extraInfo = dependencyExtraInfo.value(dep->version.addonId.toString());
|
||||
CheckUpdateTask::Update updatable = {
|
||||
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type,
|
||||
changelog, dep->pack->provider, downloadTask, !extraInfo.maybe_installed
|
||||
dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.versionType,
|
||||
changelog, dep->pack->provider, downloadTask, !extraInfo.maybeInstalled,
|
||||
};
|
||||
|
||||
appendResource(updatable, extraInfo.required_by);
|
||||
appendResource(updatable, extraInfo.requiredBy);
|
||||
m_tasks.insert(updatable.name, updatable.download);
|
||||
}
|
||||
}
|
||||
|
|
@ -412,6 +412,7 @@ void ResourceUpdateDialog::onMetadataEnsured(Resource* resource)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p)
|
||||
{
|
||||
switch (p) {
|
||||
|
|
@ -423,6 +424,7 @@ ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p)
|
|||
|
||||
return ModPlatform::ResourceProvider::FLAME;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool tryOthers, ModPlatform::ResourceProvider firstChoice)
|
||||
{
|
||||
|
|
@ -517,7 +519,7 @@ void ResourceUpdateDialog::appendResource(const CheckUpdateTask::Update& info, Q
|
|||
ui->modTreeWidget->addTopLevelItem(itemTop);
|
||||
}
|
||||
|
||||
auto ResourceUpdateDialog::getTasks() -> const QList<ResourceDownloadTask::Ptr>
|
||||
auto ResourceUpdateDialog::getTasks() const -> QList<ResourceDownloadTask::Ptr>
|
||||
{
|
||||
QList<ResourceDownloadTask::Ptr> list;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class ResourceUpdateDialog final : public ReviewMessageBox {
|
|||
|
||||
void appendResource(const CheckUpdateTask::Update& info, QStringList requiredBy = {});
|
||||
|
||||
const QList<ResourceDownloadTask::Ptr> getTasks();
|
||||
QList<ResourceDownloadTask::Ptr> getTasks() const;
|
||||
auto indexDir() const -> QDir { return m_resourceModel->indexDir(); }
|
||||
|
||||
auto noUpdates() const -> bool { return m_noUpdates; };
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi
|
|||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
connect(ui->urlLine, &QLineEdit::textChanged, this, [this](QString text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); });
|
||||
connect(ui->urlLine, &QLineEdit::textChanged, this,
|
||||
[this](QString text) { m_inst->settings()->set("ManagedPackURL", text.trimmed()); });
|
||||
}
|
||||
|
||||
ManagedPackPage::~ManagedPackPage()
|
||||
|
|
@ -273,7 +274,7 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||
m_pack = { .addonId = m_inst->getManagedPackID() };
|
||||
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
callbacks.onSucceed = [this](auto& doc) {
|
||||
m_pack.versions = doc;
|
||||
m_pack.versionsLoaded = true;
|
||||
|
||||
|
|
@ -298,8 +299,8 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||
|
||||
m_loaded = true;
|
||||
};
|
||||
callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
callbacks.onFail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.onAbort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
|
|
@ -428,7 +429,7 @@ void FlameManagedPackPage::parseManagedPack()
|
|||
ResourceAPI::Callback<QVector<ModPlatform::IndexedVersion>> callbacks{};
|
||||
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
callbacks.onSucceed = [this](auto& doc) {
|
||||
m_pack.versions = doc;
|
||||
m_pack.versionsLoaded = true;
|
||||
|
||||
|
|
@ -451,8 +452,8 @@ void FlameManagedPackPage::parseManagedPack()
|
|||
|
||||
m_loaded = true;
|
||||
};
|
||||
callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.on_abort = [this]() { setFailState(); };
|
||||
callbacks.onFail = [this](const QString& /*reason*/, int) { setFailState(); };
|
||||
callbacks.onAbort = [this]() { setFailState(); };
|
||||
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
|
||||
.mcVersions = {},
|
||||
.loaders = {},
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ ModModel::ModModel(BaseInstance& base_inst, ResourceAPI* api, QString debugName,
|
|||
|
||||
ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
||||
{
|
||||
auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile();
|
||||
auto profile = static_cast<const MinecraftInstance&>(m_base_instance).getPackProfile();
|
||||
|
||||
Q_ASSERT(profile);
|
||||
Q_ASSERT(m_filter);
|
||||
|
|
@ -50,7 +50,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
|||
ResourceAPI::VersionSearchArgs ModModel::createVersionsArguments(const QModelIndex& entry)
|
||||
{
|
||||
auto pack = m_packs[entry.row()];
|
||||
auto profile = static_cast<MinecraftInstance const&>(m_base_instance).getPackProfile();
|
||||
auto profile = static_cast<const MinecraftInstance&>(m_base_instance).getPackProfile();
|
||||
|
||||
Q_ASSERT(profile);
|
||||
Q_ASSERT(m_filter);
|
||||
|
|
@ -128,7 +128,7 @@ bool ModModel::checkVersionFilters(const ModPlatform::IndexedVersion& v)
|
|||
(!loaders.has_value() || !v.loaders || loaders.value() & v.loaders) && // loaders
|
||||
checkSide(m_filter->side, v.side) && // side
|
||||
(m_filter->releases.empty() || // releases
|
||||
std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.version_type) != m_filter->releases.cend()) &&
|
||||
std::find(m_filter->releases.cbegin(), m_filter->releases.cend(), v.versionType) != m_filter->releases.cend()) &&
|
||||
m_filter->checkMcVersions(v.mcVersion)); // mcVersions
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ void ResourceModel::search()
|
|||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int networkErrorCode) {
|
||||
callbacks.onFail = [this](QString reason, int networkErrorCode) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -157,14 +157,14 @@ void ResourceModel::search()
|
|||
}
|
||||
searchRequestFailed(std::move(reason), networkErrorCode);
|
||||
};
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onAbort = [this] {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
searchRequestAborted();
|
||||
};
|
||||
|
||||
callbacks.on_succeed = [this](auto& pack) {
|
||||
callbacks.onSucceed = [this](auto& pack) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -182,19 +182,19 @@ void ResourceModel::search()
|
|||
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
callbacks.on_succeed = [this](auto& doc) {
|
||||
callbacks.onSucceed = [this](auto& doc) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
searchRequestSucceeded(doc);
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int networkErrorCode) {
|
||||
callbacks.onFail = [this](QString reason, int networkErrorCode) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
searchRequestFailed(std::move(reason), networkErrorCode);
|
||||
};
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onAbort = [this] {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -220,16 +220,16 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
|
|||
|
||||
auto addonId = pack->addonId;
|
||||
// Use default if no callbacks are set
|
||||
if (!callbacks.on_succeed) {
|
||||
callbacks.on_succeed = [this, entry, addonId](auto& doc) {
|
||||
if (!callbacks.onSucceed) {
|
||||
callbacks.onSucceed = [this, entry, addonId](auto& doc) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
versionRequestSucceeded(doc, addonId, entry);
|
||||
};
|
||||
}
|
||||
if (!callbacks.on_fail) {
|
||||
callbacks.on_fail = [](const QString& reason, int) {
|
||||
if (!callbacks.onFail) {
|
||||
callbacks.onFail = [](const QString& reason, int) {
|
||||
QMessageBox::critical(nullptr, tr("Error"),
|
||||
tr("A network error occurred. Could not load project versions: %1").arg(reason));
|
||||
};
|
||||
|
|
@ -244,19 +244,19 @@ void ResourceModel::loadEntry(const QModelIndex& entry)
|
|||
auto args{ createInfoArguments(entry) };
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks{};
|
||||
|
||||
callbacks.on_succeed = [this, entry](auto& newpack) {
|
||||
callbacks.onSucceed = [this, entry](auto& newpack) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
infoRequestSucceeded(newpack, entry);
|
||||
};
|
||||
callbacks.on_fail = [this](const QString& reason, int) {
|
||||
callbacks.onFail = [this](const QString& reason, int) {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load project info: %1").arg(reason));
|
||||
};
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onAbort = [this] {
|
||||
if (!s_running_models.constFind(this).value()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -521,7 +521,7 @@ void ResourceModel::addPack(ModPlatform::IndexedPack::Ptr pack,
|
|||
bool isIndexed,
|
||||
QString downloadReason)
|
||||
{
|
||||
version.is_currently_selected = true;
|
||||
version.isCurrentlySelected = true;
|
||||
m_selected.append(makeShared<ResourceDownloadTask>(std::move(pack), version, packs, isIndexed, std::move(downloadReason)));
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +547,7 @@ void ResourceModel::removePack(const QString& rem)
|
|||
return;
|
||||
}
|
||||
for (auto& ver : pack->get()->versions) {
|
||||
ver.is_currently_selected = false;
|
||||
ver.isCurrentlySelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ class ResourceModel : public QAbstractListModel {
|
|||
/* Default search request callbacks */
|
||||
void searchRequestSucceeded(QList<ModPlatform::IndexedPack::Ptr>&);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
|
||||
void searchRequestFailed(QString reason, int network_error_code);
|
||||
void searchRequestFailed(QString reason, int networkErrorCode);
|
||||
void searchRequestAborted();
|
||||
|
||||
void versionRequestSucceeded(QVector<ModPlatform::IndexedVersion>&, QVariant, const QModelIndex&);
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ void ResourcePage::addSortings()
|
|||
std::ranges::sort(sorts, [](const auto& l, const auto& r) { return l.index < r.index; });
|
||||
|
||||
for (auto&& sorting : sorts) {
|
||||
m_ui->sortByBox->addItem(sorting.readable_name, QVariant(sorting.index));
|
||||
m_ui->sortByBox->addItem(sorting.readableName, QVariant(sorting.index));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,8 +318,8 @@ void ResourcePage::versionListUpdated(const QModelIndex& index)
|
|||
}
|
||||
|
||||
auto versionText = version.version;
|
||||
if (version.version_type.isValid()) {
|
||||
versionText += QString(" [%1]").arg(version.version_type.toString());
|
||||
if (version.versionType.isValid()) {
|
||||
versionText += QString(" [%1]").arg(version.versionType.toString());
|
||||
}
|
||||
if (version.fileId == installedVersion) {
|
||||
versionText += tr(" [installed]", "Mod version select");
|
||||
|
|
@ -427,7 +427,7 @@ void ResourcePage::onResourceSelected()
|
|||
|
||||
auto& version = currentPack->versions[m_selectedVersionIndex];
|
||||
Q_ASSERT(!version.downloadUrl.isNull());
|
||||
if (version.is_currently_selected) {
|
||||
if (version.isCurrentlySelected) {
|
||||
removeResourceFromDialog(currentPack->name);
|
||||
} else {
|
||||
addResourceToDialog(currentPack, version);
|
||||
|
|
|
|||
|
|
@ -173,14 +173,14 @@ void ListModel::performPaginatedSearch()
|
|||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int network_error_code) {
|
||||
if (network_error_code == 404) {
|
||||
callbacks.onFail = [this](QString reason, int networkErrorCode) {
|
||||
if (networkErrorCode == 404) {
|
||||
m_searchState = ResetRequested;
|
||||
}
|
||||
searchRequestFailed(reason);
|
||||
};
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onSucceed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.onAbort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
|
@ -198,9 +198,9 @@ void ListModel::performPaginatedSearch()
|
|||
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onSucceed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.onFail = [this](QString reason, int) { searchRequestFailed(reason); };
|
||||
callbacks.onAbort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
|
|||
|
||||
auto addonId = m_current->addonId;
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this, curr, addonId](auto& doc) {
|
||||
callbacks.onSucceed = [this, curr, addonId](auto& doc) {
|
||||
if (addonId != m_current->addonId) {
|
||||
return; // wrong request
|
||||
}
|
||||
|
|
@ -200,7 +200,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
|
|||
}
|
||||
suggestCurrent();
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
callbacks.onFail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -141,14 +141,14 @@ void ModpackListModel::performPaginatedSearch()
|
|||
if (!projectId.isEmpty()) {
|
||||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
callbacks.on_fail = [this](QString reason, int network_error_code) {
|
||||
if (network_error_code == 404) {
|
||||
callbacks.onFail = [this](QString reason, int networkErrorCode) {
|
||||
if (networkErrorCode == 404) {
|
||||
m_searchState = ResetRequested;
|
||||
}
|
||||
searchRequestFailed(reason, network_error_code);
|
||||
searchRequestFailed(reason, networkErrorCode);
|
||||
};
|
||||
callbacks.on_succeed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onSucceed = [this](auto& pack) { searchRequestForOneSucceeded(pack); };
|
||||
callbacks.onAbort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted", 0);
|
||||
};
|
||||
|
|
@ -166,9 +166,9 @@ void ModpackListModel::performPaginatedSearch()
|
|||
|
||||
ResourceAPI::Callback<QList<ModPlatform::IndexedPack::Ptr>> callbacks{};
|
||||
|
||||
callbacks.on_succeed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.on_fail = [this](QString reason, int network_error_code) { searchRequestFailed(reason, network_error_code); };
|
||||
callbacks.on_abort = [this] {
|
||||
callbacks.onSucceed = [this](auto& doc) { searchRequestFinished(doc); };
|
||||
callbacks.onFail = [this](QString reason, int networkErrorCode) { searchRequestFailed(reason, networkErrorCode); };
|
||||
callbacks.onAbort = [this] {
|
||||
qCritical() << "Search task aborted by an unknown reason!";
|
||||
searchRequestFailed("Aborted", 0);
|
||||
};
|
||||
|
|
@ -322,12 +322,12 @@ void ModpackListModel::searchRequestForOneSucceeded(ModPlatform::IndexedPack::Pt
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void ModpackListModel::searchRequestFailed(QString reason, int network_error_code)
|
||||
void ModpackListModel::searchRequestFailed(QString reason, int networkErrorCode)
|
||||
{
|
||||
if (network_error_code == -1) {
|
||||
if (networkErrorCode == -1) {
|
||||
// Unknown error in network stack
|
||||
QMessageBox::critical(nullptr, tr("Error"), tr("A network error occurred. Could not load modpacks."));
|
||||
} else if (network_error_code == 409) {
|
||||
} else if (networkErrorCode == 409) {
|
||||
// 409 Gone, notify user to update
|
||||
QMessageBox::critical(nullptr, tr("Error"),
|
||||
//: %1 refers to the launcher itself
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class ModpackListModel : public QAbstractListModel {
|
|||
|
||||
public slots:
|
||||
void searchRequestFinished(QList<ModPlatform::IndexedPack::Ptr>& doc_all);
|
||||
void searchRequestFailed(QString reason, int network_error_code);
|
||||
void searchRequestFailed(QString reason, int networkErrorCode);
|
||||
void searchRequestForOneSucceeded(ModPlatform::IndexedPack::Ptr);
|
||||
|
||||
protected slots:
|
||||
|
|
|
|||
|
|
@ -150,10 +150,10 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
ResourceAPI::Callback<ModPlatform::IndexedPack::Ptr> callbacks;
|
||||
|
||||
auto id = m_current->addonId;
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
callbacks.onFail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
callbacks.on_succeed = [this, id, curr](auto& pack) {
|
||||
callbacks.onSucceed = [this, id, curr](auto& pack) {
|
||||
if (id != m_current->addonId) {
|
||||
return; // wrong request?
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
|
||||
auto addonId = m_current->addonId;
|
||||
// Use default if no callbacks are set
|
||||
callbacks.on_succeed = [this, curr, addonId](auto& doc) {
|
||||
callbacks.onSucceed = [this, curr, addonId](auto& doc) {
|
||||
if (addonId != m_current->addonId) {
|
||||
return; // wrong request
|
||||
}
|
||||
|
|
@ -215,7 +215,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
|
||||
suggestCurrent();
|
||||
};
|
||||
callbacks.on_fail = [this](QString reason, int) {
|
||||
callbacks.onFail = [this](QString reason, int) {
|
||||
CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec();
|
||||
};
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
|
|||
} else {
|
||||
for (auto version : m_current->versions) {
|
||||
if (!version.version.contains(version.version))
|
||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.version_number),
|
||||
m_ui->versionSelectionBox->addItem(QString("%1 - %2").arg(version.version, version.versionNumber),
|
||||
QVariant(version.fileId));
|
||||
else
|
||||
m_ui->versionSelectionBox->addItem(version.version, QVariant(version.fileId));
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class ModFilterWidget : public QTabWidget {
|
|||
{
|
||||
return ((!loaders || !v.loaders || loaders & v.loaders) && // loaders
|
||||
(releases.empty() || // releases
|
||||
std::find(releases.cbegin(), releases.cend(), v.version_type) != releases.cend()) &&
|
||||
std::find(releases.cbegin(), releases.cend(), v.versionType) != releases.cend()) &&
|
||||
checkMcVersions({ v.mcVersion })); // gameVersion}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue