aboutsummaryrefslogtreecommitdiff
path: root/launcher
diff options
context:
space:
mode:
Diffstat (limited to 'launcher')
-rw-r--r--launcher/Application.cpp54
-rw-r--r--launcher/Application.h8
-rw-r--r--launcher/InstanceImportTask.cpp35
-rw-r--r--launcher/InstanceImportTask.h1
-rw-r--r--launcher/MangoHud.cpp43
-rw-r--r--launcher/MangoHud.h4
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp29
-rw-r--r--launcher/minecraft/PackProfile.cpp3
-rw-r--r--launcher/minecraft/launch/ExtractNatives.cpp12
-rw-r--r--launcher/modplatform/ResourceAPI.h4
-rw-r--r--launcher/modplatform/flame/FlameAPI.cpp11
-rw-r--r--launcher/modplatform/flame/FlameAPI.h7
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp8
-rw-r--r--launcher/modplatform/flame/FlamePackExportTask.cpp3
-rw-r--r--launcher/modplatform/import_ftb/PackHelpers.cpp6
-rw-r--r--launcher/modplatform/import_ftb/PackInstallTask.cpp4
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h7
-rw-r--r--launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp4
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp4
-rw-r--r--launcher/modplatform/modrinth/ModrinthInstanceCreationTask.h2
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackExportTask.cpp3
-rw-r--r--launcher/resources/multimc/multimc.qrc1
-rw-r--r--launcher/resources/multimc/scalable/instances/neoforged.svg3
-rw-r--r--launcher/ui/MainWindow.cpp103
-rw-r--r--launcher/ui/MainWindow.h2
-rw-r--r--launcher/ui/dialogs/InstallLoaderDialog.cpp4
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp7
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.h5
-rw-r--r--launcher/ui/dialogs/SkinUploadDialog.ui6
-rw-r--r--launcher/ui/pages/global/MinecraftPage.cpp35
-rw-r--r--launcher/ui/pages/global/MinecraftPage.h3
-rw-r--r--launcher/ui/pages/global/MinecraftPage.ui40
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.cpp35
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.h3
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.ui44
-rw-r--r--launcher/ui/pages/modplatform/CustomPage.cpp3
-rw-r--r--launcher/ui/pages/modplatform/CustomPage.ui10
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.cpp72
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.h3
-rw-r--r--launcher/ui/pages/modplatform/ImportPage.ui2
40 files changed, 543 insertions, 90 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 4da7629c..5a952c74 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -194,8 +194,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
- { { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
+ { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
+ // Has to be positional for some OS to handle that properly
+ parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
+
parser.addHelpOption();
parser.addVersionOption();
@@ -208,13 +211,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
m_instanceIdToShowWindowOf = parser.value("show");
- for (auto zip_path : parser.values("import")) {
- m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
+ for (auto url : parser.values("import")) {
+ m_urlsToImport.append(normalizeImportUrl(url));
}
// treat unspecified positional arguments as import urls
- for (auto zip_path : parser.positionalArguments()) {
- m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
+ for (auto url : parser.positionalArguments()) {
+ m_urlsToImport.append(normalizeImportUrl(url));
}
// error if --launch is missing with --server or --profile
@@ -313,11 +316,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
activate.command = "activate";
m_peerInstance->sendMessage(activate.serialize(), timeout);
- if (!m_zipsToImport.isEmpty()) {
- for (auto zip_url : m_zipsToImport) {
+ if (!m_urlsToImport.isEmpty()) {
+ for (auto url : m_urlsToImport) {
ApplicationMessage import;
import.command = "import";
- import.args.insert("path", zip_url.toString());
+ import.args.insert("url", url.toString());
m_peerInstance->sendMessage(import.serialize(), timeout);
}
}
@@ -582,7 +585,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
// Native library workarounds
m_settings->registerSetting("UseNativeOpenAL", false);
+ m_settings->registerSetting("CustomOpenALPath", "");
m_settings->registerSetting("UseNativeGLFW", false);
+ m_settings->registerSetting("CustomGLFWPath", "");
// Peformance related options
m_settings->registerSetting("EnableFeralGamemode", false);
@@ -842,6 +847,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
updateCapabilities();
+ detectLibraries();
+
if (createSetupWizard()) {
return;
}
@@ -978,9 +985,9 @@ void Application::performMainStartupAction()
showMainWindow(false);
qDebug() << "<> Main window shown.";
}
- if (!m_zipsToImport.isEmpty()) {
- qDebug() << "<> Importing from zip:" << m_zipsToImport;
- m_mainWindow->processURLs(m_zipsToImport);
+ if (!m_urlsToImport.isEmpty()) {
+ qDebug() << "<> Importing from url:" << m_urlsToImport;
+ m_mainWindow->processURLs(m_urlsToImport);
}
}
@@ -1022,12 +1029,12 @@ void Application::messageReceived(const QByteArray& message)
if (command == "activate") {
showMainWindow();
} else if (command == "import") {
- QString path = received.args["path"];
- if (path.isEmpty()) {
+ QString url = received.args["url"];
+ if (url.isEmpty()) {
qWarning() << "Received" << command << "message without a zip path/URL.";
return;
}
- m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
+ m_mainWindow->processURLs({ normalizeImportUrl(url) });
} else if (command == "launch") {
QString id = received.args["id"];
QString server = received.args["server"];
@@ -1411,6 +1418,15 @@ void Application::updateCapabilities()
#endif
}
+void Application::detectLibraries()
+{
+#ifdef Q_OS_LINUX
+ m_detectedGLFWPath = MangoHud::findLibrary(BuildConfig.GLFW_LIBRARY_NAME);
+ m_detectedOpenALPath = MangoHud::findLibrary(BuildConfig.OPENAL_LIBRARY_NAME);
+ qDebug() << "Detected native libraries:" << m_detectedGLFWPath << m_detectedOpenALPath;
+#endif
+}
+
QString Application::getJarPath(QString jarFile)
{
QStringList potentialPaths = {
@@ -1589,3 +1605,13 @@ void Application::triggerUpdateCheck()
qDebug() << "Updater not available.";
}
}
+
+QUrl Application::normalizeImportUrl(QString const& url)
+{
+ auto local_file = QFileInfo(url);
+ if (local_file.exists()) {
+ return QUrl::fromLocalFile(local_file.absoluteFilePath());
+ } else {
+ return QUrl::fromUserInput(url);
+ }
+}
diff --git a/launcher/Application.h b/launcher/Application.h
index 8a85fd95..b227bb81 100644
--- a/launcher/Application.h
+++ b/launcher/Application.h
@@ -142,6 +142,8 @@ class Application : public QApplication {
void updateCapabilities();
+ void detectLibraries();
+
/*!
* Finds and returns the full path to a jar file.
* Returns a null-string if it could not be found.
@@ -177,6 +179,8 @@ class Application : public QApplication {
int suitableMaxMem();
+ QUrl normalizeImportUrl(QString const& url);
+
signals:
void updateAllowedChanged(bool status);
void globalSettingsAboutToOpen();
@@ -274,11 +278,13 @@ class Application : public QApplication {
SetupWizard* m_setupWizard = nullptr;
public:
+ QString m_detectedGLFWPath;
+ QString m_detectedOpenALPath;
QString m_instanceIdToLaunch;
QString m_serverToJoin;
QString m_profileToUse;
bool m_liveCheck = false;
- QList<QUrl> m_zipsToImport;
+ QList<QUrl> m_urlsToImport;
QString m_instanceIdToShowWindowOf;
std::unique_ptr<QFile> logFile;
};
diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp
index 3f948a33..bc139e4f 100644
--- a/launcher/InstanceImportTask.cpp
+++ b/launcher/InstanceImportTask.cpp
@@ -50,6 +50,7 @@
#include "modplatform/technic/TechnicPackProcessor.h"
#include "settings/INISettingsObject.h"
+#include "tasks/Task.h"
#include "net/ApiDownload.h"
@@ -90,25 +91,27 @@ void InstanceImportTask::executeTask()
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
m_downloadRequired = true;
- const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
-
- auto entry = APPLICATION->metacache()->resolveEntry("general", path);
- entry->setStale(true);
- m_archivePath = entry->getFullPath();
-
- m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
- m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
-
- connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
- connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
- connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
- connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
- connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
-
- m_filesNetJob->start();
+ downloadFromUrl();
}
}
+void InstanceImportTask::downloadFromUrl()
+{
+ const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
+ auto entry = APPLICATION->metacache()->resolveEntry("general", path);
+ entry->setStale(true);
+ m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
+ m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
+ m_archivePath = entry->getFullPath();
+
+ connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
+ connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
+ connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
+ connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
+ connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
+ m_filesNetJob->start();
+}
+
void InstanceImportTask::downloadSucceeded()
{
processZipPack();
diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h
index 4459e440..ca3d30ad 100644
--- a/launcher/InstanceImportTask.h
+++ b/launcher/InstanceImportTask.h
@@ -101,4 +101,5 @@ class InstanceImportTask : public InstanceTask {
// FIXME: nuke
QWidget* m_parent;
+ void downloadFromUrl();
};
diff --git a/launcher/MangoHud.cpp b/launcher/MangoHud.cpp
index 5758da3a..ab79f418 100644
--- a/launcher/MangoHud.cpp
+++ b/launcher/MangoHud.cpp
@@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#include <QDebug>
#include <QDir>
#include <QString>
#include <QStringList>
@@ -26,6 +27,15 @@
#include "Json.h"
#include "MangoHud.h"
+#ifdef __GLIBC__
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#define UNDEF_GNU_SOURCE
+#endif
+#include <dlfcn.h>
+#include <linux/limits.h>
+#endif
+
namespace MangoHud {
QString getLibraryString()
@@ -106,4 +116,37 @@ QString getLibraryString()
return QString();
}
+
+QString findLibrary(QString libName)
+{
+#ifdef __GLIBC__
+ const char* library = libName.toLocal8Bit().constData();
+
+ void* handle = dlopen(library, RTLD_NOW);
+ if (!handle) {
+ qCritical() << "dlopen() failed:" << dlerror();
+ return {};
+ }
+
+ char path[PATH_MAX];
+ if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) {
+ qCritical() << "dlinfo() failed:" << dlerror();
+ dlclose(handle);
+ return {};
+ }
+
+ auto fullPath = FS::PathCombine(QString(path), libName);
+
+ dlclose(handle);
+ return fullPath;
+#else
+ qWarning() << "MangoHud::findLibrary is not implemented on this platform";
+ return {};
+#endif
+}
} // namespace MangoHud
+
+#ifdef UNDEF_GNU_SOURCE
+#undef _GNU_SOURCE
+#undef UNDEF_GNU_SOURCE
+#endif
diff --git a/launcher/MangoHud.h b/launcher/MangoHud.h
index 7b7c2849..5361999b 100644
--- a/launcher/MangoHud.h
+++ b/launcher/MangoHud.h
@@ -24,4 +24,6 @@
namespace MangoHud {
QString getLibraryString();
-}
+
+QString findLibrary(QString libName);
+} // namespace MangoHud
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 7e7b3554..699aaffa 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -170,7 +170,9 @@ void MinecraftInstance::loadSpecificSettings()
// Native library workarounds
auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false);
m_settings->registerOverride(global_settings->getSetting("UseNativeOpenAL"), nativeLibraryWorkaroundsOverride);
+ m_settings->registerOverride(global_settings->getSetting("CustomOpenALPath"), nativeLibraryWorkaroundsOverride);
m_settings->registerOverride(global_settings->getSetting("UseNativeGLFW"), nativeLibraryWorkaroundsOverride);
+ m_settings->registerOverride(global_settings->getSetting("CustomGLFWPath"), nativeLibraryWorkaroundsOverride);
// Peformance related options
auto performanceOverride = m_settings->registerSetting("OverridePerformance", false);
@@ -437,6 +439,33 @@ QStringList MinecraftInstance::extraArguments()
if (loaders.has_value() && loaders.value() & ResourceAPI::Quilt && settings()->get("DisableQuiltBeacon").toBool())
list.append("-Dloader.disable_beacon=true");
}
+
+ {
+ QString openALPath;
+ QString glfwPath;
+
+ if (settings()->get("UseNativeOpenAL").toBool()) {
+ openALPath = APPLICATION->m_detectedOpenALPath;
+ auto customPath = settings()->get("CustomOpenALPath").toString();
+ if (!customPath.isEmpty())
+ openALPath = customPath;
+ }
+ if (settings()->get("UseNativeGLFW").toBool()) {
+ glfwPath = APPLICATION->m_detectedGLFWPath;
+ auto customPath = settings()->get("CustomGLFWPath").toString();
+ if (!customPath.isEmpty())
+ glfwPath = customPath;
+ }
+
+ QFileInfo openALInfo(openALPath);
+ QFileInfo glfwInfo(glfwPath);
+
+ if (!openALPath.isEmpty() && openALInfo.exists())
+ list.append("-Dorg.lwjgl.openal.libname=" + openALInfo.absoluteFilePath());
+ if (!glfwPath.isEmpty() && glfwInfo.exists())
+ list.append("-Dorg.lwjgl.glfw.libname=" + glfwInfo.absoluteFilePath());
+ }
+
return list;
}
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index cf8270cd..92988808 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -62,7 +62,8 @@
#include "Application.h"
#include "modplatform/ResourceAPI.h"
-static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.minecraftforge", ResourceAPI::Forge },
+static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{ { "net.neoforged", ResourceAPI::NeoForge },
+ { "net.minecraftforge", ResourceAPI::Forge },
{ "net.fabricmc.fabric-loader", ResourceAPI::Fabric },
{ "org.quiltmc.quilt-loader", ResourceAPI::Quilt },
{ "com.mumfrey.liteloader", ResourceAPI::LiteLoader } };
diff --git a/launcher/minecraft/launch/ExtractNatives.cpp b/launcher/minecraft/launch/ExtractNatives.cpp
index cebeaedd..8f3cac4d 100644
--- a/launcher/minecraft/launch/ExtractNatives.cpp
+++ b/launcher/minecraft/launch/ExtractNatives.cpp
@@ -39,7 +39,7 @@ static QString replaceSuffix(QString target, const QString& suffix, const QStrin
return target + replacement;
}
-static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack, bool nativeOpenAL, bool nativeGLFW)
+static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibHack)
{
QuaZip zip(source);
if (!zip.open(QuaZip::mdUnzip)) {
@@ -52,12 +52,6 @@ static bool unzipNatives(QString source, QString targetFolder, bool applyJnilibH
do {
QString name = zip.getCurrentFileName();
auto lowercase = name.toLower();
- if (nativeGLFW && name.contains("glfw")) {
- continue;
- }
- if (nativeOpenAL && name.contains("openal")) {
- continue;
- }
if (applyJnilibHack) {
name = replaceSuffix(name, ".jnilib", ".dylib");
}
@@ -83,14 +77,12 @@ void ExtractNatives::executeTask()
return;
}
auto settings = minecraftInstance->settings();
- bool nativeOpenAL = settings->get("UseNativeOpenAL").toBool();
- bool nativeGLFW = settings->get("UseNativeGLFW").toBool();
auto outputPath = minecraftInstance->getNativePath();
auto javaVersion = minecraftInstance->getJavaVersion();
bool jniHackEnabled = javaVersion.major() >= 8;
for (const auto& source : toExtract) {
- if (!unzipNatives(source, outputPath, jniHackEnabled, nativeOpenAL, nativeGLFW)) {
+ if (!unzipNatives(source, outputPath, jniHackEnabled)) {
const char* reason = QT_TR_NOOP("Couldn't extract native jar '%1' to destination '%2'");
emit logLine(QString(reason).arg(source, outputPath), MessageLevel::Fatal);
emitFailed(tr(reason).arg(source, outputPath));
diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h
index a92217a0..f6ccb426 100644
--- a/launcher/modplatform/ResourceAPI.h
+++ b/launcher/modplatform/ResourceAPI.h
@@ -54,7 +54,7 @@ class ResourceAPI {
public:
virtual ~ResourceAPI() = default;
- enum ModLoaderType { Forge = 1 << 0, Cauldron = 1 << 1, LiteLoader = 1 << 2, Fabric = 1 << 3, Quilt = 1 << 4 };
+ enum ModLoaderType { NeoForge = 1 << 0, Forge = 1 << 1, Cauldron = 1 << 2, LiteLoader = 1 << 3, Fabric = 1 << 4, Quilt = 1 << 5 };
Q_DECLARE_FLAGS(ModLoaderTypes, ModLoaderType)
struct SortingMethod {
@@ -164,6 +164,8 @@ class ResourceAPI {
static auto getModLoaderString(ModLoaderType type) -> const QString
{
switch (type) {
+ case NeoForge:
+ return "neoforge";
case Forge:
return "forge";
case Cauldron:
diff --git a/launcher/modplatform/flame/FlameAPI.cpp b/launcher/modplatform/flame/FlameAPI.cpp
index 73ed1011..74d7db97 100644
--- a/launcher/modplatform/flame/FlameAPI.cpp
+++ b/launcher/modplatform/flame/FlameAPI.cpp
@@ -204,6 +204,17 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr
return netJob;
}
+Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const
+{
+ auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network());
+ netJob->addNetAction(
+ Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response));
+
+ QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; });
+
+ return netJob;
+}
+
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 49bc316f..a1256e17 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -20,10 +20,11 @@ class FlameAPI : public NetworkResourceAPI {
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
+ Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
- static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
+ static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (NeoForge | Forge | Fabric | Quilt); }
private:
static int getClassId(ModPlatform::ResourceType type)
@@ -46,7 +47,9 @@ class FlameAPI : public NetworkResourceAPI {
return 4;
// TODO: remove this once Quilt drops official Fabric support
if (loaders & Quilt) // NOTE: Most if not all Fabric mods should work *currently*
- return 4; // Quilt would probably be 5
+ return 4; // FIXME: implement multiple loaders filter
+ if (loaders & NeoForge)
+ return 6;
return 0;
}
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index 9fe8d486..45b4e212 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -284,7 +284,7 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType,
// 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") {
+ if (loaderType == "forge" || loaderType == "neoforge") {
auto iter = std::find_if(reqs.begin(), reqs.end(), [mcVersion](const Meta::Require& req) {
return req.uid == "net.minecraft" && req.equalsVersion == mcVersion;
});
@@ -350,7 +350,11 @@ bool FlameCreationTask::createInstance()
for (auto& loader : m_pack.minecraft.modLoaders) {
auto id = loader.id;
- if (id.startsWith("forge-")) {
+ if (id.startsWith("neoforge-")) {
+ id.remove("neoforge-");
+ loaderType = "neoforge";
+ loaderUid = "net.neoforged";
+ } else if (id.startsWith("forge-")) {
id.remove("forge-");
loaderType = "forge";
loaderUid = "net.minecraftforge";
diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp
index f5f3af37..0863f0b2 100644
--- a/launcher/modplatform/flame/FlamePackExportTask.cpp
+++ b/launcher/modplatform/flame/FlamePackExportTask.cpp
@@ -381,6 +381,7 @@ QByteArray FlamePackExportTask::generateIndex()
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");
+ const ComponentPtr neoforge = profile->getComponent("net.neoforged");
// convert all available components to mrpack dependencies
if (minecraft != nullptr)
@@ -392,6 +393,8 @@ QByteArray FlamePackExportTask::generateIndex()
id = "fabric-" + fabric->getVersion();
else if (forge != nullptr)
id = "forge-" + forge->getVersion();
+ else if (neoforge != nullptr)
+ id = "neoforge-" + neoforge->getVersion();
version["modLoaders"] = QJsonArray();
if (!id.isEmpty()) {
QJsonObject loader;
diff --git a/launcher/modplatform/import_ftb/PackHelpers.cpp b/launcher/modplatform/import_ftb/PackHelpers.cpp
index 4a1bbef9..118bdd15 100644
--- a/launcher/modplatform/import_ftb/PackHelpers.cpp
+++ b/launcher/modplatform/import_ftb/PackHelpers.cpp
@@ -59,7 +59,11 @@ Modpack parseDirectory(QString path)
auto obj = Json::requireObject(target, "target");
auto name = Json::requireString(obj, "name", "name");
auto version = Json::requireString(obj, "version", "version");
- if (name == "forge") {
+ if (name == "neoforge") {
+ modpack.loaderType = ResourceAPI::NeoForge;
+ modpack.version = version;
+ break;
+ } else if (name == "forge") {
modpack.loaderType = ResourceAPI::Forge;
modpack.version = version;
break;
diff --git a/launcher/modplatform/import_ftb/PackInstallTask.cpp b/launcher/modplatform/import_ftb/PackInstallTask.cpp
index b5e424d1..9e4decb0 100644
--- a/launcher/modplatform/import_ftb