diff options
Diffstat (limited to 'launcher')
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 |
