Merge branch 'develop' into feat/acknowledge_release_type
Signed-off-by: Alexandru Ionut Tripon <alexandru.tripon97@gmail.com>
This commit is contained in:
@ -145,7 +145,8 @@ void EnsureMetadataTask::executeTask()
|
||||
connect(project_task.get(), &Task::finished, this, [=] {
|
||||
invalidade_leftover();
|
||||
project_task->deleteLater();
|
||||
m_current_task = nullptr;
|
||||
if (m_current_task)
|
||||
m_current_task.reset();
|
||||
});
|
||||
|
||||
m_current_task = project_task;
|
||||
@ -154,7 +155,8 @@ void EnsureMetadataTask::executeTask()
|
||||
|
||||
connect(version_task.get(), &Task::finished, [=] {
|
||||
version_task->deleteLater();
|
||||
m_current_task = nullptr;
|
||||
if (m_current_task)
|
||||
m_current_task.reset();
|
||||
});
|
||||
|
||||
if (m_mods.size() > 1)
|
||||
|
@ -35,10 +35,7 @@ class EnsureMetadataTask : public Task {
|
||||
auto flameProjectsTask() -> Task::Ptr;
|
||||
|
||||
// Helpers
|
||||
enum class RemoveFromList {
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
enum class RemoveFromList { Yes, No };
|
||||
void emitReady(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
void emitFail(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes);
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
@ -138,11 +138,17 @@ auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString t
|
||||
}
|
||||
|
||||
QCryptographicHash hash(algo);
|
||||
if(!hash.addData(device))
|
||||
if (!hash.addData(device))
|
||||
qCritical() << "Failed to read JAR to create hash!";
|
||||
|
||||
Q_ASSERT(hash.result().length() == hash.hashLength(algo));
|
||||
return { hash.result().toHex() };
|
||||
}
|
||||
|
||||
QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
||||
{
|
||||
return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") +
|
||||
projectID.toString();
|
||||
}
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
@ -159,6 +159,7 @@ struct IndexedPack {
|
||||
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
|
||||
}
|
||||
};
|
||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||
|
||||
struct OverrideDep {
|
||||
QString quilt;
|
||||
@ -175,6 +176,7 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
||||
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
||||
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
||||
};
|
||||
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||
|
||||
} // namespace ModPlatform
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -21,23 +21,20 @@
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
static void loadIndexedVersion(ATLauncher::IndexedVersion & v, QJsonObject & obj)
|
||||
static void loadIndexedVersion(ATLauncher::IndexedVersion& v, QJsonObject& obj)
|
||||
{
|
||||
v.version = Json::requireString(obj, "version");
|
||||
v.minecraft = Json::requireString(obj, "minecraft");
|
||||
}
|
||||
|
||||
void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack & m, QJsonObject & obj)
|
||||
void ATLauncher::loadIndexedPack(ATLauncher::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
m.id = Json::requireInteger(obj, "id");
|
||||
m.position = Json::requireInteger(obj, "position");
|
||||
m.name = Json::requireString(obj, "name");
|
||||
m.type = Json::requireString(obj, "type") == "private" ?
|
||||
ATLauncher::PackType::Private :
|
||||
ATLauncher::PackType::Public;
|
||||
m.type = Json::requireString(obj, "type") == "private" ? ATLauncher::PackType::Private : ATLauncher::PackType::Public;
|
||||
auto versionsArr = Json::requireArray(obj, "versions");
|
||||
for (const auto versionRaw : versionsArr)
|
||||
{
|
||||
for (const auto versionRaw : versionsArr) {
|
||||
auto versionObj = Json::requireObject(versionRaw);
|
||||
ATLauncher::IndexedVersion version;
|
||||
loadIndexedVersion(version, versionObj);
|
||||
|
@ -18,21 +18,18 @@
|
||||
|
||||
#include "ATLPackManifest.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QMetaType>
|
||||
|
||||
namespace ATLauncher
|
||||
{
|
||||
namespace ATLauncher {
|
||||
|
||||
struct IndexedVersion
|
||||
{
|
||||
struct IndexedVersion {
|
||||
QString version;
|
||||
QString minecraft;
|
||||
};
|
||||
|
||||
struct IndexedPack
|
||||
{
|
||||
struct IndexedPack {
|
||||
int id;
|
||||
int position;
|
||||
QString name;
|
||||
@ -44,7 +41,7 @@ struct IndexedPack
|
||||
QString safeName;
|
||||
};
|
||||
|
||||
void loadIndexedPack(IndexedPack & m, QJsonObject & obj);
|
||||
}
|
||||
void loadIndexedPack(IndexedPack& m, QJsonObject& obj);
|
||||
} // namespace ATLauncher
|
||||
|
||||
Q_DECLARE_METATYPE(ATLauncher::IndexedPack)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -40,27 +40,27 @@
|
||||
|
||||
#include <quazip/quazip.h>
|
||||
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "Version.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "MMCZip.h"
|
||||
#include "Version.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/OneSixVersionFormat.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "net/ChecksumValidator.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
namespace ATLauncher {
|
||||
|
||||
static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version);
|
||||
|
||||
PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString packName, QString version, InstallMode installMode)
|
||||
PackInstallTask::PackInstallTask(UserInteractionSupport* support, QString packName, QString version, InstallMode installMode)
|
||||
{
|
||||
m_support = support;
|
||||
m_pack_name = packName;
|
||||
@ -71,8 +71,7 @@ PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString packNa
|
||||
|
||||
bool PackInstallTask::abort()
|
||||
{
|
||||
if(abortable)
|
||||
{
|
||||
if (abortable) {
|
||||
return jobPtr->abort();
|
||||
}
|
||||
return false;
|
||||
@ -110,12 +109,9 @@ void PackInstallTask::onDownloadSucceeded()
|
||||
auto obj = doc.object();
|
||||
|
||||
ATLauncher::PackVersion version;
|
||||
try
|
||||
{
|
||||
try {
|
||||
ATLauncher::loadVersion(version, obj);
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
} catch (const JSONValidationError& e) {
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
@ -126,20 +122,20 @@ void PackInstallTask::onDownloadSucceeded()
|
||||
bool resetDirectory;
|
||||
|
||||
switch (m_install_mode) {
|
||||
case InstallMode::Reinstall:
|
||||
case InstallMode::Update:
|
||||
message = m_version.messages.update;
|
||||
resetDirectory = true;
|
||||
break;
|
||||
case InstallMode::Reinstall:
|
||||
case InstallMode::Update:
|
||||
message = m_version.messages.update;
|
||||
resetDirectory = true;
|
||||
break;
|
||||
|
||||
case InstallMode::Install:
|
||||
message = m_version.messages.install;
|
||||
resetDirectory = false;
|
||||
break;
|
||||
case InstallMode::Install:
|
||||
message = m_version.messages.install;
|
||||
resetDirectory = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
emitFailed(tr("Unsupported installation mode"));
|
||||
return;
|
||||
default:
|
||||
emitFailed(tr("Unsupported installation mode"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Display message if one exists
|
||||
@ -157,10 +153,9 @@ void PackInstallTask::onDownloadSucceeded()
|
||||
deleteExistingFiles();
|
||||
}
|
||||
|
||||
if(m_version.noConfigs) {
|
||||
if (m_version.noConfigs) {
|
||||
downloadMods();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
installConfigs();
|
||||
}
|
||||
}
|
||||
@ -212,11 +207,9 @@ void PackInstallTask::deleteExistingFiles()
|
||||
|
||||
if (base == "root") {
|
||||
return minecraftPath;
|
||||
}
|
||||
else if (base == "config") {
|
||||
} else if (base == "config") {
|
||||
return FS::PathCombine(minecraftPath, "config");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
qWarning() << "Unrecognised base path" << base;
|
||||
return minecraftPath;
|
||||
}
|
||||
@ -338,19 +331,18 @@ QString PackInstallTask::getDirForModType(ModType type, QString raw)
|
||||
|
||||
QString PackInstallTask::getVersionForLoader(QString uid)
|
||||
{
|
||||
if(m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) {
|
||||
if (m_version.loader.recommended || m_version.loader.latest || m_version.loader.choose) {
|
||||
auto vlist = APPLICATION->metadataIndex()->get(uid);
|
||||
if(!vlist)
|
||||
{
|
||||
if (!vlist) {
|
||||
emitFailed(tr("Failed to get local metadata index for %1").arg(uid));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
if(!vlist->isLoaded()) {
|
||||
if (!vlist->isLoaded()) {
|
||||
vlist->load(Net::Mode::Online);
|
||||
}
|
||||
|
||||
if(m_version.loader.recommended || m_version.loader.latest) {
|
||||
if (m_version.loader.recommended || m_version.loader.latest) {
|
||||
for (int i = 0; i < vlist->versions().size(); i++) {
|
||||
auto version = vlist->versions().at(i);
|
||||
auto reqs = version->requiredSet();
|
||||
@ -359,16 +351,17 @@ QString PackInstallTask::getVersionForLoader(QString uid)
|
||||
// not all mod loaders depend on a given Minecraft version, so we won't do this
|
||||
// filtering for those loaders.
|
||||
if (m_version.loader.type != "fabric") {
|
||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require &req) {
|
||||
return req.uid == "net.minecraft";
|
||||
});
|
||||
if (iter == reqs.end()) continue;
|
||||
if (iter->equalsVersion != m_version.minecraft) continue;
|
||||
auto iter = std::find_if(reqs.begin(), reqs.end(), [](const Meta::Require& req) { return req.uid == "net.minecraft"; });
|
||||
if (iter == reqs.end())
|
||||
continue;
|
||||
if (iter->equalsVersion != m_version.minecraft)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_version.loader.recommended) {
|
||||
// first recommended build we find, we use.
|
||||
if (!version->isRecommended()) continue;
|
||||
if (!version->isRecommended())
|
||||
continue;
|
||||
}
|
||||
|
||||
return version->descriptor();
|
||||
@ -376,8 +369,7 @@ QString PackInstallTask::getVersionForLoader(QString uid)
|
||||
|
||||
emitFailed(tr("Failed to find version for %1 loader").arg(m_version.loader.type));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
else if(m_version.loader.choose) {
|
||||
} else if (m_version.loader.choose) {
|
||||
// Fabric Loader doesn't depend on a given Minecraft version.
|
||||
if (m_version.loader.type == "fabric") {
|
||||
return m_support->chooseVersion(vlist, Q_NULLPTR);
|
||||
@ -414,15 +406,14 @@ QString PackInstallTask::detectLibrary(VersionLibrary library)
|
||||
return group + ":" + artefact + ":" + version;
|
||||
}
|
||||
|
||||
if(library.file.contains("-")) {
|
||||
if (library.file.contains("-")) {
|
||||
auto lastSlash = library.file.lastIndexOf("-");
|
||||
auto name = library.file.mid(0, lastSlash);
|
||||
auto version = library.file.mid(lastSlash + 1).remove(".jar");
|
||||
|
||||
if(name == QString("guava")) {
|
||||
if (name == QString("guava")) {
|
||||
return "com.google.guava:guava:" + version;
|
||||
}
|
||||
else if(name == QString("commons-lang3")) {
|
||||
} else if (name == QString("commons-lang3")) {
|
||||
return "org.apache.commons:commons-lang3:" + version;
|
||||
}
|
||||
}
|
||||
@ -432,22 +423,22 @@ QString PackInstallTask::detectLibrary(VersionLibrary library)
|
||||
|
||||
bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile)
|
||||
{
|
||||
if(m_version.libraries.isEmpty()) {
|
||||
if (m_version.libraries.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<GradleSpecifier> exempt;
|
||||
for(const auto & componentUid : componentsToInstall.keys()) {
|
||||
for (const auto& componentUid : componentsToInstall.keys()) {
|
||||
auto componentVersion = componentsToInstall.value(componentUid);
|
||||
|
||||
for(const auto & library : componentVersion->data()->libraries) {
|
||||
for (const auto& library : componentVersion->data()->libraries) {
|
||||
GradleSpecifier lib(library->rawName());
|
||||
exempt.append(lib);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for(const auto & library : minecraftVersion->data()->libraries) {
|
||||
for (const auto& library : minecraftVersion->data()->libraries) {
|
||||
GradleSpecifier lib(library->rawName());
|
||||
exempt.append(lib);
|
||||
}
|
||||
@ -458,8 +449,7 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
|
||||
auto target_id = "org.multimc.atlauncher." + id;
|
||||
|
||||
auto patchDir = FS::PathCombine(instanceRoot, "patches");
|
||||
if(!FS::ensureFolderPathExists(patchDir))
|
||||
{
|
||||
if (!FS::ensureFolderPathExists(patchDir)) {
|
||||
return false;
|
||||
}
|
||||
auto patchFileName = FS::PathCombine(patchDir, target_id + ".json");
|
||||
@ -468,38 +458,24 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
|
||||
f->name = m_pack_name + " " + m_version_name + " (libraries)";
|
||||
|
||||
const static QMap<QString, QString> liteLoaderMap = {
|
||||
{ "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" },
|
||||
{ "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" },
|
||||
{ "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" },
|
||||
{ "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" },
|
||||
{ "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" },
|
||||
{ "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" },
|
||||
{ "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" },
|
||||
{ "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" },
|
||||
{ "7983e4b28217c9ae8569074388409c86", "1.7.10_03" },
|
||||
{ "c09882458d74fe0697c7681b8993097e", "1.7.10_02" },
|
||||
{ "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" },
|
||||
{ "6e9028816027f53957bd8fcdfabae064", "1.8" },
|
||||
{ "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" },
|
||||
{ "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" },
|
||||
{ "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" },
|
||||
{ "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" },
|
||||
{ "53639d52340479ccf206a04f5e16606f", "1.5.2_01" },
|
||||
{ "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" },
|
||||
{ "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" },
|
||||
{ "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" },
|
||||
{ "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" },
|
||||
{ "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" },
|
||||
{ "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" },
|
||||
{ "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" },
|
||||
{ "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" },
|
||||
{ "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" },
|
||||
{ "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" },
|
||||
{ "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" },
|
||||
{ "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" }
|
||||
{ "61179803bcd5fb7790789b790908663d", "1.12-SNAPSHOT" }, { "1420785ecbfed5aff4a586c5c9dd97eb", "1.12.2-SNAPSHOT" },
|
||||
{ "073f68e2fcb518b91fd0d99462441714", "1.6.2_03" }, { "10a15b52fc59b1bfb9c05b56de1097d6", "1.6.2_02" },
|
||||
{ "b52f90f08303edd3d4c374e268a5acf1", "1.6.2_04" }, { "ea747e24e03e24b7cad5bc8a246e0319", "1.6.2_01" },
|
||||
{ "55785ccc82c07ff0ba038fe24be63ea2", "1.7.10_01" }, { "63ada46e033d0cb6782bada09ad5ca4e", "1.7.10_04" },
|
||||
{ "7983e4b28217c9ae8569074388409c86", "1.7.10_03" }, { "c09882458d74fe0697c7681b8993097e", "1.7.10_02" },
|
||||
{ "db7235aefd407ac1fde09a7baba50839", "1.7.10_00" }, { "6e9028816027f53957bd8fcdfabae064", "1.8" },
|
||||
{ "5e732dc446f9fe2abe5f9decaec40cde", "1.10-SNAPSHOT" }, { "3a98b5ed95810bf164e71c1a53be568d", "1.11.2-SNAPSHOT" },
|
||||
{ "ba8e6285966d7d988a96496f48cbddaa", "1.8.9-SNAPSHOT" }, { "8524af3ac3325a82444cc75ae6e9112f", "1.11-SNAPSHOT" },
|
||||
{ "53639d52340479ccf206a04f5e16606f", "1.5.2_01" }, { "1fcdcf66ce0a0806b7ad8686afdce3f7", "1.6.4_00" },
|
||||
{ "531c116f71ae2b11033f9a11a0f8e668", "1.6.4_01" }, { "4009eeb99c9068f608d3483a6439af88", "1.7.2_03" },
|
||||
{ "66f343354b8417abce1a10d557d2c6e9", "1.7.2_04" }, { "ab554c21f28fbc4ae9b098bcb5f4cceb", "1.7.2_05" },
|
||||
{ "e1d76a05a3723920e2f80a5e66c45f16", "1.7.2_02" }, { "00318cb0c787934d523f63cdfe8ddde4", "1.9-SNAPSHOT" },
|
||||
{ "986fd1ee9525cb0dcab7609401cef754", "1.9.4-SNAPSHOT" }, { "571ad5e6edd5ff40259570c9be588bb5", "1.9.4" },
|
||||
{ "1cdd72f7232e45551f16cc8ffd27ccf3", "1.10.2-SNAPSHOT" }, { "8a7c21f32d77ee08b393dd3921ced8eb", "1.10.2" },
|
||||
{ "b9bef8abc8dc309069aeba6fbbe58980", "1.12.1-SNAPSHOT" }
|
||||
};
|
||||
|
||||
for(const auto & lib : m_version.libraries) {
|
||||
for (const auto& lib : m_version.libraries) {
|
||||
// If the library is LiteLoader, we need to ignore it and handle it separately.
|
||||
if (liteLoaderMap.contains(lib.md5)) {
|
||||
auto ver = getComponentVersion("com.mumfrey.liteloader", liteLoaderMap.value(lib.md5));
|
||||
@ -513,18 +489,19 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
|
||||
GradleSpecifier libSpecifier(libName);
|
||||
|
||||
bool libExempt = false;
|
||||
for(const auto & existingLib : exempt) {
|
||||
if(libSpecifier.matchName(existingLib)) {
|
||||
for (const auto& existingLib : exempt) {
|
||||
if (libSpecifier.matchName(existingLib)) {
|
||||
// If the pack specifies a newer version of the lib, use that!
|
||||
libExempt = Version(libSpecifier.version()) >= Version(existingLib.version());
|
||||
}
|
||||
}
|
||||
if(libExempt) continue;
|
||||
if (libExempt)
|
||||
continue;
|
||||
|
||||
auto library = std::make_shared<Library>();
|
||||
library->setRawName(libName);
|
||||
|
||||
switch(lib.download) {
|
||||
switch (lib.download) {
|
||||
case DownloadType::Server:
|
||||
library->setAbsoluteUrl(BuildConfig.ATL_DOWNLOAD_SERVER_URL + lib.url);
|
||||
break;
|
||||
@ -540,15 +517,13 @@ bool PackInstallTask::createLibrariesComponent(QString instanceRoot, std::shared
|
||||
f->libraries.append(library);
|
||||
}
|
||||
|
||||
if(f->libraries.isEmpty()) {
|
||||
if (f->libraries.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QFile file(patchFileName);
|
||||
if (!file.open(QFile::WriteOnly))
|
||||
{
|
||||
qCritical() << "Error opening" << file.fileName()
|
||||
<< "for reading:" << file.errorString();
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
|
||||
@ -593,18 +568,17 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<
|
||||
auto target_id = "org.multimc.atlauncher." + id;
|
||||
|
||||
auto patchDir = FS::PathCombine(instanceRoot, "patches");
|
||||
if(!FS::ensureFolderPathExists(patchDir))
|
||||
{
|
||||
if (!FS::ensureFolderPathExists(patchDir)) {
|
||||
return false;
|
||||
}
|
||||
auto patchFileName = FS::PathCombine(patchDir, target_id + ".json");
|
||||
|
||||
QStringList mainClasses;
|
||||
QStringList tweakers;
|
||||
for(const auto & componentUid : componentsToInstall.keys()) {
|
||||
for (const auto& componentUid : componentsToInstall.keys()) {
|
||||
auto componentVersion = componentsToInstall.value(componentUid);
|
||||
|
||||
if(componentVersion->data()->mainClass != QString("")) {
|
||||
if (componentVersion->data()->mainClass != QString("")) {
|
||||
mainClasses.append(componentVersion->data()->mainClass);
|
||||
}
|
||||
tweakers.append(componentVersion->data()->addTweakers);
|
||||
@ -619,25 +593,24 @@ bool PackInstallTask::createPackComponent(QString instanceRoot, std::shared_ptr<
|
||||
// Parse out tweakers
|
||||
auto args = extraArguments.split(" ");
|
||||
QString previous;
|
||||
for(auto arg : args) {
|
||||
if(arg.startsWith("--tweakClass=") || previous == "--tweakClass") {
|
||||
for (auto arg : args) {
|
||||
if (arg.startsWith("--tweakClass=") || previous == "--tweakClass") {
|
||||
auto tweakClass = arg.remove("--tweakClass=");
|
||||
if(tweakers.contains(tweakClass)) continue;
|
||||
if (tweakers.contains(tweakClass))
|
||||
continue;
|
||||
|
||||
f->addTweakers.append(tweakClass);
|
||||
}
|
||||
previous = arg;
|
||||
}
|
||||
|
||||
if(f->mainClass == QString() && f->addTweakers.isEmpty()) {
|
||||
if (f->mainClass == QString() && f->addTweakers.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QFile file(patchFileName);
|
||||
if (!file.open(QFile::WriteOnly))
|
||||
{
|
||||
qCritical() << "Error opening" << file.fileName()
|
||||
<< "for reading:" << file.errorString();
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
qCritical() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
|
||||
return false;
|
||||
}
|
||||
file.write(OneSixVersionFormat::versionFileToJson(f).toJson());
|
||||
@ -654,8 +627,7 @@ void PackInstallTask::installConfigs()
|
||||
jobPtr.reset(new NetJob(tr("Config download"), APPLICATION->network()));
|
||||
|
||||
auto path = QString("Configs/%1/%2.zip").arg(m_pack_safe_name).arg(m_version_name);
|
||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip")
|
||||
.arg(m_pack_safe_name).arg(m_version_name);
|
||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "packs/%1/versions/%2/Configs.zip").arg(m_pack_safe_name).arg(m_version_name);
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", path);
|
||||
entry->setStale(true);
|
||||
|
||||
@ -667,25 +639,22 @@ void PackInstallTask::installConfigs()
|
||||
jobPtr->addNetAction(dl);
|
||||
archivePath = entry->getFullPath();
|
||||
|
||||
connect(jobPtr.get(), &NetJob::succeeded, this, [&]()
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::succeeded, this, [&]() {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
extractConfigs();
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::failed, [&](QString reason) {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
abortable = true;
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
||||
connect(jobPtr.get(), &NetJob::aborted, [&]{
|
||||
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(jobPtr.get(), &NetJob::aborted, [&] {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitAborted();
|
||||
@ -702,25 +671,20 @@ void PackInstallTask::extractConfigs()
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
QuaZip packZip(archivePath);
|
||||
if(!packZip.open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!packZip.open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Failed to open pack configs %1!").arg(archivePath));
|
||||
return;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/minecraft");
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
|
||||
extractDir.absolutePath() + "/minecraft");
|
||||
#else
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft");
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/minecraft");
|
||||
#endif
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, [&]()
|
||||
{
|
||||
downloadMods();
|
||||
});
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]()
|
||||
{
|
||||
emitAborted();
|
||||
});
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, [&]() { downloadMods(); });
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]() { emitAborted(); });
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
}
|
||||
|
||||
@ -751,15 +715,17 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
jarmods.clear();
|
||||
jobPtr.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
|
||||
for(const auto& mod : m_version.mods) {
|
||||
for (const auto& mod : m_version.mods) {
|
||||
// skip non-client mods
|
||||
if(!mod.client) continue;
|
||||
if (!mod.client)
|
||||
continue;
|
||||
|
||||
// skip optional mods that were not selected
|
||||
if(mod.optional && !selectedMods.contains(mod.name)) continue;
|
||||
if (mod.optional && !selectedMods.contains(mod.name))
|
||||
continue;
|
||||
|
||||
QString url;
|
||||
switch(mod.download) {
|
||||
switch (mod.download) {
|
||||
case DownloadType::Server:
|
||||
url = BuildConfig.ATL_DOWNLOAD_SERVER_URL + mod.url;
|
||||
break;
|
||||
@ -788,8 +754,7 @@ void PackInstallTask::downloadMods()
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
}
|
||||
else if(mod.type == ModType::Decomp) {
|
||||
} else if (mod.type == ModType::Decomp) {
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName);
|
||||
entry->setStale(true);
|
||||
modsToDecomp.insert(entry->getFullPath(), mod);
|
||||
@ -800,10 +765,10 @@ void PackInstallTask::downloadMods()
|
||||
dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Md5, rawMd5));
|
||||
}
|
||||
jobPtr->addNetAction(dl);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto relpath = getDirForModType(mod.type, mod.type_raw);
|
||||
if(relpath == Q_NULLPTR) continue;
|
||||
if (relpath == Q_NULLPTR)
|
||||
continue;
|
||||
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", cacheName);
|
||||
entry->setStale(true);
|
||||
@ -817,7 +782,7 @@ void PackInstallTask::downloadMods()
|
||||
|
||||
auto path = FS::PathCombine(m_stagingPath, "minecraft", relpath, mod.file);
|
||||
|
||||
if(mod.type == ModType::Forge) {
|
||||
if (mod.type == ModType::Forge) {
|
||||
auto ver = getComponentVersion("net.minecraftforge", mod.version);
|
||||
if (ver) {
|
||||
componentsToInstall.insert("net.minecraftforge", ver);
|
||||
@ -828,7 +793,7 @@ void PackInstallTask::downloadMods()
|
||||
jarmods.push_back(path);
|
||||
}
|
||||
|
||||
if(mod.type == ModType::Jar) {
|
||||
if (mod.type == ModType::Jar) {
|
||||
qDebug() << "Jarmod: " + path;
|
||||
jarmods.push_back(path);
|
||||
}
|
||||
@ -840,21 +805,18 @@ void PackInstallTask::downloadMods()
|
||||
}
|
||||
|
||||
connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModsDownloaded);
|
||||
connect(jobPtr.get(), &NetJob::failed, [&](QString reason)
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::failed, [&](QString reason) {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
abortable = true;
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
||||
connect(jobPtr.get(), &NetJob::aborted, [&]
|
||||
{
|
||||
connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(jobPtr.get(), &NetJob::aborted, [&] {
|
||||
abortable = false;
|
||||
jobPtr.reset();
|
||||
emitAborted();
|
||||
@ -863,60 +825,56 @@ void PackInstallTask::downloadMods()
|
||||
jobPtr->start();
|
||||
}
|
||||
|
||||
void PackInstallTask::onModsDownloaded() {
|
||||
void PackInstallTask::onModsDownloaded()
|
||||
{
|
||||
abortable = false;
|
||||
|
||||
qDebug() << "PackInstallTask::onModsDownloaded: " << QThread::currentThreadId();
|
||||
jobPtr.reset();
|
||||
|
||||
if(!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) {
|
||||
if (!modsToExtract.empty() || !modsToDecomp.empty() || !modsToCopy.empty()) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), &PackInstallTask::extractMods, this, modsToExtract, modsToDecomp, modsToCopy);
|
||||
m_modExtractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), &PackInstallTask::extractMods, this, modsToExtract, modsToDecomp, modsToCopy);
|
||||
#else
|
||||
m_modExtractFuture = QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
|
||||
m_modExtractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), this, &PackInstallTask::extractMods, modsToExtract, modsToDecomp, modsToCopy);
|
||||
#endif
|
||||
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onModsExtracted);
|
||||
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]()
|
||||
{
|
||||
emitAborted();
|
||||
});
|
||||
connect(&m_modExtractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, [&]() { emitAborted(); });
|
||||
m_modExtractFutureWatcher.setFuture(m_modExtractFuture);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
install();
|
||||
}
|
||||
}
|
||||
|
||||
void PackInstallTask::onModsExtracted() {
|
||||
void PackInstallTask::onModsExtracted()
|
||||
{
|
||||
qDebug() << "PackInstallTask::onModsExtracted: " << QThread::currentThreadId();
|
||||
if(m_modExtractFuture.result()) {
|
||||
if (m_modExtractFuture.result()) {
|
||||
install();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
emitFailed(tr("Failed to extract mods..."));
|
||||
}
|
||||
}
|
||||
|
||||
bool PackInstallTask::extractMods(
|
||||
const QMap<QString, VersionMod> &toExtract,
|
||||
const QMap<QString, VersionMod> &toDecomp,
|
||||
const QMap<QString, QString> &toCopy
|
||||
) {
|
||||
bool PackInstallTask::extractMods(const QMap<QString, VersionMod>& toExtract,
|
||||
const QMap<QString, VersionMod>& toDecomp,
|
||||
const QMap<QString, QString>& toCopy)
|
||||
{
|
||||
qDebug() << "PackInstallTask::extractMods: " << QThread::currentThreadId();
|
||||
|
||||
setStatus(tr("Extracting mods..."));
|
||||
for (auto iter = toExtract.begin(); iter != toExtract.end(); iter++) {
|
||||
auto &modPath = iter.key();
|
||||
auto &mod = iter.value();
|
||||
auto& modPath = iter.key();
|
||||
auto& mod = iter.value();
|
||||
|
||||
QString extractToDir;
|
||||
if(mod.type == ModType::Extract) {
|
||||
if (mod.type == ModType::Extract) {
|
||||
extractToDir = getDirForModType(mod.extractTo, mod.extractTo_raw);
|
||||
}
|
||||
else if(mod.type == ModType::TexturePackExtract) {
|
||||
} else if (mod.type == ModType::TexturePackExtract) {
|
||||
extractToDir = FS::PathCombine("texturepacks", "extracted");
|
||||
}
|
||||
else if(mod.type == ModType::ResourcePackExtract) {
|
||||
} else if (mod.type == ModType::ResourcePackExtract) {
|
||||
extractToDir = FS::PathCombine("resourcepacks", "extracted");
|
||||
}
|
||||
|
||||
@ -924,36 +882,36 @@ bool PackInstallTask::extractMods(
|
||||
auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir);
|
||||
|
||||
QString folderToExtract = "";
|
||||
if(mod.type == ModType::Extract) {
|
||||
if (mod.type == ModType::Extract) {
|
||||
folderToExtract = mod.extractFolder;
|
||||
folderToExtract.remove(QRegularExpression("^/"));
|
||||
}
|
||||
|
||||
qDebug() << "Extracting " + mod.file + " to " + extractToDir;
|
||||
if(!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) {
|
||||
if (!MMCZip::extractDir(modPath, folderToExtract, extractToPath)) {
|
||||
// assume error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = toDecomp.begin(); iter != toDecomp.end(); iter++) {
|
||||
auto &modPath = iter.key();
|
||||
auto &mod = iter.value();
|
||||
auto& modPath = iter.key();
|
||||
auto& mod = iter.value();
|
||||
auto extractToDir = getDirForModType(mod.decompType, mod.decompType_raw);
|
||||
|
||||
QDir extractDir(m_stagingPath);
|
||||
auto extractToPath = FS::PathCombine(extractDir.absolutePath(), "minecraft", extractToDir, mod.decompFile);
|
||||
|
||||
qDebug() << "Extracting " + mod.decompFile + " to " + extractToDir;
|
||||
if(!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) {
|
||||
if (!MMCZip::extractFile(modPath, mod.decompFile, extractToPath)) {
|
||||
qWarning() << "Failed to extract" << mod.decompFile;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = toCopy.begin(); iter != toCopy.end(); iter++) {
|
||||
auto &from = iter.key();
|
||||
auto &to = iter.value();
|
||||
auto& from = iter.key();
|
||||
auto& to = iter.value();
|
||||
|
||||
// If the file already exists, assume the mod is the correct copy - and remove
|
||||
// the copy from the Configs.zip
|
||||
@ -966,7 +924,7 @@ bool PackInstallTask::extractMods(
|
||||
}
|
||||
|
||||
FS::copy fileCopyOperation(from, to);
|
||||
if(!fileCopyOperation()) {
|
||||
if (!fileCopyOperation()) {
|
||||
qWarning() << "Failed to copy" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
@ -988,7 +946,7 @@ void PackInstallTask::install()
|
||||
components->buildingFromScratch();
|
||||
|
||||
// Use a component to add libraries BEFORE Minecraft
|
||||
if(!createLibrariesComponent(instance.instanceRoot(), components)) {
|
||||
if (!createLibrariesComponent(instance.instanceRoot(), components)) {
|
||||
emitFailed(tr("Failed to create libraries component"));
|
||||
return;
|
||||
}
|
||||
@ -997,27 +955,24 @@ void PackInstallTask::install()
|
||||
components->setComponentVersion("net.minecraft", m_version.minecraft, true);
|
||||
|
||||
// Loader
|
||||
if(m_version.loader.type == QString("forge"))
|
||||
{
|
||||
if (m_version.loader.type == QString("forge")) {
|
||||
auto version = getVersionForLoader("net.minecraftforge");
|
||||
if(version == Q_NULLPTR) return;
|
||||
if (version == Q_NULLPTR)
|
||||
return;
|
||||
|
||||
components->setComponentVersion("net.minecraftforge", version);
|
||||
}
|
||||
else if(m_version.loader.type == QString("fabric"))
|
||||
{
|
||||
} else if (m_version.loader.type == QString("fabric")) {
|
||||
auto version = getVersionForLoader("net.fabricmc.fabric-loader");
|
||||
if(version == Q_NULLPTR) return;
|
||||
if (version == Q_NULLPTR)
|
||||
return;
|
||||
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", version);
|
||||
}
|
||||
else if(m_version.loader.type != QString())
|
||||
{
|
||||
} else if (m_version.loader.type != QString()) {
|
||||
emitFailed(tr("Unknown loader type: ") + m_version.loader.type);
|
||||
return;
|
||||
}
|
||||
|
||||
for(const auto & componentUid : componentsToInstall.keys()) {
|
||||
for (const auto& componentUid : componentsToInstall.keys()) {
|
||||
auto version = componentsToInstall.value(componentUid);
|
||||
components->setComponentVersion(componentUid, version->version());
|
||||
}
|
||||
@ -1026,7 +981,7 @@ void PackInstallTask::install()
|
||||
|
||||
// Use a component to fill in the rest of the data
|
||||
// todo: use more detection
|
||||
if(!createPackComponent(instance.instanceRoot(), components)) {
|
||||
if (!createPackComponent(instance.instanceRoot(), components)) {
|
||||
emitFailed(tr("Failed to create pack component"));
|
||||
return;
|
||||
}
|
||||
@ -1061,4 +1016,4 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString&
|
||||
return ver;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ATLauncher
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -38,84 +38,67 @@
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
static ATLauncher::DownloadType parseDownloadType(QString rawType) {
|
||||
if(rawType == QString("server")) {
|
||||
static ATLauncher::DownloadType parseDownloadType(QString rawType)
|
||||
{
|
||||
if (rawType == QString("server")) {
|
||||
return ATLauncher::DownloadType::Server;
|
||||
}
|
||||
else if(rawType == QString("browser")) {
|
||||
} else if (rawType == QString("browser")) {
|
||||
return ATLauncher::DownloadType::Browser;
|
||||
}
|
||||
else if(rawType == QString("direct")) {
|
||||
} else if (rawType == QString("direct")) {
|
||||
return ATLauncher::DownloadType::Direct;
|
||||
}
|
||||
|
||||
return ATLauncher::DownloadType::Unknown;
|
||||
}
|
||||
|
||||
static ATLauncher::ModType parseModType(QString rawType) {
|
||||
static ATLauncher::ModType parseModType(QString rawType)
|
||||
{
|
||||
// See https://wiki.atlauncher.com/mod_types
|
||||
if(rawType == QString("root")) {
|
||||
if (rawType == QString("root")) {
|
||||
return ATLauncher::ModType::Root;
|
||||
}
|
||||
else if(rawType == QString("forge")) {
|
||||
} else if (rawType == QString("forge")) {
|
||||
return ATLauncher::ModType::Forge;
|
||||
}
|
||||
else if(rawType == QString("jar")) {
|
||||
} else if (rawType == QString("jar")) {
|
||||
return ATLauncher::ModType::Jar;
|
||||
}
|
||||
else if(rawType == QString("mods")) {
|
||||
} else if (rawType == QString("mods")) {
|
||||
return ATLauncher::ModType::Mods;
|
||||
}
|
||||
else if(rawType == QString("flan")) {
|
||||
} else if (rawType == QString("flan")) {
|
||||
return ATLauncher::ModType::Flan;
|
||||
}
|
||||
else if(rawType == QString("dependency") || rawType == QString("depandency")) {
|
||||
} else if (rawType == QString("dependency") || rawType == QString("depandency")) {
|
||||
return ATLauncher::ModType::Dependency;
|
||||
}
|
||||
else if(rawType == QString("ic2lib")) {
|
||||
} else if (rawType == QString("ic2lib")) {
|
||||
return ATLauncher::ModType::Ic2Lib;
|
||||
}
|
||||
else if(rawType == QString("denlib")) {
|
||||
} else if (rawType == QString("denlib")) {
|
||||
return ATLauncher::ModType::DenLib;
|
||||
}
|
||||
else if(rawType == QString("coremods")) {
|
||||
} else if (rawType == QString("coremods")) {
|
||||
return ATLauncher::ModType::Coremods;
|
||||
}
|
||||
else if(rawType == QString("mcpc")) {
|
||||
} else if (rawType == QString("mcpc")) {
|
||||
return ATLauncher::ModType::MCPC;
|
||||
}
|
||||
else if(rawType == QString("plugins")) {
|
||||
} else if (rawType == QString("plugins")) {
|
||||
return ATLauncher::ModType::Plugins;
|
||||
}
|
||||
else if(rawType == QString("extract")) {
|
||||
} else if (rawType == QString("extract")) {
|
||||
return ATLauncher::ModType::Extract;
|
||||
}
|
||||
else if(rawType == QString("decomp")) {
|
||||
} else if (rawType == QString("decomp")) {
|
||||
return ATLauncher::ModType::Decomp;
|
||||
}
|
||||
else if(rawType == QString("texturepack")) {
|
||||
} else if (rawType == QString("texturepack")) {
|
||||
return ATLauncher::ModType::TexturePack;
|
||||
}
|
||||
else if(rawType == QString("resourcepack")) {
|
||||
} else if (rawType == QString("resourcepack")) {
|
||||
return ATLauncher::ModType::ResourcePack;
|
||||
}
|
||||
else if(rawType == QString("shaderpack")) {
|
||||
} else if (rawType == QString("shaderpack")) {
|
||||
return ATLauncher::ModType::ShaderPack;
|
||||
}
|
||||
else if(rawType == QString("texturepackextract")) {
|
||||
} else if (rawType == QString("texturepackextract")) {
|
||||
return ATLauncher::ModType::TexturePackExtract;
|
||||
}
|
||||
else if(rawType == QString("resourcepackextract")) {
|
||||
} else if (rawType == QString("resourcepackextract")) {
|
||||
return ATLauncher::ModType::ResourcePackExtract;
|
||||
}
|
||||
else if(rawType == QString("millenaire")) {
|
||||
} else if (rawType == QString("millenaire")) {
|
||||
return ATLauncher::ModType::Millenaire;
|
||||
}
|
||||
|
||||
return ATLauncher::ModType::Unknown;
|
||||
}
|
||||
|
||||
static void loadVersionLoader(ATLauncher::VersionLoader & p, QJsonObject & obj) {
|
||||
static void loadVersionLoader(ATLauncher::VersionLoader& p, QJsonObject& obj)
|
||||
{
|
||||
p.type = Json::requireString(obj, "type");
|
||||
p.choose = Json::ensureBoolean(obj, QString("choose"), false);
|
||||
|
||||
@ -134,7 +117,8 @@ static void loadVersionLoader(ATLauncher::VersionLoader & p, QJsonObject & obj)
|
||||
}
|
||||
}
|
||||
|
||||
static void loadVersionLibrary(ATLauncher::VersionLibrary & p, QJsonObject & obj) {
|
||||
static void loadVersionLibrary(ATLauncher::VersionLibrary& p, QJsonObject& obj)
|
||||
{
|
||||
p.url = Json::requireString(obj, "url");
|
||||
p.file = Json::requireString(obj, "file");
|
||||
p.md5 = Json::requireString(obj, "md5");
|
||||
@ -145,12 +129,14 @@ static void loadVersionLibrary(ATLauncher::VersionLibrary & p, QJsonObject & obj
|
||||
p.server = Json::ensureString(obj, "server", "");
|
||||
}
|
||||
|
||||
static void loadVersionConfigs(ATLauncher::VersionConfigs & p, QJsonObject & obj) {
|
||||
static void loadVersionConfigs(ATLauncher::VersionConfigs& p, QJsonObject& obj)
|
||||
{
|
||||
p.filesize = Json::requireInteger(obj, "filesize");
|
||||
p.sha1 = Json::requireString(obj, "sha1");
|
||||
}
|
||||
|
||||
static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
|
||||
static void loadVersionMod(ATLauncher::VersionMod& p, QJsonObject& obj)
|
||||
{
|
||||
p.name = Json::requireString(obj, "name");
|
||||
p.version = Json::requireString(obj, "version");
|
||||
p.url = Json::requireString(obj, "url");
|
||||
@ -167,18 +153,18 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
|
||||
// when the mod represents Forge. As there is little difference between "Jar" and "Forge, some
|
||||
// packs regretfully use "Jar". This will correct the type to "Forge" in these cases (as best
|
||||
// it can).
|
||||
if(p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) {
|
||||
if (p.name == QString("Minecraft Forge") && p.type == ATLauncher::ModType::Jar) {
|
||||
p.type_raw = "forge";
|
||||
p.type = ATLauncher::ModType::Forge;
|
||||
}
|
||||
|
||||
if(obj.contains("extractTo")) {
|
||||
if (obj.contains("extractTo")) {
|
||||
p.extractTo_raw = Json::requireString(obj, "extractTo");
|
||||
p.extractTo = parseModType(p.extractTo_raw);
|
||||
p.extractFolder = Json::ensureString(obj, "extractFolder", "").replace("%s%", "/");
|
||||
}
|
||||
|
||||
if(obj.contains("decompType")) {
|
||||
if (obj.contains("decompType")) {
|
||||
p.decompType_raw = Json::requireString(obj, "decompType");
|
||||
p.decompType = parseModType(p.decompType_raw);
|
||||
p.decompFile = Json::requireString(obj, "decompFile");
|
||||
@ -191,7 +177,7 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) {
|
||||
p.hidden = Json::ensureBoolean(obj, QString("hidden"), false);
|
||||
p.library = Json::ensureBoolean(obj, QString("library"), false);
|
||||
p.group = Json::ensureString(obj, QString("group"), "");
|
||||
if(obj.contains("depends")) {
|
||||
if (obj.contains("depends")) {
|
||||
auto dependsArr = Json::requireArray(obj, "depends");
|
||||
for (const auto depends : dependsArr) {
|
||||
p.depends.append(Json::requireString(depends));
|
||||
@ -282,31 +268,30 @@ static void loadVersionDeletes(ATLauncher::VersionDeletes& d, QJsonObject& obj)
|
||||
}
|
||||
}
|
||||
|
||||
void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
|
||||
void ATLauncher::loadVersion(PackVersion& v, QJsonObject& obj)
|
||||
{
|
||||
v.version = Json::requireString(obj, "version");
|
||||
v.minecraft = Json::requireString(obj, "minecraft");
|
||||
v.noConfigs = Json::ensureBoolean(obj, QString("noConfigs"), false);
|
||||
|
||||
if(obj.contains("mainClass")) {
|
||||
if (obj.contains("mainClass")) {
|
||||
auto main = Json::requireObject(obj, "mainClass");
|
||||
loadVersionMainClass(v.mainClass, main);
|
||||
}
|
||||
|
||||
if(obj.contains("extraArguments")) {
|
||||
if (obj.contains("extraArguments")) {
|
||||
auto arguments = Json::requireObject(obj, "extraArguments");
|
||||
loadVersionExtraArguments(v.extraArguments, arguments);
|
||||
}
|
||||
|
||||
if(obj.contains("loader")) {
|
||||
if (obj.contains("loader")) {
|
||||
auto loader = Json::requireObject(obj, "loader");
|
||||
loadVersionLoader(v.loader, loader);
|
||||
}
|
||||
|
||||
if(obj.contains("libraries")) {
|
||||
if (obj.contains("libraries")) {
|
||||
auto libraries = Json::requireArray(obj, "libraries");
|
||||
for (const auto libraryRaw : libraries)
|
||||
{
|
||||
for (const auto libraryRaw : libraries) {
|
||||
auto libraryObj = Json::requireObject(libraryRaw);
|
||||
ATLauncher::VersionLibrary target;
|
||||
loadVersionLibrary(target, libraryObj);
|
||||
@ -314,10 +299,9 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
|
||||
}
|
||||
}
|
||||
|
||||
if(obj.contains("mods")) {
|
||||
if (obj.contains("mods")) {
|
||||
auto mods = Json::requireArray(obj, "mods");
|
||||
for (const auto modRaw : mods)
|
||||
{
|
||||
for (const auto modRaw : mods) {
|
||||
auto modObj = Json::requireObject(modRaw);
|
||||
ATLauncher::VersionMod mod;
|
||||
loadVersionMod(mod, modObj);
|
||||
@ -325,18 +309,18 @@ void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj)
|
||||
}
|
||||
}
|
||||
|
||||
if(obj.contains("configs")) {
|
||||
if (obj.contains("configs")) {
|
||||
auto configsObj = Json::requireObject(obj, "configs");
|
||||
loadVersionConfigs(v.configs, configsObj);
|
||||
}
|
||||
|
||||
auto colourObj = Json::ensureObject(obj, "colours");
|
||||
for (const auto &key : colourObj.keys()) {
|
||||
for (const auto& key : colourObj.keys()) {
|
||||
v.colours[key] = Json::requireString(colourObj.value(key), "colour");
|
||||
}
|
||||
|
||||
auto warningsObj = Json::ensureObject(obj, "warnings");
|
||||
for (const auto &key : warningsObj.keys()) {
|
||||
for (const auto& key : warningsObj.keys()) {
|
||||
v.warnings[key] = Json::requireString(warningsObj.value(key), "warning");
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -40,17 +40,11 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
namespace ATLauncher
|
||||
{
|
||||
namespace ATLauncher {
|
||||
|
||||
enum class PackType
|
||||
{
|
||||
Public,
|
||||
Private
|
||||
};
|
||||
enum class PackType { Public, Private };
|
||||
|
||||
enum class ModType
|
||||
{
|
||||
enum class ModType {
|
||||
Root,
|
||||
Forge,
|
||||
Jar,
|
||||
@ -73,16 +67,9 @@ enum class ModType
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum class DownloadType
|
||||
{
|
||||
Server,
|
||||
Browser,
|
||||
Direct,
|
||||
Unknown
|
||||
};
|
||||
enum class DownloadType { Server, Browser, Direct, Unknown };
|
||||
|
||||
struct VersionLoader
|
||||
{
|
||||
struct VersionLoader {
|
||||
QString type;
|
||||
bool latest;
|
||||
bool recommended;
|
||||
@ -91,8 +78,7 @@ struct VersionLoader
|
||||
QString version;
|
||||
};
|
||||
|
||||
struct VersionLibrary
|
||||
{
|
||||
struct VersionLibrary {
|
||||
QString url;
|
||||
QString file;
|
||||
QString server;
|
||||
@ -101,8 +87,7 @@ struct VersionLibrary
|
||||
QString download_raw;
|
||||
};
|
||||
|
||||
struct VersionMod
|
||||
{
|
||||
struct VersionMod {
|
||||
QString name;
|
||||
QString version;
|
||||
QString url;
|
||||
@ -138,14 +123,12 @@ struct VersionMod
|
||||
bool effectively_hidden;
|
||||
};
|
||||
|
||||
struct VersionConfigs
|
||||
{
|
||||
struct VersionConfigs {
|
||||
int filesize;
|
||||
QString sha1;
|
||||
};
|
||||
|
||||
struct VersionMessages
|
||||
{
|
||||
struct VersionMessages {
|
||||
QString install;
|
||||
QString update;
|
||||
};
|
||||
@ -170,20 +153,17 @@ struct VersionDeletes {
|
||||
QVector<VersionDelete> folders;
|
||||
};
|
||||
|
||||
struct PackVersionMainClass
|
||||
{
|
||||
struct PackVersionMainClass {
|
||||
QString mainClass;
|
||||
QString depends;
|
||||
};
|
||||
|
||||
struct PackVersionExtraArguments
|
||||
{
|
||||
struct PackVersionExtraArguments {
|
||||
QString arguments;
|
||||
QString depends;
|
||||
};
|
||||
|
||||
struct PackVersion
|
||||
{
|
||||
struct PackVersion {
|
||||
QString version;
|
||||
QString minecraft;
|
||||
bool noConfigs;
|
||||
@ -203,6 +183,6 @@ struct PackVersion
|
||||
VersionDeletes deletes;
|
||||
};
|
||||
|
||||
void loadVersion(PackVersion & v, QJsonObject & obj);
|
||||
void loadVersion(PackVersion& v, QJsonObject& obj);
|
||||
|
||||
}
|
||||
} // namespace ATLauncher
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -57,4 +57,4 @@ void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ATLauncher
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -18,9 +18,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace ATLauncher {
|
||||
|
||||
@ -44,4 +44,4 @@ struct ShareCodeResponse {
|
||||
|
||||
void loadShareCodeResponse(ShareCodeResponse& r, QJsonObject& obj);
|
||||
|
||||
}
|
||||
} // namespace ATLauncher
|
||||
|
@ -21,6 +21,10 @@ bool Flame::FileResolvingTask::abort()
|
||||
|
||||
void Flame::FileResolvingTask::executeTask()
|
||||
{
|
||||
if (m_toProcess.files.isEmpty()) { // no file to resolve so leave it empty and emit success immediately
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
setStatus(tr("Resolving mod IDs..."));
|
||||
setProgress(0, 3);
|
||||
m_dljob.reset(new NetJob("Mod id resolver", m_network));
|
||||
@ -48,7 +52,7 @@ void Flame::FileResolvingTask::executeTask()
|
||||
stepProgress(*step_progress);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
|
||||
connect(m_dljob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_dljob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
@ -114,7 +118,7 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
stepProgress(*step_progress);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
|
||||
connect(m_checkJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_checkJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
@ -128,12 +132,13 @@ void Flame::FileResolvingTask::netJobFinished()
|
||||
m_checkJob->start();
|
||||
}
|
||||
|
||||
void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
void Flame::FileResolvingTask::modrinthCheckFinished()
|
||||
{
|
||||
setProgress(2, 3);
|
||||
qDebug() << "Finished with blocked mods : " << blockedProjects.size();
|
||||
|
||||
for (auto it = blockedProjects.keyBegin(); it != blockedProjects.keyEnd(); it++) {
|
||||
auto &out = *it;
|
||||
auto& out = *it;
|
||||
auto bytes = blockedProjects[out];
|
||||
if (!out->resolved) {
|
||||
continue;
|
||||
@ -153,15 +158,13 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
out->resolved = false;
|
||||
}
|
||||
}
|
||||
//copy to an output list and filter out projects found on modrinth
|
||||
// copy to an output list and filter out projects found on modrinth
|
||||
auto block = std::make_shared<QList<File*>>();
|
||||
auto it = blockedProjects.keys();
|
||||
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File *f) {
|
||||
return !f->resolved;
|
||||
});
|
||||
//Display not found mods early
|
||||
std::copy_if(it.begin(), it.end(), std::back_inserter(*block), [](File* f) { return !f->resolved; });
|
||||
// Display not found mods early
|
||||
if (!block->empty()) {
|
||||
//blocked mods found, we need the slug for displaying.... we need another job :D !
|
||||
// blocked mods found, we need the slug for displaying.... we need another job :D !
|
||||
m_slugJob.reset(new NetJob("Slug Job", m_network));
|
||||
int index = 0;
|
||||
for (auto mod : *block) {
|
||||
@ -173,8 +176,8 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
QObject::connect(dl.get(), &Net::Download::succeeded, [block, index, output]() {
|
||||
auto mod = block->at(index); // use the shared_ptr so it is captured and only freed when we are done
|
||||
auto json = QJsonDocument::fromJson(*output);
|
||||
auto base = Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json),"data"),"links"),
|
||||
"websiteUrl");
|
||||
auto base =
|
||||
Json::requireString(Json::requireObject(Json::requireObject(Json::requireObject(json), "data"), "links"), "websiteUrl");
|
||||
auto link = QString("%1/download/%2").arg(base, QString::number(mod->fileId));
|
||||
mod->websiteUrl = link;
|
||||
});
|
||||
@ -192,7 +195,7 @@ void Flame::FileResolvingTask::modrinthCheckFinished() {
|
||||
stepProgress(*step_progress);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propogateStepProgress);
|
||||
connect(m_slugJob.get(), &NetJob::stepProgress, this, &FileResolvingTask::propagateStepProgress);
|
||||
connect(m_slugJob.get(), &NetJob::progress, this, [this, step_progress](qint64 current, qint64 total) {
|
||||
qDebug() << "Resolve slug progress" << current << total;
|
||||
step_progress->update(current, total);
|
||||
|
@ -23,6 +23,8 @@ class FlameAPI : public NetworkResourceAPI {
|
||||
|
||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||
|
||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||
|
||||
private:
|
||||
static int getClassId(ModPlatform::ResourceType type)
|
||||
{
|
||||
|
@ -8,7 +8,10 @@ class FlameCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, std::optional<ResourceAPI::ModLoaderTypes> loaders, std::shared_ptr<ModFolderModel> mods_folder)
|
||||
FlameCheckUpdate(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
{}
|
||||
|
||||
|
@ -57,15 +57,11 @@
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/World.h"
|
||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||
|
||||
|
||||
const static QMap<QString, QString> forgemap = { { "1.2.5", "3.4.9.171" },
|
||||
{ "1.4.2", "6.0.1.355" },
|
||||
{ "1.4.7", "6.6.2.534" },
|
||||
{ "1.5.2", "7.8.1.737" } };
|
||||
|
||||
static const FlameAPI api;
|
||||
|
||||
bool FlameCreationTask::abort()
|
||||
@ -259,6 +255,56 @@ bool FlameCreationTask::updateInstance()
|
||||
return false;
|
||||
}
|
||||
|
||||
QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, QString loaderVersion, QString mcVersion)
|
||||
{
|
||||
if (loaderVersion == "recommended") {
|
||||
auto vlist = APPLICATION->metadataIndex()->get(uid);
|
||||
if (!vlist) {
|
||||
setError(tr("Failed to get local metadata index for %1").arg(uid));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!vlist->isLoaded()) {
|
||||
QEventLoop loadVersionLoop;
|
||||
auto task = vlist->getLoadTask();
|
||||
connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||
if (!task->isRunning())
|
||||
task->start();
|
||||
|
||||
loadVersionLoop.exec();
|
||||
}
|
||||
|
||||
for (auto version : vlist->versions()) {
|
||||
// first recommended build we find, we use.
|
||||
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") {
|
||||
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())
|
||||
continue;
|
||||
}
|
||||
return version->descriptor();
|
||||
}
|
||||
|
||||
setError(tr("Failed to find version for %1 loader").arg(loaderType));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (loaderVersion.isEmpty()) {
|
||||
emitFailed(tr("No loader version set for modpack!"));
|
||||
return {};
|
||||
}
|
||||
|
||||
return loaderVersion;
|
||||
}
|
||||
|
||||
bool FlameCreationTask::createInstance()
|
||||
{
|
||||
QEventLoop loop;
|
||||
@ -297,22 +343,29 @@ bool FlameCreationTask::createInstance()
|
||||
}
|
||||
}
|
||||
|
||||
QString forgeVersion;
|
||||
QString fabricVersion;
|
||||
// TODO: is Quilt relevant here?
|
||||
QString loaderType;
|
||||
QString loaderUid;
|
||||
QString loaderVersion;
|
||||
|
||||
for (auto& loader : m_pack.minecraft.modLoaders) {
|
||||
auto id = loader.id;
|
||||
if (id.startsWith("forge-")) {
|
||||
id.remove("forge-");
|
||||
forgeVersion = id;
|
||||
continue;
|
||||
}
|
||||
if (id.startsWith("fabric-")) {
|
||||
loaderType = "forge";
|
||||
loaderUid = "net.minecraftforge";
|
||||
} else if (id.startsWith("fabric-")) {
|
||||
id.remove("fabric-");
|
||||
fabricVersion = id;
|
||||
loaderType = "fabric";
|
||||
loaderUid = "net.fabricmc.fabric-loader";
|
||||
} else if (id.startsWith("quilt-")) {
|
||||
id.remove("quilt-");
|
||||
loaderType = "quilt";
|
||||
loaderUid = "org.quiltmc.quilt-loader";
|
||||
} else {
|
||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
||||
continue;
|
||||
}
|
||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
||||
loaderVersion = id;
|
||||
}
|
||||
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
@ -329,19 +382,12 @@ bool FlameCreationTask::createInstance()
|
||||
auto components = instance.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
||||
if (!forgeVersion.isEmpty()) {
|
||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
||||
if (forgeVersion == "recommended") {
|
||||
if (forgemap.contains(mcVersion)) {
|
||||
forgeVersion = forgemap[mcVersion];
|
||||
} else {
|
||||
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
|
||||
}
|
||||
}
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
||||
if (!loaderType.isEmpty()) {
|
||||
auto version = getVersionForLoader(loaderUid, loaderType, loaderVersion, mcVersion);
|
||||
if (version.isEmpty())
|
||||
return false;
|
||||
components->setComponentVersion(loaderUid, version);
|
||||
}
|
||||
if (!fabricVersion.isEmpty())
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
||||
|
||||
if (m_instIcon != "default") {
|
||||
instance.setIconKey(m_instIcon);
|
||||
@ -386,7 +432,7 @@ bool FlameCreationTask::createInstance()
|
||||
});
|
||||
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress);
|
||||
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus);
|
||||
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress);
|
||||
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propagateStepProgress);
|
||||
connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::details, this, &FlameCreationTask::setDetails);
|
||||
m_mod_id_resolver->start();
|
||||
|
||||
@ -470,8 +516,9 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||
switch (result.type) {
|
||||
case Flame::File::Type::Folder: {
|
||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
|
||||
}
|
||||
/* fallthrough */
|
||||
case Flame::File::Type::SingleFile:
|
||||
case Flame::File::Type::Mod: {
|
||||
if (!result.url.isEmpty()) {
|
||||
@ -501,11 +548,11 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||
m_files_job.reset();
|
||||
setError(reason);
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total){
|
||||
connect(m_files_job.get(), &NetJob::progress, this, [this](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress);
|
||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propagateStepProgress);
|
||||
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
setStatus(tr("Downloading mods..."));
|
||||
@ -544,7 +591,6 @@ void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||
setAbortable(true);
|
||||
}
|
||||
|
||||
|
||||
void FlameCreationTask::validateZIPResouces()
|
||||
{
|
||||
qDebug() << "Validating whether resources stored as .zip are in the right place";
|
||||
@ -562,11 +608,13 @@ void FlameCreationTask::validateZIPResouces()
|
||||
if (FS::move(localPath, destPath)) {
|
||||
return destPath;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
|
||||
}
|
||||
return localPath;
|
||||
};
|
||||
|
||||
auto installWorld = [this](QString worldPath){
|
||||
auto installWorld = [this](QString worldPath) {
|
||||
qDebug() << "Installing World from" << worldPath;
|
||||
QFileInfo worldFileInfo(worldPath);
|
||||
World w(worldFileInfo);
|
||||
@ -583,29 +631,29 @@ void FlameCreationTask::validateZIPResouces()
|
||||
QString worldPath;
|
||||
|
||||
switch (type) {
|
||||
case PackedResourceType::ResourcePack :
|
||||
validatePath(fileName, targetFolder, "resourcepacks");
|
||||
break;
|
||||
case PackedResourceType::TexturePack :
|
||||
validatePath(fileName, targetFolder, "texturepacks");
|
||||
break;
|
||||
case PackedResourceType::DataPack :
|
||||
validatePath(fileName, targetFolder, "datapacks");
|
||||
break;
|
||||
case PackedResourceType::Mod :
|
||||
case PackedResourceType::Mod:
|
||||
validatePath(fileName, targetFolder, "mods");
|
||||
break;
|
||||
case PackedResourceType::ShaderPack :
|
||||
case PackedResourceType::ResourcePack:
|
||||
validatePath(fileName, targetFolder, "resourcepacks");
|
||||
break;
|
||||
case PackedResourceType::TexturePack:
|
||||
validatePath(fileName, targetFolder, "texturepacks");
|
||||
break;
|
||||
case PackedResourceType::DataPack:
|
||||
validatePath(fileName, targetFolder, "datapacks");
|
||||
break;
|
||||
case PackedResourceType::ShaderPack:
|
||||
// in theroy flame API can't do this but who knows, that *may* change ?
|
||||
// better to handle it if it *does* occure in the future
|
||||
validatePath(fileName, targetFolder, "shaderpacks");
|
||||
break;
|
||||
case PackedResourceType::WorldSave :
|
||||
case PackedResourceType::WorldSave:
|
||||
worldPath = validatePath(fileName, targetFolder, "saves");
|
||||
installWorld(worldPath);
|
||||
break;
|
||||
case PackedResourceType::UNKNOWN :
|
||||
default :
|
||||
case PackedResourceType::UNKNOWN:
|
||||
default:
|
||||
qDebug() << "Can't Identify" << fileName << "at" << localPath << ", leaving it where it is.";
|
||||
break;
|
||||
}
|
||||
|
@ -57,10 +57,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
QString id,
|
||||
QString version_id,
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask()
|
||||
, m_parent(parent)
|
||||
, m_managed_id(std::move(id))
|
||||
, m_managed_version_id(std::move(version_id))
|
||||
: InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
@ -78,6 +75,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
||||
void setupDownloadJob(QEventLoop&);
|
||||
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||
void validateZIPResouces();
|
||||
QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion);
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
|
431
launcher/modplatform/flame/FlamePackExportTask.cpp
Normal file
431
launcher/modplatform/flame/FlamePackExportTask.cpp
Normal file
@ -0,0 +1,431 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "FlamePackExportTask.h"
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include "Json.h"
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
#include "modplatform/helpers/HashUtils.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
const QString FlamePackExportTask::TEMPLATE = "<li><a href=\"{url}\">{name}{authors}</a></li>\n";
|
||||
const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" });
|
||||
|
||||
FlamePackExportTask::FlamePackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& author,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter)
|
||||
: name(name)
|
||||
, version(version)
|
||||
, author(author)
|
||||
, instance(instance)
|
||||
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||
, gameRoot(instance->gameRoot())
|
||||
, output(output)
|
||||
, filter(filter)
|
||||
{}
|
||||
|
||||
void FlamePackExportTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Searching for files..."));
|
||||
setProgress(0, 5);
|
||||
collectFiles();
|
||||
}
|
||||
|
||||
bool FlamePackExportTask::abort()
|
||||
{
|
||||
if (task) {
|
||||
task->abort();
|
||||
emitAborted();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FlamePackExportTask::collectFiles()
|
||||
{
|
||||
setAbortable(false);
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
files.clear();
|
||||
if (!MMCZip::collectFileListRecursively(instance->gameRoot(), nullptr, &files, filter)) {
|
||||
emitFailed(tr("Could not search for files"));
|
||||
return;
|
||||
}
|
||||
|
||||
pendingHashes.clear();
|
||||
resolvedFiles.clear();
|
||||
|
||||
if (mcInstance != nullptr) {
|
||||
mcInstance->loaderModList()->update();
|
||||
connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &FlamePackExportTask::collectHashes);
|
||||
} else
|
||||
collectHashes();
|
||||
}
|
||||
|
||||
void FlamePackExportTask::collectHashes()
|
||||
{
|
||||
setAbortable(true);
|
||||
setStatus(tr("Finding file hashes..."));
|
||||
setProgress(1, 5);
|
||||
auto allMods = mcInstance->loaderModList()->allMods();
|
||||
ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||
task.reset(hashingTask);
|
||||
for (const QFileInfo& file : files) {
|
||||
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
|
||||
// require sensible file types
|
||||
if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) {
|
||||
return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled");
|
||||
}))
|
||||
continue;
|
||||
|
||||
if (relative.startsWith("resourcepacks/") &&
|
||||
(relative.endsWith(".zip") || relative.endsWith(".zip.disabled"))) { // is resourcepack
|
||||
auto hashTask = Hashing::createFlameHasher(file.absoluteFilePath());
|
||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, relative, file](QString hash) {
|
||||
if (m_state == Task::State::Running) {
|
||||
pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") });
|
||||
}
|
||||
});
|
||||
connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
|
||||
hashingTask->addTask(hashTask);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; });
|
||||
modIter != allMods.end()) {
|
||||
const Mod* mod = *modIter;
|
||||
if (!mod || mod->type() == ResourceType::FOLDER) {
|
||||
continue;
|
||||
}
|
||||
if (mod->metadata() && mod->metadata()->provider == ModPlatform::ResourceProvider::FLAME) {
|
||||
resolvedFiles.insert(mod->fileinfo().absoluteFilePath(),
|
||||
{ mod->metadata()->project_id.toInt(), mod->metadata()->file_id.toInt(), mod->enabled(), true,
|
||||
mod->metadata()->name, mod->metadata()->slug, mod->authors().join(", ") });
|
||||
continue;
|
||||
}
|
||||
|
||||
auto hashTask = Hashing::createFlameHasher(mod->fileinfo().absoluteFilePath());
|
||||
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) {
|
||||
if (m_state == Task::State::Running) {
|
||||
pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true });
|
||||
}
|
||||
});
|
||||
connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
|
||||
hashingTask->addTask(hashTask);
|
||||
}
|
||||
}
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(hashingTask.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(hashingTask.get(), &Task::succeeded, this, &FlamePackExportTask::makeApiRequest);
|
||||
connect(hashingTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress);
|
||||
|
||||
connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(hashingTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
hashingTask->start();
|
||||
}
|
||||
|
||||
void FlamePackExportTask::makeApiRequest()
|
||||
{
|
||||
if (pendingHashes.isEmpty()) {
|
||||
buildZip();
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(tr("Finding versions for hashes..."));
|
||||
setProgress(2, 5);
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QList<uint> fingerprints;
|
||||
for (auto& murmur : pendingHashes.keys()) {
|
||||
fingerprints.push_back(murmur.toUInt());
|
||||
}
|
||||
|
||||
task.reset(api.matchFingerprints(fingerprints, response));
|
||||
|
||||
connect(task.get(), &Task::succeeded, this, [this, response] {
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from CurseForge::CurrentVersions at " << parseError.offset
|
||||
<< " reason: " << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
|
||||
failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto docObj = Json::requireObject(doc);
|
||||
auto dataObj = Json::requireObject(docObj, "data");
|
||||
auto dataArr = Json::requireArray(dataObj, "exactMatches");
|
||||
|
||||
if (dataArr.isEmpty()) {
|
||||
qWarning() << "No matches found for fingerprint search!";
|
||||
|
||||
return;
|
||||
}
|
||||
for (auto match : dataArr) {
|
||||
auto matchObj = Json::ensureObject(match, {});
|
||||
auto fileObj = Json::ensureObject(matchObj, "file", {});
|
||||
|
||||
if (matchObj.isEmpty() || fileObj.isEmpty()) {
|
||||
qWarning() << "Fingerprint match is empty!";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto fingerprint = QString::number(Json::ensureVariant(fileObj, "fileFingerprint").toUInt());
|
||||
auto mod = pendingHashes.find(fingerprint);
|
||||
if (mod == pendingHashes.end()) {
|
||||
qWarning() << "Invalid fingerprint from the API response.";
|
||||
continue;
|
||||
}
|
||||
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name));
|
||||
if (Json::ensureBoolean(fileObj, "isAvailable", false, "isAvailable"))
|
||||
resolvedFiles.insert(mod->path, { Json::requireInteger(fileObj, "modId"), Json::requireInteger(fileObj, "id"),
|
||||
mod->enabled, mod->isMod });
|
||||
}
|
||||
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << doc;
|
||||
}
|
||||
pendingHashes.clear();
|
||||
});
|
||||
connect(task.get(), &Task::finished, this, &FlamePackExportTask::getProjectsInfo);
|
||||
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::emitFailed);
|
||||
task->start();
|
||||
}
|
||||
|
||||
void FlamePackExportTask::getProjectsInfo()
|
||||
{
|
||||
setStatus(tr("Finding project info from CurseForge..."));
|
||||
setProgress(3, 5);
|
||||
QStringList addonIds;
|
||||
for (const auto& resolved : resolvedFiles) {
|
||||
if (resolved.slug.isEmpty()) {
|
||||
addonIds << QString::number(resolved.addonId);
|
||||
}
|
||||
}
|
||||
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
Task::Ptr projTask;
|
||||
|
||||
if (addonIds.isEmpty()) {
|
||||
buildZip();
|
||||
return;
|
||||
} else if (addonIds.size() == 1) {
|
||||
projTask = api.getProject(*addonIds.begin(), response);
|
||||
} else {
|
||||
projTask = api.getProjects(addonIds, response);
|
||||
}
|
||||
|
||||
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 CurseForge projects task at " << parseError.offset
|
||||
<< " reason: " << parseError.errorString();
|
||||
qWarning() << *response;
|
||||
failed(parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
QJsonArray entries;
|
||||
if (addonIds.size() == 1)
|
||||
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||
else
|
||||
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||
|
||||
for (auto entry : entries) {
|
||||
auto entryObj = Json::requireObject(entry);
|
||||
|
||||
try {
|
||||
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(Json::requireString(entryObj, "name")));
|
||||
|
||||
ModPlatform::IndexedPack pack;
|
||||
FlameMod::loadIndexedPack(pack, entryObj);
|
||||
for (auto key : resolvedFiles.keys()) {
|
||||
auto val = resolvedFiles.value(key);
|
||||
if (val.addonId == pack.addonId) {
|
||||
val.name = pack.name;
|
||||
val.slug = pack.slug;
|
||||
QStringList authors;
|
||||
for (auto author : pack.authors)
|
||||
authors << author.name;
|
||||
|
||||
val.authors = authors.join(", ");
|
||||
resolvedFiles[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << entries;
|
||||
}
|
||||
}
|
||||
} catch (Json::JsonException& e) {
|
||||
qDebug() << e.cause();
|
||||
qDebug() << doc;
|
||||
}
|
||||
buildZip();
|
||||
});
|
||||
task.reset(projTask);
|
||||
task->start();
|
||||
}
|
||||
|
||||
void FlamePackExportTask::buildZip()
|
||||
{
|
||||
setStatus(tr("Adding files..."));
|
||||
setProgress(4, 5);
|
||||
|
||||
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
|
||||
zipTask->addExtraFile("manifest.json", generateIndex());
|
||||
zipTask->addExtraFile("modlist.html", generateHTML());
|
||||
|
||||
QStringList exclude;
|
||||
std::transform(resolvedFiles.keyBegin(), resolvedFiles.keyEnd(), std::back_insert_iterator(exclude),
|
||||
[this](QString file) { return gameRoot.relativeFilePath(file); });
|
||||
zipTask->setExcludeFiles(exclude);
|
||||
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
|
||||
connect(zipTask.get(), &Task::succeeded, this, &FlamePackExportTask::emitSucceeded);
|
||||
connect(zipTask.get(), &Task::aborted, this, &FlamePackExportTask::emitAborted);
|
||||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(zipTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propagateStepProgress);
|
||||
|
||||
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
task.reset(zipTask);
|
||||
zipTask->start();
|
||||
}
|
||||
|
||||
QByteArray FlamePackExportTask::generateIndex()
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["manifestType"] = "minecraftModpack";
|
||||
obj["manifestVersion"] = 1;
|
||||
obj["name"] = name;
|
||||
obj["version"] = version;
|
||||
obj["author"] = author;
|
||||
obj["overrides"] = "overrides";
|
||||
if (mcInstance) {
|
||||
QJsonObject version;
|
||||
auto profile = mcInstance->getPackProfile();
|
||||
// collect all supported components
|
||||
const ComponentPtr minecraft = profile->getComponent("net.minecraft");
|
||||
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||
|
||||
// convert all available components to mrpack dependencies
|
||||
if (minecraft != nullptr)
|
||||
version["version"] = minecraft->m_version;
|
||||
QString id;
|
||||
if (quilt != nullptr)
|
||||
id = "quilt-" + quilt->getVersion();
|
||||
else if (fabric != nullptr)
|
||||
id = "fabric-" + fabric->getVersion();
|
||||
else if (forge != nullptr)
|
||||
id = "forge-" + forge->getVersion();
|
||||
version["modLoaders"] = QJsonArray();
|
||||
if (!id.isEmpty()) {
|
||||
QJsonObject loader;
|
||||
loader["id"] = id;
|
||||
loader["primary"] = true;
|
||||
version["modLoaders"] = QJsonArray({ loader });
|
||||
}
|
||||
obj["minecraft"] = version;
|
||||
}
|
||||
|
||||
QJsonArray files;
|
||||
for (auto mod : resolvedFiles) {
|
||||
QJsonObject file;
|
||||
file["projectID"] = mod.addonId;
|
||||
file["fileID"] = mod.version;
|
||||
file["required"] = mod.enabled;
|
||||
files << file;
|
||||
}
|
||||
obj["files"] = files;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
QByteArray FlamePackExportTask::generateHTML()
|
||||
{
|
||||
QString content = "";
|
||||
for (auto mod : resolvedFiles) {
|
||||
if (mod.isMod) {
|
||||
content += QString(TEMPLATE)
|
||||
.replace("{name}", mod.name.toHtmlEscaped())
|
||||
.replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped())
|
||||
.replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : "");
|
||||
}
|
||||
}
|
||||
content = "<ul>" + content + "</ul>";
|
||||
return content.toUtf8();
|
||||
}
|
85
launcher/modplatform/flame/FlamePackExportTask.h
Normal file
85
launcher/modplatform/flame/FlamePackExportTask.h
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class FlamePackExportTask : public Task {
|
||||
public:
|
||||
FlamePackExportTask(const QString& name,
|
||||
const QString& version,
|
||||
const QString& author,
|
||||
InstancePtr instance,
|
||||
const QString& output,
|
||||
MMCZip::FilterFunction filter);
|
||||
|
||||
protected:
|
||||
void executeTask() override;
|
||||
bool abort() override;
|
||||
|
||||
private:
|
||||
static const QString TEMPLATE;
|
||||
static const QStringList FILE_EXTENSIONS;
|
||||
|
||||
// inputs
|
||||
const QString name, version, author;
|
||||
const InstancePtr instance;
|
||||
MinecraftInstance* mcInstance;
|
||||
const QDir gameRoot;
|
||||
const QString output;
|
||||
const MMCZip::FilterFunction filter;
|
||||
|
||||
struct ResolvedFile {
|
||||
int addonId;
|
||||
int version;
|
||||
bool enabled;
|
||||
bool isMod;
|
||||
|
||||
QString name;
|
||||
QString slug;
|
||||
QString authors;
|
||||
};
|
||||
struct HashInfo {
|
||||
QString name;
|
||||
QString path;
|
||||
bool enabled;
|
||||
bool isMod;
|
||||
};
|
||||
|
||||
FlameAPI api;
|
||||
|
||||
QFileInfoList files;
|
||||
QMap<QString, HashInfo> pendingHashes{};
|
||||
QMap<QString, ResolvedFile> resolvedFiles{};
|
||||
Task::Ptr task;
|
||||
|
||||
void collectFiles();
|
||||
void collectHashes();
|
||||
void makeApiRequest();
|
||||
void getProjectsInfo();
|
||||
void buildZip();
|
||||
|
||||
QByteArray generateIndex();
|
||||
QByteArray generateHTML();
|
||||
};
|
@ -54,23 +54,22 @@ void Flame::loadIndexedInfo(IndexedPack& pack, QJsonObject& obj)
|
||||
auto links_obj = Json::ensureObject(obj, "links");
|
||||
|
||||
pack.extra.websiteUrl = Json::ensureString(links_obj, "websiteUrl");
|
||||
if(pack.extra.websiteUrl.endsWith('/'))
|
||||
if (pack.extra.websiteUrl.endsWith('/'))
|
||||
pack.extra.websiteUrl.chop(1);
|
||||
|
||||
pack.extra.issuesUrl = Json::ensureString(links_obj, "issuesUrl");
|
||||
if(pack.extra.issuesUrl.endsWith('/'))
|
||||
if (pack.extra.issuesUrl.endsWith('/'))
|
||||
pack.extra.issuesUrl.chop(1);
|
||||
|
||||
pack.extra.sourceUrl = Json::ensureString(links_obj, "sourceUrl");
|
||||
if(pack.extra.sourceUrl.endsWith('/'))
|
||||
if (pack.extra.sourceUrl.endsWith('/'))
|
||||
pack.extra.sourceUrl.chop(1);
|
||||
|
||||
pack.extra.wikiUrl = Json::ensureString(links_obj, "wikiUrl");
|
||||
if(pack.extra.wikiUrl.endsWith('/'))
|
||||
if (pack.extra.wikiUrl.endsWith('/'))
|
||||
pack.extra.wikiUrl.chop(1);
|
||||
|
||||
pack.extraInfoLoaded = true;
|
||||
|
||||
}
|
||||
|
||||
void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr)
|
||||
|
@ -46,7 +46,7 @@ static void loadManifestV1(Flame::Manifest& pack, QJsonObject& manifest)
|
||||
Flame::File file;
|
||||
loadFileV1(file, obj);
|
||||
|
||||
pack.files.insert(file.fileId,file);
|
||||
pack.files.insert(file.fileId, file);
|
||||
}
|
||||
|
||||
pack.overrides = Json::ensureString(manifest, "overrides", "overrides");
|
||||
@ -69,24 +69,19 @@ void Flame::loadManifest(Flame::Manifest& m, const QString& filepath)
|
||||
loadManifestV1(m, obj);
|
||||
}
|
||||
|
||||
bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked)
|
||||
bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked)
|
||||
{
|
||||
fileName = Json::requireString(obj, "fileName");
|
||||
// This is a piece of a Flame project JSON pulled out into the file metadata (here) for convenience
|
||||
// It is also optional
|
||||
type = File::Type::SingleFile;
|
||||
|
||||
if (fileName.endsWith(".zip")) {
|
||||
// this is probably a resource pack
|
||||
targetFolder = "resourcepacks";
|
||||
} else {
|
||||
// this is probably a mod, dunno what else could modpacks download
|
||||
targetFolder = "mods";
|
||||
}
|
||||
targetFolder = "mods";
|
||||
|
||||
// get the hash
|
||||
hash = QString();
|
||||
auto hashes = Json::ensureArray(obj, "hashes");
|
||||
for(QJsonValueRef item : hashes) {
|
||||
for (QJsonValueRef item : hashes) {
|
||||
auto hobj = Json::requireObject(item);
|
||||
auto algo = Json::requireInteger(hobj, "algo");
|
||||
auto value = Json::requireString(hobj, "value");
|
||||
@ -95,7 +90,6 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// may throw, if the project is blocked
|
||||
QString rawUrl = Json::ensureString(obj, "downloadUrl");
|
||||
url = QUrl(rawUrl, QUrl::TolerantMode);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -41,10 +41,8 @@
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
namespace Flame
|
||||
{
|
||||
struct File
|
||||
{
|
||||
namespace Flame {
|
||||
struct File {
|
||||
// NOTE: throws JSONValidationError
|
||||
bool parseFromObject(const QJsonObject& object, bool throw_on_blocked = true);
|
||||
|
||||
@ -61,45 +59,33 @@ struct File
|
||||
QString fileName;
|
||||
QUrl url;
|
||||
QString targetFolder = QStringLiteral("mods");
|
||||
enum class Type
|
||||
{
|
||||
Unknown,
|
||||
Folder,
|
||||
Ctoc,
|
||||
SingleFile,
|
||||
Cmod2,
|
||||
Modpack,
|
||||
Mod
|
||||
} type = Type::Mod;
|
||||
enum class Type { Unknown, Folder, Ctoc, SingleFile, Cmod2, Modpack, Mod } type = Type::Mod;
|
||||
};
|
||||
|
||||
struct Modloader
|
||||
{
|
||||
struct Modloader {
|
||||
QString id;
|
||||
bool primary = false;
|
||||
};
|
||||
|
||||
struct Minecraft
|
||||
{
|
||||
struct Minecraft {
|
||||
QString version;
|
||||
QString libraries;
|
||||
QVector<Flame::Modloader> modLoaders;
|
||||
};
|
||||
|
||||
struct Manifest
|
||||
{
|
||||
struct Manifest {
|
||||
QString manifestType;
|
||||
int manifestVersion = 0;
|
||||
Flame::Minecraft minecraft;
|
||||
QString name;
|
||||
QString version;
|
||||
QString author;
|
||||
//File id -> File
|
||||
QMap<int,Flame::File> files;
|
||||
// File id -> File
|
||||
QMap<int, Flame::File> files;
|
||||
QString overrides;
|
||||
|
||||
bool is_loaded = false;
|
||||
};
|
||||
|
||||
void loadManifest(Flame::Manifest & m, const QString &filepath);
|
||||
}
|
||||
void loadManifest(Flame::Manifest& m, const QString& filepath);
|
||||
} // namespace Flame
|
||||
|
200
launcher/modplatform/helpers/ExportToModList.cpp
Normal file
200
launcher/modplatform/helpers/ExportToModList.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "ExportToModList.h"
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace ExportToModList {
|
||||
QString toHTML(QList<Mod*> mods, OptionalData extraData)
|
||||
{
|
||||
QStringList lines;
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name().toHtmlEscaped();
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl().toHtmlEscaped();
|
||||
if (!url.isEmpty())
|
||||
modName = QString("<a href=\"%1\">%2</a>").arg(url, modName);
|
||||
}
|
||||
auto line = modName;
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
if (!ver.isEmpty())
|
||||
line += QString(" [%1]").arg(ver.toHtmlEscaped());
|
||||
}
|
||||
if (extraData & Authors && !mod->authors().isEmpty())
|
||||
line += " by " + mod->authors().join(", ").toHtmlEscaped();
|
||||
lines.append(QString("<li>%1</li>").arg(line));
|
||||
}
|
||||
return QString("<html><body><ul>\n\t%1\n</ul></body></html>").arg(lines.join("\n\t"));
|
||||
}
|
||||
|
||||
QString toMarkdown(QList<Mod*> mods, OptionalData extraData)
|
||||
{
|
||||
QStringList lines;
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
if (!url.isEmpty())
|
||||
modName = QString("[%1](%2)").arg(modName, url);
|
||||
}
|
||||
auto line = modName;
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
if (!ver.isEmpty())
|
||||
line += QString(" [%1]").arg(ver);
|
||||
}
|
||||
if (extraData & Authors && !mod->authors().isEmpty())
|
||||
line += " by " + mod->authors().join(", ");
|
||||
lines << "- " + line;
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
QString toPlainTXT(QList<Mod*> mods, OptionalData extraData)
|
||||
{
|
||||
QStringList lines;
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
|
||||
auto line = modName;
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
if (!url.isEmpty())
|
||||
line += QString(" (%1)").arg(url);
|
||||
}
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
if (!ver.isEmpty())
|
||||
line += QString(" [%1]").arg(ver);
|
||||
}
|
||||
if (extraData & Authors && !mod->authors().isEmpty())
|
||||
line += " by " + mod->authors().join(", ");
|
||||
lines << line;
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
QString toJSON(QList<Mod*> mods, OptionalData extraData)
|
||||
{
|
||||
QJsonArray lines;
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
QJsonObject line;
|
||||
line["name"] = modName;
|
||||
if (extraData & Url) {
|
||||
auto url = mod->metaurl();
|
||||
if (!url.isEmpty())
|
||||
line["url"] = url;
|
||||
}
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
if (!ver.isEmpty())
|
||||
line["version"] = ver;
|
||||
}
|
||||
if (extraData & Authors && !mod->authors().isEmpty())
|
||||
line["authors"] = QJsonArray::fromStringList(mod->authors());
|
||||
lines << line;
|
||||
}
|
||||
QJsonDocument doc;
|
||||
doc.setArray(lines);
|
||||
return doc.toJson();
|
||||
}
|
||||
|
||||
QString toCSV(QList<Mod*> mods, OptionalData extraData)
|
||||
{
|
||||
QStringList lines;
|
||||
for (auto mod : mods) {
|
||||
QStringList data;
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
|
||||
data << modName;
|
||||
if (extraData & Url)
|
||||
data << mod->metaurl();
|
||||
if (extraData & Version) {
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
data << ver;
|
||||
}
|
||||
if (extraData & Authors) {
|
||||
QString authors;
|
||||
if (mod->authors().length() == 1)
|
||||
authors = mod->authors().back();
|
||||
else if (mod->authors().length() > 1)
|
||||
authors = QString("\"%1\"").arg(mod->authors().join(","));
|
||||
data << authors;
|
||||
}
|
||||
lines << data.join(",");
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
QString exportToModList(QList<Mod*> mods, Formats format, OptionalData extraData)
|
||||
{
|
||||
switch (format) {
|
||||
case HTML:
|
||||
return toHTML(mods, extraData);
|
||||
case MARKDOWN:
|
||||
return toMarkdown(mods, extraData);
|
||||
case PLAINTXT:
|
||||
return toPlainTXT(mods, extraData);
|
||||
case JSON:
|
||||
return toJSON(mods, extraData);
|
||||
case CSV:
|
||||
return toCSV(mods, extraData);
|
||||
default: {
|
||||
return QString("unknown format:%1").arg(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString exportToModList(QList<Mod*> mods, QString lineTemplate)
|
||||
{
|
||||
QStringList lines;
|
||||
for (auto mod : mods) {
|
||||
auto meta = mod->metadata();
|
||||
auto modName = mod->name();
|
||||
auto url = mod->metaurl();
|
||||
auto ver = mod->version();
|
||||
if (ver.isEmpty() && meta != nullptr)
|
||||
ver = meta->version().toString();
|
||||
auto authors = mod->authors().join(", ");
|
||||
lines << QString(lineTemplate)
|
||||
.replace("{name}", modName)
|
||||
.replace("{url}", url)
|
||||
.replace("{version}", ver)
|
||||
.replace("{authors}", authors);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
} // namespace ExportToModList
|
33
launcher/modplatform/helpers/ExportToModList.h
Normal file
33
launcher/modplatform/helpers/ExportToModList.h
Normal file
@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include "minecraft/mod/Mod.h"
|
||||
|
||||
namespace ExportToModList {
|
||||
|
||||
enum Formats { HTML, MARKDOWN, PLAINTXT, JSON, CSV, CUSTOM };
|
||||
enum OptionalData {
|
||||
Authors = 1 << 0,
|
||||
Url = 1 << 1,
|
||||
Version = 1 << 2,
|
||||
};
|
||||
QString exportToModList(QList<Mod*> mods, Formats format, OptionalData extraData);
|
||||
QString exportToModList(QList<Mod*> mods, QString lineTemplate);
|
||||
} // namespace ExportToModList
|
87
launcher/modplatform/import_ftb/PackHelpers.cpp
Normal file
87
launcher/modplatform/import_ftb/PackHelpers.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "modplatform/import_ftb/PackHelpers.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
|
||||
namespace FTBImportAPP {
|
||||
|
||||
Modpack parseDirectory(QString path)
|
||||
{
|
||||
Modpack modpack{ path };
|
||||
auto instanceFile = QFileInfo(FS::PathCombine(path, "instance.json"));
|
||||
if (!instanceFile.exists() || !instanceFile.isFile())
|
||||
return {};
|
||||
try {
|
||||
auto doc = Json::requireDocument(instanceFile.absoluteFilePath(), "FTB_APP instance JSON file");
|
||||
const auto root = doc.object();
|
||||
modpack.uuid = Json::requireString(root, "uuid", "uuid");
|
||||
modpack.id = Json::requireInteger(root, "id", "id");
|
||||
modpack.versionId = Json::requireInteger(root, "versionId", "versionId");
|
||||
modpack.name = Json::requireString(root, "name", "name");
|
||||
modpack.version = Json::requireString(root, "version", "version");
|
||||
modpack.mcVersion = Json::requireString(root, "mcVersion", "mcVersion");
|
||||
modpack.jvmArgs = Json::ensureVariant(root, "jvmArgs", {}, "jvmArgs");
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << "Couldn't load ftb instance json: " << e.cause();
|
||||
return {};
|
||||
}
|
||||
auto versionsFile = QFileInfo(FS::PathCombine(path, "version.json"));
|
||||
if (!versionsFile.exists() || !versionsFile.isFile())
|
||||
return {};
|
||||
try {
|
||||
auto doc = Json::requireDocument(versionsFile.absoluteFilePath(), "FTB_APP version JSON file");
|
||||
const auto root = doc.object();
|
||||
auto targets = Json::requireArray(root, "targets", "targets");
|
||||
|
||||
for (auto target : targets) {
|
||||
auto obj = Json::requireObject(target, "target");
|
||||
auto name = Json::requireString(obj, "name", "name");
|
||||
auto version = Json::requireString(obj, "version", "version");
|
||||
if (name == "forge") {
|
||||
modpack.loaderType = ResourceAPI::Forge;
|
||||
modpack.version = version;
|
||||
break;
|
||||
} else if (name == "fabric") {
|
||||
modpack.loaderType = ResourceAPI::Fabric;
|
||||
modpack.version = version;
|
||||
break;
|
||||
} else if (name == "quilt") {
|
||||
modpack.loaderType = ResourceAPI::Quilt;
|
||||
modpack.version = version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const Exception& e) {
|
||||
qDebug() << "Couldn't load ftb version json: " << e.cause();
|
||||
return {};
|
||||
}
|
||||
auto iconFile = QFileInfo(FS::PathCombine(path, "folder.jpg"));
|
||||
if (iconFile.exists() && iconFile.isFile()) {
|
||||
modpack.icon = QIcon(iconFile.absoluteFilePath());
|
||||
}
|
||||
return modpack;
|
||||
}
|
||||
|
||||
} // namespace FTBImportAPP
|
55
launcher/modplatform/import_ftb/PackHelpers.h
Normal file
55
launcher/modplatform/import_ftb/PackHelpers.h
Normal file
@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QIcon>
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
|
||||
namespace FTBImportAPP {
|
||||
|
||||
struct Modpack {
|
||||
QString path;
|
||||
|
||||
// json data
|
||||
QString uuid;
|
||||
int id;
|
||||
int versionId;
|
||||
QString name;
|
||||
QString version;
|
||||
QString mcVersion;
|
||||
// not needed for instance creation
|
||||
QVariant jvmArgs;
|
||||
|
||||
std::optional<ResourceAPI::ModLoaderType> loaderType;
|
||||
QString loaderVersion;
|
||||
|
||||
QIcon icon;
|
||||
};
|
||||
|
||||
typedef QList<Modpack> ModpackList;
|
||||
|
||||
Modpack parseDirectory(QString path);
|
||||
|
||||
} // namespace FTBImportAPP
|
||||
|
||||
// We need it for the proxy model
|
||||
Q_DECLARE_METATYPE(FTBImportAPP::Modpack)
|
99
launcher/modplatform/import_ftb/PackInstallTask.cpp
Normal file
99
launcher/modplatform/import_ftb/PackInstallTask.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PackInstallTask.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/import_ftb/PackHelpers.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
namespace FTBImportAPP {
|
||||
|
||||
void PackInstallTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Copying files..."));
|
||||
setAbortable(false);
|
||||
progress(1, 2);
|
||||
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||
FS::copy folderCopy(m_pack.path, FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||
folderCopy.followSymlinks(true);
|
||||
return folderCopy();
|
||||
});
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &PackInstallTask::copySettings);
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &PackInstallTask::emitAborted);
|
||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||
}
|
||||
|
||||
void PackInstallTask::copySettings()
|
||||
{
|
||||
setStatus(tr("Copying settings..."));
|
||||
progress(2, 2);
|
||||
QString instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(instanceConfigPath);
|
||||
instanceSettings->suspendSave();
|
||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
instance.settings()->set("InstanceType", "OneSix");
|
||||
|
||||
if (m_pack.jvmArgs.isValid() && !m_pack.jvmArgs.toString().isEmpty()) {
|
||||
instance.settings()->set("OverrideJavaArgs", true);
|
||||
instance.settings()->set("JvmArgs", m_pack.jvmArgs.toString());
|
||||
}
|
||||
|
||||
auto components = instance.getPackProfile();
|
||||
components->buildingFromScratch();
|
||||
components->setComponentVersion("net.minecraft", m_pack.mcVersion, true);
|
||||
|
||||
auto modloader = m_pack.loaderType;
|
||||
if (modloader.has_value())
|
||||
switch (modloader.value()) {
|
||||
case ResourceAPI::Forge: {
|
||||
components->setComponentVersion("net.minecraftforge", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Fabric: {
|
||||
components->setComponentVersion("net.fabricmc.fabric-loader", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Quilt: {
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", m_pack.version, true);
|
||||
break;
|
||||
}
|
||||
case ResourceAPI::Cauldron:
|
||||
break;
|
||||
case ResourceAPI::LiteLoader:
|
||||
break;
|
||||
}
|
||||
components->saveNow();
|
||||
|
||||
instance.setName(name());
|
||||
if (m_instIcon == "default")
|
||||
m_instIcon = "ftb_logo";
|
||||
instance.setIconKey(m_instIcon);
|
||||
instanceSettings->resumeSave();
|
||||
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
} // namespace FTBImportAPP
|
49
launcher/modplatform/import_ftb/PackInstallTask.h
Normal file
49
launcher/modplatform/import_ftb/PackInstallTask.h
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "PackHelpers.h"
|
||||
|
||||
namespace FTBImportAPP {
|
||||
|
||||
class PackInstallTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PackInstallTask(const Modpack& pack) : m_pack(pack) {}
|
||||
virtual ~PackInstallTask() = default;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
private slots:
|
||||
void copySettings();
|
||||
|
||||
private:
|
||||
QFuture<bool> m_copyFuture;
|
||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||
|
||||
const Modpack m_pack;
|
||||
};
|
||||
|
||||
} // namespace FTBImportAPP
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -37,8 +37,8 @@
|
||||
#include "PrivatePackManager.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include "BuildConfig.h"
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
|
@ -1,22 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QMetaType>
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
//Header for structs etc...
|
||||
enum class PackType
|
||||
{
|
||||
Public,
|
||||
ThirdParty,
|
||||
Private
|
||||
};
|
||||
// Header for structs etc...
|
||||
enum class PackType { Public, ThirdParty, Private };
|
||||
|
||||
struct Modpack
|
||||
{
|
||||
struct Modpack {
|
||||
QString name;
|
||||
QString description;
|
||||
QString author;
|
||||
@ -26,9 +20,9 @@ struct Modpack
|
||||
QString mods;
|
||||
QString logo;
|
||||
|
||||
//Technical data
|
||||
// Technical data
|
||||
QString dir;
|
||||
QString file; //<- Url in the xml, but doesn't make much sense
|
||||
QString file; //<- Url in the xml, but doesn't make much sense
|
||||
|
||||
bool bugged = false;
|
||||
bool broken = false;
|
||||
@ -39,7 +33,7 @@ struct Modpack
|
||||
|
||||
typedef QList<Modpack> ModpackList;
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
||||
//We need it for the proxy model
|
||||
// We need it for the proxy model
|
||||
Q_DECLARE_METATYPE(LegacyFTB::Modpack)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -37,16 +37,16 @@
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "MMCZip.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "MMCZip.h"
|
||||
#include "minecraft/GradleSpecifier.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/GradleSpecifier.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
@ -65,6 +65,7 @@ void PackInstallTask::executeTask()
|
||||
void PackInstallTask::downloadPack()
|
||||
{
|
||||
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
|
||||
setProgress(1, 4);
|
||||
setAbortable(false);
|
||||
|
||||
archivePath = QString("%1/%2/%3").arg(m_pack.dir, m_version.replace(".", "_"), m_pack.file);
|
||||
@ -78,11 +79,10 @@ void PackInstallTask::downloadPack()
|
||||
}
|
||||
netJobContainer->addNetAction(Net::Download::makeFile(url, archivePath));
|
||||
|
||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
|
||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
|
||||
connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
|
||||
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress);
|
||||
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted);
|
||||
connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::unzip);
|
||||
connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::emitFailed);
|
||||
connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propagateStepProgress);
|
||||
connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::emitAborted);
|
||||
|
||||
netJobContainer->start();
|
||||
|
||||
@ -90,27 +90,6 @@ void PackInstallTask::downloadPack()
|
||||
progress(1, 4);
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadSucceeded()
|
||||
{
|
||||
unzip();
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadFailed(QString reason)
|
||||
{
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadProgress(qint64 current, qint64 total)
|
||||
{
|
||||
progress(current, total * 4);
|
||||
setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
|
||||
}
|
||||
|
||||
void PackInstallTask::onDownloadAborted()
|
||||
{
|
||||
emitAborted();
|
||||
}
|
||||
|
||||
void PackInstallTask::unzip()
|
||||
{
|
||||
setStatus(tr("Extracting modpack"));
|
||||
@ -120,16 +99,17 @@ void PackInstallTask::unzip()
|
||||
QDir extractDir(m_stagingPath);
|
||||
|
||||
m_packZip.reset(new QuaZip(archivePath));
|
||||
if(!m_packZip->open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Failed to open modpack file %1!").arg(archivePath));
|
||||
return;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath, extractDir.absolutePath() + "/unzip");
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), QOverload<QString, QString>::of(MMCZip::extractDir), archivePath,
|
||||
extractDir.absolutePath() + "/unzip");
|
||||
#else
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
|
||||
#endif
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
|
||||
@ -151,11 +131,9 @@ void PackInstallTask::install()
|
||||
setStatus(tr("Installing modpack"));
|
||||
progress(3, 4);
|
||||
QDir unzipMcDir(m_stagingPath + "/unzip/minecraft");
|
||||
if(unzipMcDir.exists())
|
||||
{
|
||||
//ok, found minecraft dir, move contents to instance dir
|
||||
if(!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft"))
|
||||
{
|
||||
if (unzipMcDir.exists()) {
|
||||
// ok, found minecraft dir, move contents to instance dir
|
||||
if (!QDir().rename(m_stagingPath + "/unzip/minecraft", m_stagingPath + "/.minecraft")) {
|
||||
emitFailed(tr("Failed to move unzipped Minecraft!"));
|
||||
return;
|
||||
}
|
||||
@ -172,23 +150,20 @@ void PackInstallTask::install()
|
||||
|
||||
bool fallback = true;
|
||||
|
||||
//handle different versions
|
||||
// handle different versions
|
||||
QFile packJson(m_stagingPath + "/.minecraft/pack.json");
|
||||
QDir jarmodDir = QDir(m_stagingPath + "/unzip/instMods");
|
||||
if(packJson.exists())
|
||||
{
|
||||
if (packJson.exists()) {
|
||||
packJson.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(packJson.readAll());
|
||||
packJson.close();
|
||||
|
||||
//we only care about the libs
|
||||
// we only care about the libs
|
||||
QJsonArray libs = doc.object().value("libraries").toArray();
|
||||
|
||||
foreach (const QJsonValue &value, libs)
|
||||
{
|
||||
foreach (const QJsonValue& value, libs) {
|
||||
QString nameValue = value.toObject().value("name").toString();
|
||||
if(!nameValue.startsWith("net.minecraftforge"))
|
||||
{
|
||||
if (!nameValue.startsWith("net.minecraftforge")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -199,16 +174,13 @@ void PackInstallTask::install()
|
||||
fallback = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(jarmodDir.exists())
|
||||
{
|
||||
if (jarmodDir.exists()) {
|
||||
qDebug() << "Found jarmods, installing...";
|
||||
|
||||
QStringList jarmods;
|
||||
for (auto info: jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
||||
{
|
||||
for (auto info : jarmodDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
|
||||
qDebug() << "Jarmod:" << info.fileName();
|
||||
jarmods.push_back(info.absoluteFilePath());
|
||||
}
|
||||
@ -217,12 +189,11 @@ void PackInstallTask::install()
|
||||
fallback = false;
|
||||
}
|
||||
|
||||
//just nuke unzip directory, it s not needed anymore
|
||||
// just nuke unzip directory, it s not needed anymore
|
||||
FS::deletePath(m_stagingPath + "/unzip");
|
||||
|
||||
if(fallback)
|
||||
{
|
||||
//TODO: Some fallback mechanism... or just keep failing!
|
||||
if (fallback) {
|
||||
// TODO: Some fallback mechanism... or just keep failing!
|
||||
emitFailed(tr("No installation method found!"));
|
||||
return;
|
||||
}
|
||||
@ -232,8 +203,7 @@ void PackInstallTask::install()
|
||||
progress(4, 4);
|
||||
|
||||
instance.setName(name());
|
||||
if(m_instIcon == "default")
|
||||
{
|
||||
if (m_instIcon == "default") {
|
||||
m_instIcon = "ftb_logo";
|
||||
}
|
||||
instance.setIconKey(m_instIcon);
|
||||
@ -252,4 +222,4 @@ bool PackInstallTask::abort()
|
||||
return InstanceTask::abort();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include "InstanceTask.h"
|
||||
#include "PackHelpers.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/Version.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "PackHelpers.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
|
||||
@ -14,36 +14,31 @@
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
class PackInstallTask : public InstanceTask
|
||||
{
|
||||
class PackInstallTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
public:
|
||||
explicit PackInstallTask(shared_qobject_ptr<QNetworkAccessManager> network, Modpack pack, QString version);
|
||||
virtual ~PackInstallTask(){}
|
||||
virtual ~PackInstallTask() {}
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
void downloadPack();
|
||||
void unzip();
|
||||
void install();
|
||||
|
||||
private slots:
|
||||
void onDownloadSucceeded();
|
||||
void onDownloadFailed(QString reason);
|
||||
void onDownloadProgress(qint64 current, qint64 total);
|
||||
void onDownloadAborted();
|
||||
private slots:
|
||||
|
||||
void onUnzipFinished();
|
||||
void onUnzipCanceled();
|
||||
|
||||
private: /* data */
|
||||
private: /* data */
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
bool abortable = false;
|
||||
std::unique_ptr<QuaZip> m_packZip;
|
||||
@ -56,4 +51,4 @@ private: /* data */
|
||||
QString m_version;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -43,8 +43,7 @@ namespace LegacyFTB {
|
||||
|
||||
void PrivatePackManager::load()
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
auto foo = QString::fromUtf8(FS::read(m_filename)).split('\n', Qt::SkipEmptyParts);
|
||||
currentPacks = QSet<QString>(foo.begin(), foo.end());
|
||||
@ -53,9 +52,7 @@ void PrivatePackManager::load()
|
||||
#endif
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
} catch (...) {
|
||||
currentPacks = {};
|
||||
qWarning() << "Failed to read third party FTB pack codes from" << m_filename;
|
||||
}
|
||||
@ -63,20 +60,16 @@ void PrivatePackManager::load()
|
||||
|
||||
void PrivatePackManager::save() const
|
||||
{
|
||||
if(!dirty)
|
||||
{
|
||||
if (!dirty) {
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
try {
|
||||
QStringList list = currentPacks.values();
|
||||
FS::write(m_filename, list.join('\n').toUtf8());
|
||||
dirty = false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
} catch (...) {
|
||||
qWarning() << "Failed to write third party FTB pack codes to" << m_filename;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
@ -1,43 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
class PrivatePackManager
|
||||
{
|
||||
public:
|
||||
~PrivatePackManager()
|
||||
{
|
||||
save();
|
||||
}
|
||||
class PrivatePackManager {
|
||||
public:
|
||||
~PrivatePackManager() { save(); }
|
||||
void load();
|
||||
void save() const;
|
||||
bool empty() const
|
||||
{
|
||||
return currentPacks.empty();
|
||||
}
|
||||
const QSet<QString> &getCurrentPackCodes() const
|
||||
{
|
||||
return currentPacks;
|
||||
}
|
||||
void add(const QString &code)
|
||||
bool empty() const { return currentPacks.empty(); }
|
||||
const QSet<QString>& getCurrentPackCodes() const { return currentPacks; }
|
||||
void add(const QString& code)
|
||||
{
|
||||
currentPacks.insert(code);
|
||||
dirty = true;
|
||||
}
|
||||
void remove(const QString &code)
|
||||
void remove(const QString& code)
|
||||
{
|
||||
currentPacks.remove(code);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
QSet<QString> currentPacks;
|
||||
QString m_filename = "private_packs.txt";
|
||||
mutable bool dirty = false;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
||||
{
|
||||
QStringList l;
|
||||
for (auto loader : { Forge, Fabric, Quilt }) {
|
||||
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
|
||||
if (types & loader) {
|
||||
l << getModLoaderString(loader);
|
||||
}
|
||||
@ -92,7 +92,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
{
|
||||
if (args.loaders.has_value()) {
|
||||
if (!validateModLoaders(args.loaders.value())) {
|
||||
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
|
||||
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -141,7 +141,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
||||
return s.isEmpty() ? QString() : s;
|
||||
}
|
||||
|
||||
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
|
||||
|
||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||
{
|
||||
|
@ -8,7 +8,10 @@ class ModrinthCheckUpdate : public CheckUpdateTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthCheckUpdate(QList<Mod*>& mods, std::list<Version>& mcVersions, std::optional<ResourceAPI::ModLoaderTypes> loaders, std::shared_ptr<ModFolderModel> mods_folder)
|
||||
ModrinthCheckUpdate(QList<Mod*>& mods,
|
||||
std::list<Version>& mcVersions,
|
||||
std::optional<ResourceAPI::ModLoaderTypes> loaders,
|
||||
std::shared_ptr<ModFolderModel> mods_folder)
|
||||
: CheckUpdateTask(mods, mcVersions, loaders, mods_folder)
|
||||
{}
|
||||
|
||||
|
@ -133,10 +133,10 @@ bool ModrinthCreationTask::updateInstance()
|
||||
}
|
||||
} else {
|
||||
// We don't have an old index file, so we may duplicate stuff!
|
||||
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);
|
||||
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);
|
||||
|
||||
if (dialog->exec() == QDialog::DialogCode::Rejected) {
|
||||
m_abort = true;
|
||||
@ -144,7 +144,6 @@ bool ModrinthCreationTask::updateInstance()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setOverride(true, inst->id());
|
||||
qDebug() << "Will override instance!";
|
||||
|
||||
@ -233,7 +232,8 @@ bool ModrinthCreationTask::createInstance()
|
||||
auto file_path = FS::PathCombine(root_modpack_path, file.path);
|
||||
if (!root_modpack_url.isParentOf(QUrl::fromLocalFile(file_path))) {
|
||||
// This means we somehow got out of the root folder, so abort here to prevent exploits
|
||||
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.").arg(file.path));
|
||||
setError(tr("One of the files has a path that leads to an arbitrary location (%1). This is a security risk and isn't allowed.")
|
||||
.arg(file.path));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -250,7 +250,8 @@ bool ModrinthCreationTask::createInstance()
|
||||
auto ndl = Net::Download::makeFile(file.downloads.dequeue(), file_path);
|
||||
ndl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||
m_files_job->addNetAction(ndl);
|
||||
if (auto shared = param.lock()) shared->succeeded();
|
||||
if (auto shared = param.lock())
|
||||
shared->succeeded();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -263,11 +264,11 @@ bool ModrinthCreationTask::createInstance()
|
||||
setError(reason);
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
||||
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
||||
setDetails(tr("%1 out of %2 complete").arg(current).arg(total));
|
||||
setProgress(current, total);
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress);
|
||||
connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propagateStepProgress);
|
||||
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_files_job->start();
|
||||
@ -293,7 +294,10 @@ bool ModrinthCreationTask::createInstance()
|
||||
return ended_well;
|
||||
}
|
||||
|
||||
bool ModrinthCreationTask::parseManifest(const QString& index_path, std::vector<Modrinth::File>& files, bool set_internal_data, bool show_optional_dialog)
|
||||
bool ModrinthCreationTask::parseManifest(const QString& index_path,
|
||||
std::vector<Modrinth::File>& files,
|
||||
bool set_internal_data,
|
||||
bool show_optional_dialog)
|
||||
{
|
||||
try {
|
||||
auto doc = Json::requireDocument(index_path);
|
||||
|
@ -20,10 +20,7 @@ class ModrinthCreationTask final : public InstanceCreationTask {
|
||||
QString id,
|
||||
QString version_id = {},
|
||||
QString original_instance_id = {})
|
||||
: InstanceCreationTask()
|
||||
, m_parent(parent)
|
||||
, m_managed_id(std::move(id))
|
||||
, m_managed_version_id(std::move(version_id))
|
||||
: InstanceCreationTask(), m_parent(parent), m_managed_id(std::move(id)), m_managed_version_id(std::move(version_id))
|
||||
{
|
||||
setStagingPath(staging_path);
|
||||
setParentSettings(global_settings);
|
||||
|
@ -55,19 +55,11 @@ void ModrinthPackExportTask::executeTask()
|
||||
|
||||
bool ModrinthPackExportTask::abort()
|
||||
{
|
||||
if (task != nullptr) {
|
||||
if (task) {
|
||||
task->abort();
|
||||
task = nullptr;
|
||||
emitAborted();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buildZipFuture.isRunning()) {
|
||||
buildZipFuture.cancel();
|
||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur immediately.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -94,6 +86,7 @@ void ModrinthPackExportTask::collectFiles()
|
||||
|
||||
void ModrinthPackExportTask::collectHashes()
|
||||
{
|
||||
setStatus(tr("Finding file hashes..."));
|
||||
for (const QFileInfo& file : files) {
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
@ -157,6 +150,7 @@ void ModrinthPackExportTask::makeApiRequest()
|
||||
if (pendingHashes.isEmpty())
|
||||
buildZip();
|
||||
else {
|
||||
setStatus(tr("Finding versions for hashes..."));
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
||||
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
||||
@ -202,74 +196,47 @@ void ModrinthPackExportTask::buildZip()
|
||||
{
|
||||
setStatus(tr("Adding files..."));
|
||||
|
||||
buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() {
|
||||
QuaZip zip(output);
|
||||
if (!zip.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(output);
|
||||
return BuildZipResult(tr("Could not create file"));
|
||||
}
|
||||
auto zipTask = makeShared<MMCZip::ExportToZipTask>(output, gameRoot, files, "overrides/", true);
|
||||
zipTask->addExtraFile("modrinth.index.json", generateIndex());
|
||||
|
||||
if (buildZipFuture.isCanceled())
|
||||
return BuildZipResult();
|
||||
zipTask->setExcludeFiles(resolvedFiles.keys());
|
||||
|
||||
QuaZipFile indexFile(&zip);
|
||||
if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("modrinth.index.json"))) {
|
||||
QFile::remove(output);
|
||||
return BuildZipResult(tr("Could not create index"));
|
||||
}
|
||||
indexFile.write(generateIndex());
|
||||
|
||||
size_t progress = 0;
|
||||
for (const QFileInfo& file : files) {
|
||||
if (buildZipFuture.isCanceled()) {
|
||||
QFile::remove(output);
|
||||
return BuildZipResult();
|
||||
}
|
||||
|
||||
setProgress(progress, files.length());
|
||||
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
|
||||
if (!resolvedFiles.contains(relative) && !JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) {
|
||||
QFile::remove(output);
|
||||
return BuildZipResult(tr("Could not read and compress %1").arg(relative));
|
||||
}
|
||||
progress++;
|
||||
}
|
||||
|
||||
zip.close();
|
||||
|
||||
if (zip.getZipError() != 0) {
|
||||
QFile::remove(output);
|
||||
return BuildZipResult(tr("A zip error occurred"));
|
||||
}
|
||||
|
||||
return BuildZipResult();
|
||||
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||
connect(zipTask.get(), &Task::finished, this, [this, progressStep] {
|
||||
progressStep->state = TaskStepState::Succeeded;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &ModrinthPackExportTask::finish);
|
||||
buildZipWatcher.setFuture(buildZipFuture);
|
||||
}
|
||||
|
||||
void ModrinthPackExportTask::finish()
|
||||
{
|
||||
if (buildZipFuture.isCanceled())
|
||||
emitAborted();
|
||||
else {
|
||||
const BuildZipResult result = buildZipFuture.result();
|
||||
if (result.has_value())
|
||||
emitFailed(result.value());
|
||||
else
|
||||
emitSucceeded();
|
||||
}
|
||||
connect(zipTask.get(), &Task::succeeded, this, &ModrinthPackExportTask::emitSucceeded);
|
||||
connect(zipTask.get(), &Task::aborted, this, &ModrinthPackExportTask::emitAborted);
|
||||
connect(zipTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||
progressStep->state = TaskStepState::Failed;
|
||||
stepProgress(*progressStep);
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(zipTask.get(), &Task::stepProgress, this, &ModrinthPackExportTask::propagateStepProgress);
|
||||
|
||||
connect(zipTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||
progressStep->update(current, total);
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
connect(zipTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||
progressStep->status = status;
|
||||
stepProgress(*progressStep);
|
||||
});
|
||||
task.reset(zipTask);
|
||||
zipTask->start();
|
||||
}
|
||||
|
||||
QByteArray ModrinthPackExportTask::generateIndex()
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["formatVersion"] = 1;
|
||||
obj["game"] = "minecraft";
|
||||
obj["name"] = name;
|
||||
obj["versionId"] = version;
|
||||
QJsonObject out;
|
||||
out["formatVersion"] = 1;
|
||||
out["game"] = "minecraft";
|
||||
out["name"] = name;
|
||||
out["versionId"] = version;
|
||||
if (!summary.isEmpty())
|
||||
obj["summary"] = summary;
|
||||
out["summary"] = summary;
|
||||
|
||||
if (mcInstance) {
|
||||
auto profile = mcInstance->getPackProfile();
|
||||
@ -290,30 +257,40 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
||||
if (forge != nullptr)
|
||||
dependencies["forge"] = forge->m_version;
|
||||
|
||||
obj["dependencies"] = dependencies;
|
||||
out["dependencies"] = dependencies;
|
||||
}
|
||||
|
||||
QJsonArray files;
|
||||
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles);
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
QJsonArray filesOut;
|
||||
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
|
||||
QJsonObject fileOut;
|
||||
|
||||
QString path = iterator.key();
|
||||
const ResolvedFile& value = iterator.value();
|
||||
|
||||
QJsonObject file;
|
||||
file["path"] = iterator.key();
|
||||
file["downloads"] = QJsonArray({ iterator.value().url });
|
||||
// detect disabled mod
|
||||
const QFileInfo pathInfo(path);
|
||||
if (pathInfo.suffix() == "disabled") {
|
||||
// rename it
|
||||
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
||||
// ...and make it optional
|
||||
QJsonObject env;
|
||||
env["client"] = "optional";
|
||||
env["server"] = "optional";
|
||||
fileOut["env"] = env;
|
||||
}
|
||||
|
||||
fileOut["path"] = path;
|
||||
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
||||
|
||||
QJsonObject hashes;
|
||||
hashes["sha1"] = value.sha1;
|
||||
hashes["sha512"] = value.sha512;
|
||||
fileOut["hashes"] = hashes;
|
||||
|
||||
file["hashes"] = hashes;
|
||||
file["fileSize"] = value.size;
|
||||
|
||||
files << file;
|
||||
fileOut["fileSize"] = value.size;
|
||||
filesOut << fileOut;
|
||||
}
|
||||
obj["files"] = files;
|
||||
out["files"] = filesOut;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
return QJsonDocument(out).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
@ -56,22 +56,17 @@ class ModrinthPackExportTask : public Task {
|
||||
const QString output;
|
||||
const MMCZip::FilterFunction filter;
|
||||
|
||||
typedef std::optional<QString> BuildZipResult;
|
||||
|
||||
ModrinthAPI api;
|
||||
QFileInfoList files;
|
||||
QMap<QString, QString> pendingHashes;
|
||||
QMap<QString, ResolvedFile> resolvedFiles;
|
||||
Task::Ptr task;
|
||||
QFuture<BuildZipResult> buildZipFuture;
|
||||
QFutureWatcher<BuildZipResult> buildZipWatcher;
|
||||
|
||||
void collectFiles();
|
||||
void collectHashes();
|
||||
void makeApiRequest();
|
||||
void parseApiResponse(const std::shared_ptr<QByteArray> response);
|
||||
void buildZip();
|
||||
void finish();
|
||||
|
||||
QByteArray generateIndex();
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -66,23 +66,23 @@ void loadIndexedInfo(Modpack& pack, QJsonObject& obj)
|
||||
pack.extra.projectUrl = QString("https://modrinth.com/modpack/%1").arg(Json::ensureString(obj, "slug"));
|
||||
|
||||
pack.extra.issuesUrl = Json::ensureString(obj, "issues_url");
|
||||
if(pack.extra.issuesUrl.endsWith('/'))
|
||||
if (pack.extra.issuesUrl.endsWith('/'))
|
||||
pack.extra.issuesUrl.chop(1);
|
||||
|
||||
pack.extra.sourceUrl = Json::ensureString(obj, "source_url");
|
||||
if(pack.extra.sourceUrl.endsWith('/'))
|
||||
if (pack.extra.sourceUrl.endsWith('/'))
|
||||
pack.extra.sourceUrl.chop(1);
|
||||
|
||||
pack.extra.wikiUrl = Json::ensureString(obj, "wiki_url");
|
||||
if(pack.extra.wikiUrl.endsWith('/'))
|
||||
if (pack.extra.wikiUrl.endsWith('/'))
|
||||
pack.extra.wikiUrl.chop(1);
|
||||
|
||||
pack.extra.discordUrl = Json::ensureString(obj, "discord_url");
|
||||
if(pack.extra.discordUrl.endsWith('/'))
|
||||
if (pack.extra.discordUrl.endsWith('/'))
|
||||
pack.extra.discordUrl.chop(1);
|
||||
|
||||
auto donate_arr = Json::ensureArray(obj, "donation_urls");
|
||||
for(auto d : donate_arr){
|
||||
for (auto d : donate_arr) {
|
||||
auto d_obj = Json::requireObject(d);
|
||||
|
||||
DonationData donate;
|
||||
@ -107,7 +107,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
|
||||
auto obj = Json::requireObject(versionIter);
|
||||
auto file = loadIndexedVersion(obj);
|
||||
|
||||
if(!file.id.isEmpty()) // Heuristic to check if the returned value is valid
|
||||
if (!file.id.isEmpty()) // Heuristic to check if the returned value is valid
|
||||
unsortedVersions.append(file);
|
||||
}
|
||||
auto orderSortPredicate = [](const ModpackVersion& a, const ModpackVersion& b) -> bool {
|
||||
@ -123,7 +123,7 @@ void loadIndexedVersions(Modpack& pack, QJsonDocument& doc)
|
||||
pack.versionsLoaded = true;
|
||||
}
|
||||
|
||||
auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
|
||||
auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion
|
||||
{
|
||||
ModpackVersion file;
|
||||
|
||||
@ -134,12 +134,11 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
|
||||
|
||||
file.id = Json::requireString(obj, "id");
|
||||
file.project_id = Json::requireString(obj, "project_id");
|
||||
|
||||
|
||||
file.date = Json::requireString(obj, "date_published");
|
||||
|
||||
auto files = Json::requireArray(obj, "files");
|
||||
|
||||
|
||||
for (auto file_iter : files) {
|
||||
File indexed_file;
|
||||
auto parent = Json::requireObject(file_iter);
|
||||
@ -148,21 +147,21 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion
|
||||
auto filename = Json::ensureString(parent, "filename");
|
||||
// Checking suffix here is fine because it's the response from Modrinth,
|
||||
// so one would assume it will always be in English.
|
||||
if(!filename.endsWith("mrpack") && !filename.endsWith("zip"))
|
||||
if (!filename.endsWith("mrpack") && !filename.endsWith("zip"))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto url = Json::requireString(parent, "url");
|
||||
|
||||
file.download_url = url;
|
||||
if(is_primary)
|
||||
if (is_primary)
|
||||
break;
|
||||
}
|
||||
|
||||
if(file.download_url.isEmpty())
|
||||
if (file.download_url.isEmpty())
|
||||
return {};
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Modrinth
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -76,7 +76,6 @@ struct ModpackExtra {
|
||||
QString discordUrl;
|
||||
|
||||
QList<DonationData> donate;
|
||||
|
||||
};
|
||||
|
||||
struct ModpackVersion {
|
||||
@ -100,10 +99,10 @@ struct Modpack {
|
||||
QString description;
|
||||
std::tuple<QString, QUrl> author;
|
||||
QString iconName;
|
||||
QUrl iconUrl;
|
||||
QUrl iconUrl;
|
||||
|
||||
bool versionsLoaded = false;
|
||||
bool extraInfoLoaded = false;
|
||||
bool versionsLoaded = false;
|
||||
bool extraInfoLoaded = false;
|
||||
|
||||
ModpackExtra extra;
|
||||
QVector<ModpackVersion> versions;
|
||||
@ -116,7 +115,7 @@ auto loadIndexedVersion(QJsonObject&) -> ModpackVersion;
|
||||
|
||||
auto validateDownloadUrl(QUrl) -> bool;
|
||||
|
||||
}
|
||||
} // namespace Modrinth
|
||||
|
||||
Q_DECLARE_METATYPE(Modrinth::Modpack)
|
||||
Q_DECLARE_METATYPE(Modrinth::ModpackVersion)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,20 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -36,22 +36,22 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul
|
||||
class V1 {
|
||||
public:
|
||||
struct Mod {
|
||||
QString slug {};
|
||||
QString name {};
|
||||
QString filename {};
|
||||
QString slug{};
|
||||
QString name{};
|
||||
QString filename{};
|
||||
// FIXME: make side an enum
|
||||
QString side {"both"};
|
||||
QString side{ "both" };
|
||||
|
||||
// [download]
|
||||
QString mode {};
|
||||
QUrl url {};
|
||||
QString hash_format {};
|
||||
QString hash {};
|
||||
QString mode{};
|
||||
QUrl url{};
|
||||
QString hash_format{};
|
||||
QString hash{};
|
||||
|
||||
// [update]
|
||||
ModPlatform::ResourceProvider provider {};
|
||||
QVariant file_id {};
|
||||
QVariant project_id {};
|
||||
ModPlatform::ResourceProvider provider{};
|
||||
QVariant file_id{};
|
||||
QVariant project_id{};
|
||||
|
||||
public:
|
||||
// This is a totally heuristic, but should work for now.
|
||||
@ -95,4 +95,4 @@ class V1 {
|
||||
static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod;
|
||||
};
|
||||
|
||||
} // namespace Packwiz
|
||||
} // namespace Packwiz
|
||||
|
@ -17,21 +17,21 @@
|
||||
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "MMCZip.h"
|
||||
#include "TechnicPackProcessor.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion)
|
||||
Technic::SingleZipPackInstallTask::SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion)
|
||||
{
|
||||
m_sourceUrl = sourceUrl;
|
||||
m_minecraftVersion = minecraftVersion;
|
||||
}
|
||||
|
||||
bool Technic::SingleZipPackInstallTask::abort() {
|
||||
if(m_abortable)
|
||||
{
|
||||
bool Technic::SingleZipPackInstallTask::abort()
|
||||
{
|
||||
if (m_abortable) {
|
||||
return m_filesNetJob->abort();
|
||||
}
|
||||
return false;
|
||||
@ -50,7 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask()
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded);
|
||||
connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged);
|
||||
connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress);
|
||||
connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propagateStepProgress);
|
||||
connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
@ -65,12 +65,12 @@ void Technic::SingleZipPackInstallTask::downloadSucceeded()
|
||||
|
||||
// open the zip and find relevant files in it
|
||||
m_packZip.reset(new QuaZip(m_archivePath));
|
||||
if (!m_packZip->open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||
return;
|
||||
}
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath());
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), QString(""), extractDir.absolutePath());
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &Technic::SingleZipPackInstallTask::extractFinished);
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &Technic::SingleZipPackInstallTask::extractAborted);
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
@ -93,8 +93,7 @@ void Technic::SingleZipPackInstallTask::downloadProgressChanged(qint64 current,
|
||||
void Technic::SingleZipPackInstallTask::extractFinished()
|
||||
{
|
||||
m_packZip.reset();
|
||||
if (!m_extractFuture.result())
|
||||
{
|
||||
if (!m_extractFuture.result()) {
|
||||
emitFailed(tr("Failed to extract modpack"));
|
||||
return;
|
||||
}
|
||||
@ -102,30 +101,22 @@ void Technic::SingleZipPackInstallTask::extractFinished()
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
{
|
||||
while (it.hasNext()) {
|
||||
auto filepath = it.next();
|
||||
QFileInfo file(filepath);
|
||||
auto permissions = QFile::permissions(filepath);
|
||||
auto origPermissions = permissions;
|
||||
if (file.isDir())
|
||||
{
|
||||
if (file.isDir()) {
|
||||
// Folder +rwx for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// File +rw for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
}
|
||||
if (origPermissions != permissions)
|
||||
{
|
||||
if (!QFile::setPermissions(filepath, permissions))
|
||||
{
|
||||
if (origPermissions != permissions) {
|
||||
if (!QFile::setPermissions(filepath, permissions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
qDebug() << "Fixed" << filepath;
|
||||
}
|
||||
}
|
||||
|
@ -28,28 +28,26 @@
|
||||
|
||||
namespace Technic {
|
||||
|
||||
class SingleZipPackInstallTask : public InstanceTask
|
||||
{
|
||||
class SingleZipPackInstallTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SingleZipPackInstallTask(const QUrl &sourceUrl, const QString &minecraftVersion);
|
||||
public:
|
||||
SingleZipPackInstallTask(const QUrl& sourceUrl, const QString& minecraftVersion);
|
||||
|
||||
bool canAbort() const override { return true; }
|
||||
bool abort() override;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void executeTask() override;
|
||||
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void extractFinished();
|
||||
void extractAborted();
|
||||
|
||||
private:
|
||||
private:
|
||||
bool m_abortable = false;
|
||||
|
||||
QUrl m_sourceUrl;
|
||||
@ -61,4 +59,4 @@ private:
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
};
|
||||
|
||||
} // namespace Technic
|
||||
} // namespace Technic
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -96,8 +96,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
||||
TechnicSolder::PackBuild build;
|
||||
try {
|
||||
TechnicSolder::loadPackBuild(build, obj);
|
||||
}
|
||||
catch (const JSONValidationError& e) {
|
||||
} catch (const JSONValidationError& e) {
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
m_filesNetJob.reset();
|
||||
return;
|
||||
@ -126,7 +125,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded()
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted);
|
||||
m_filesNetJob->start();
|
||||
@ -138,17 +137,14 @@ void Technic::SolderPackInstallTask::downloadSucceeded()
|
||||
|
||||
setStatus(tr("Extracting modpack"));
|
||||
m_filesNetJob.reset();
|
||||
m_extractFuture = QtConcurrent::run([this]()
|
||||
{
|
||||
m_extractFuture = QtConcurrent::run([this]() {
|
||||
int i = 0;
|
||||
QString extractDir = FS::PathCombine(m_stagingPath, ".minecraft");
|
||||
FS::ensureFolderPathExists(extractDir);
|
||||
|
||||
while (m_modCount > i)
|
||||
{
|
||||
while (m_modCount > i) {
|
||||
auto path = FS::PathCombine(m_outputDir.path(), QString("%1").arg(i));
|
||||
if (!MMCZip::extractDir(path, extractDir))
|
||||
{
|
||||
if (!MMCZip::extractDir(path, extractDir)) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
@ -181,8 +177,7 @@ void Technic::SolderPackInstallTask::downloadAborted()
|
||||
|
||||
void Technic::SolderPackInstallTask::extractFinished()
|
||||
{
|
||||
if (!m_extractFuture.result())
|
||||
{
|
||||
if (!m_extractFuture.result()) {
|
||||
emitFailed(tr("Failed to extract modpack"));
|
||||
return;
|
||||
}
|
||||
@ -190,30 +185,22 @@ void Technic::SolderPackInstallTask::extractFinished()
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
{
|
||||
while (it.hasNext()) {
|
||||
auto filepath = it.next();
|
||||
QFileInfo file(filepath);
|
||||
auto permissions = QFile::permissions(filepath);
|
||||
auto origPermissions = permissions;
|
||||
if(file.isDir())
|
||||
{
|
||||
if (file.isDir()) {
|
||||
// Folder +rwx for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// File +rw for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
}
|
||||
if(origPermissions != permissions)
|
||||
{
|
||||
if(!QFile::setPermissions(filepath, permissions))
|
||||
{
|
||||
if (origPermissions != permissions) {
|
||||
if (!QFile::setPermissions(filepath, permissions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
qDebug() << "Fixed" << filepath;
|
||||
}
|
||||
}
|
||||
@ -229,4 +216,3 @@ void Technic::SolderPackInstallTask::extractAborted()
|
||||
{
|
||||
emitFailed(tr("Instance import has been aborted."));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2021-2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -55,4 +55,4 @@ void loadPackBuild(PackBuild& v, QJsonObject& obj)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace TechnicSolder
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -18,9 +18,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace TechnicSolder {
|
||||
|
||||
@ -46,4 +46,4 @@ struct PackBuild {
|
||||
|
||||
void loadPackBuild(PackBuild& v, QJsonObject& obj);
|
||||
|
||||
}
|
||||
} // namespace TechnicSolder
|
||||
|
@ -26,7 +26,12 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion, const bool isSolder)
|
||||
void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings,
|
||||
const QString& instName,
|
||||
const QString& instIcon,
|
||||
const QString& stagingPath,
|
||||
const QString& minecraftVersion,
|
||||
const bool isSolder)
|
||||
{
|
||||
QString minecraftPath = FS::PathCombine(stagingPath, ".minecraft");
|
||||
QString configPath = FS::PathCombine(stagingPath, "instance.cfg");
|
||||
@ -35,8 +40,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
|
||||
instance.setName(instName);
|
||||
|
||||
if (instIcon != "default")
|
||||
{
|
||||
if (instIcon != "default") {
|
||||
instance.setIconKey(instIcon);
|
||||
}
|
||||
|
||||
@ -48,23 +52,18 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
QString modpackJar = FS::PathCombine(minecraftPath, "bin", "modpack.jar");
|
||||
QString versionJson = FS::PathCombine(minecraftPath, "bin", "version.json");
|
||||
QString fmlMinecraftVersion;
|
||||
if (QFile::exists(modpackJar))
|
||||
{
|
||||
if (QFile::exists(modpackJar)) {
|
||||
QuaZip zipFile(modpackJar);
|
||||
if (!zipFile.open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!zipFile.open(QuaZip::mdUnzip)) {
|
||||
emit failed(tr("Unable to open \"bin/modpack.jar\" file!"));
|
||||
return;
|
||||
}
|
||||
QuaZipDir zipFileRoot(&zipFile, "/");
|
||||
if (zipFileRoot.exists("/version.json"))
|
||||
{
|
||||
if (zipFileRoot.exists("/fmlversion.properties"))
|
||||
{
|
||||
if (zipFileRoot.exists("/version.json")) {
|
||||
if (zipFileRoot.exists("/fmlversion.properties")) {
|
||||
zipFile.setCurrentFile("fmlversion.properties");
|
||||
QuaZipFile file(&zipFile);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
emit failed(tr("Unable to open \"fmlversion.properties\"!"));
|
||||
return;
|
||||
}
|
||||
@ -77,30 +76,25 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
}
|
||||
zipFile.setCurrentFile("version.json", QuaZip::csSensitive);
|
||||
QuaZipFile file(&zipFile);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
emit failed(tr("Unable to open \"version.json\"!"));
|
||||
return;
|
||||
}
|
||||
data = file.readAll();
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if (minecraftVersion.isEmpty())
|
||||
emit failed(tr("Could not find \"version.json\" inside \"bin/modpack.jar\", but Minecraft version is unknown"));
|
||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
||||
components->installJarMods({modpackJar});
|
||||
components->installJarMods({ modpackJar });
|
||||
|
||||
// Forge for 1.4.7 and for 1.5.2 require extra libraries.
|
||||
// Figure out the forge version and add it as a component
|
||||
// (the code still comes from the jar mod installed above)
|
||||
if (zipFileRoot.exists("/forgeversion.properties"))
|
||||
{
|
||||
if (zipFileRoot.exists("/forgeversion.properties")) {
|
||||
zipFile.setCurrentFile("forgeversion.properties", QuaZip::csSensitive);
|
||||
QuaZipFile file(&zipFile);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
// Really shouldn't happen, but error handling shall not be forgotten
|
||||
emit failed(tr("Unable to open \"forgeversion.properties\""));
|
||||
return;
|
||||
@ -115,8 +109,7 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
revision = iniFile["forge.revision.number"].toString();
|
||||
build = iniFile["forge.build.number"].toString();
|
||||
|
||||
if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty())
|
||||
{
|
||||
if (major.isEmpty() || minor.isEmpty() || revision.isEmpty() || build.isEmpty()) {
|
||||
emit failed(tr("Invalid \"forgeversion.properties\"!"));
|
||||
return;
|
||||
}
|
||||
@ -128,84 +121,63 @@ void Technic::TechnicPackProcessor::run(SettingsObjectPtr globalSettings, const
|
||||
emit succeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (QFile::exists(versionJson))
|
||||
{
|
||||
} else if (QFile::exists(versionJson)) {
|
||||
QFile file(versionJson);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
emit failed(tr("Unable to open \"version.json\"!"));
|
||||
return;
|
||||
}
|
||||
data = file.readAll();
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// This is the "Vanilla" modpack, excluded by the search code
|
||||
emit failed(tr("Unable to find a \"version.json\"!"));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
QJsonDocument doc = Json::requireDocument(data);
|
||||
QJsonObject root = Json::requireObject(doc, "version.json");
|
||||
QString minecraftVersion = Json::ensureString(root, "inheritsFrom", QString(), "");
|
||||
if (minecraftVersion.isEmpty())
|
||||
{
|
||||
if (fmlMinecraftVersion.isEmpty())
|
||||
{
|
||||
if (minecraftVersion.isEmpty()) {
|
||||
if (fmlMinecraftVersion.isEmpty()) {
|
||||
emit failed(tr("Could not understand \"version.json\":\ninheritsFrom is missing"));
|
||||
return;
|
||||
}
|
||||
minecraftVersion = fmlMinecraftVersion;
|
||||
}
|
||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
||||
for (auto library: Json::ensureArray(root, "libraries", {}))
|
||||
{
|
||||
if (!library.isObject())
|
||||
{
|
||||
for (auto library : Json::ensureArray(root, "libraries", {})) {
|
||||
if (!library.isObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto libraryObject = Json::ensureObject(library, {}, "");
|
||||
auto libraryName = Json::ensureString(libraryObject, "name", "", "");
|
||||
|
||||
if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) && libraryName.contains('-'))
|
||||
{
|
||||
if ((libraryName.startsWith("net.minecraftforge:forge:") || libraryName.startsWith("net.minecraftforge:fmlloader:")) &&
|
||||
libraryName.contains('-')) {
|
||||
QString libraryVersion = libraryName.section(':', 2);
|
||||
if (!libraryVersion.startsWith("1.7.10-"))
|
||||
{
|
||||
if (!libraryVersion.startsWith("1.7.10-")) {
|
||||
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// 1.7.10 versions sometimes look like 1.7.10-10.13.4.1614-1.7.10, this filters out the 10.13.4.1614 part
|
||||
components->setComponentVersion("net.minecraftforge", libraryName.section('-', 1, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// <Technic library name prefix> -> <our component name>
|
||||
static QMap<QString, QString> loaderMap {
|
||||
{"net.minecraftforge:minecraftforge:", "net.minecraftforge"},
|
||||
{"net.fabricmc:fabric-loader:", "net.fabricmc.fabric-loader"},
|
||||
{"org.quiltmc:quilt-loader:", "org.quiltmc.quilt-loader"}
|
||||
};
|
||||
for (const auto& loader : loaderMap.keys())
|
||||
{
|
||||
if (libraryName.startsWith(loader))
|
||||
{
|
||||
static QMap<QString, QString> loaderMap{ { "net.minecraftforge:minecraftforge:", "net.minecraftforge" },
|
||||
{ "net.fabricmc:fabric-loader:", "net.fabricmc.fabric-loader" },
|
||||
{ "org.quiltmc:quilt-loader:", "org.quiltmc.quilt-loader" } };
|
||||
for (const auto& loader : loaderMap.keys()) {
|
||||
if (libraryName.startsWith(loader)) {
|
||||
components->setComponentVersion(loaderMap.value(loader), libraryName.section(':', 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
} catch (const JSONValidationError& e) {
|
||||
emit failed(tr("Could not understand \"version.json\":\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
|
@ -18,18 +18,21 @@
|
||||
#include <QString>
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
namespace Technic
|
||||
{
|
||||
// not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask
|
||||
class TechnicPackProcessor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
namespace Technic {
|
||||
// not exporting it, only used in SingleZipPackInstallTask, InstanceImportTask and SolderPackInstallTask
|
||||
class TechnicPackProcessor : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void succeeded();
|
||||
void failed(QString reason);
|
||||
signals:
|
||||
void succeeded();
|
||||
void failed(QString reason);
|
||||
|
||||
public:
|
||||
void run(SettingsObjectPtr globalSettings, const QString &instName, const QString &instIcon, const QString &stagingPath, const QString &minecraftVersion=QString(), const bool isSolder = false);
|
||||
};
|
||||
}
|
||||
public:
|
||||
void run(SettingsObjectPtr globalSettings,
|
||||
const QString& instName,
|
||||
const QString& instIcon,
|
||||
const QString& stagingPath,
|
||||
const QString& minecraftVersion = QString(),
|
||||
const bool isSolder = false);
|
||||
};
|
||||
} // namespace Technic
|
||||
|
Reference in New Issue
Block a user