diff options
Diffstat (limited to 'launcher')
26 files changed, 276 insertions, 48 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp index aed862dc..879af535 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -7,6 +7,7 @@ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Lenny McLennington <lenny@sneed.church> * Copyright (C) 2022 Tayou <tayou@gmx.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * 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 @@ -225,7 +226,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_serverToJoin = parser.value("server"); m_profileToUse = parser.value("profile"); m_liveCheck = parser.isSet("alive"); - + m_instanceIdToShowWindowOf = parser.value("show"); for (auto zip_path : parser.values("import")){ @@ -346,7 +347,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) import.command = "import"; import.args.insert("path", zip_url.toString()); m_peerInstance->sendMessage(import.serialize(), timeout); - } + } } } else @@ -515,6 +516,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("InstanceDir", "instances"); m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods"); m_settings->registerSetting("IconsDir", "icons"); + m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); // Editors m_settings->registerSetting("JsonEditor", QString()); @@ -660,6 +662,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->set("FlameKeyOverride", flameKey); m_settings->reset("CFKeyOverride"); } + m_settings->registerSetting("ModrinthToken", ""); m_settings->registerSetting("UserAgentOverride", ""); // Init page provider @@ -1548,6 +1551,15 @@ QString Application::getFlameAPIKey() return BuildConfig.FLAME_API_KEY; } +QString Application::getModrinthAPIToken() +{ + QString tokenOverride = m_settings->get("ModrinthToken").toString(); + if (!tokenOverride.isEmpty()) + return tokenOverride; + + return QString(); +} + QString Application::getUserAgent() { QString uaOverride = m_settings->get("UserAgentOverride").toString(); diff --git a/launcher/Application.h b/launcher/Application.h index 1b3dc499..91c5fc63 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Tayou <tayou@gmx.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * 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 @@ -177,6 +178,7 @@ public: QString getMSAClientID(); QString getFlameAPIKey(); + QString getModrinthAPIToken(); QString getUserAgent(); QString getUserAgentUncached(); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 66099c4e..c475773d 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1045,6 +1045,7 @@ target_link_libraries(Launcher_logic nbt++ ${ZLIB_LIBRARIES} tomlplusplus::tomlplusplus + qdcss BuildConfig Katabasis Qt${QT_VERSION_MAJOR}::Widgets diff --git a/launcher/InstancePageProvider.h b/launcher/InstancePageProvider.h index 5d8beca9..b4b6e739 100644 --- a/launcher/InstancePageProvider.h +++ b/launcher/InstancePageProvider.h @@ -36,9 +36,10 @@ public: values.append(new VersionPage(onesix.get())); values.append(ManagedPackPage::createPage(onesix.get())); auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList()); - modsPage->setFilter("%1 (*.zip *.jar *.litemod)"); + modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)"); values.append(modsPage); values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList())); + values.append(new NilModFolderPage(onesix.get(), onesix->nilModList())); values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList())); values.append(new TexturePackPage(onesix.get(), onesix->texturePackList())); values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList())); diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index c6d56543..1eda43fe 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -320,7 +320,7 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su if (relative_file_name.isEmpty()) { target_file_path = target + '/'; } else { - target_file_path = FS::PathCombine(target_top_dir.path(), sub_path, relative_file_name); + target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name); if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/')) target_file_path += '/'; } diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 4fe234c4..af4da5d0 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -290,6 +290,11 @@ QString MinecraftInstance::coreModsDir() const return FS::PathCombine(gameRoot(), "coremods"); } +QString MinecraftInstance::nilModsDir() const +{ + return FS::PathCombine(gameRoot(), "nilmods"); +} + QString MinecraftInstance::resourcePacksDir() const { return FS::PathCombine(gameRoot(), "resourcepacks"); @@ -1125,6 +1130,18 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList() const return m_core_mod_list; } +std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList() const +{ + if (!m_nil_mod_list) + { + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed, false)); + m_nil_mod_list->disableInteraction(isRunning()); + connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction); + } + return m_nil_mod_list; +} + std::shared_ptr<ResourcePackFolderModel> MinecraftInstance::resourcePackList() const { if (!m_resource_pack_list) diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 1bbd7b83..a75fa481 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -84,6 +84,7 @@ public: QString shaderPacksDir() const; QString modsRoot() const override; QString coreModsDir() const; + QString nilModsDir() const; QString modsCacheLocation() const; QString libDir() const; QString worldDir() const; @@ -116,6 +117,7 @@ public: ////// Mod Lists ////// std::shared_ptr<ModFolderModel> loaderModList() const; std::shared_ptr<ModFolderModel> coreModList() const; + std::shared_ptr<ModFolderModel> nilModList() const; std::shared_ptr<ResourcePackFolderModel> resourcePackList() const; std::shared_ptr<TexturePackFolderModel> texturePackList() const; std::shared_ptr<ShaderPackFolderModel> shaderPackList() const; @@ -170,6 +172,7 @@ protected: // data std::shared_ptr<PackProfile> m_components; mutable std::shared_ptr<ModFolderModel> m_loader_mod_list; mutable std::shared_ptr<ModFolderModel> m_core_mod_list; + mutable std::shared_ptr<ModFolderModel> m_nil_mod_list; mutable std::shared_ptr<ResourcePackFolderModel> m_resource_pack_list; mutable std::shared_ptr<ShaderPackFolderModel> m_shader_pack_list; mutable std::shared_ptr<TexturePackFolderModel> m_texture_pack_list; diff --git a/launcher/minecraft/launch/ScanModFolders.cpp b/launcher/minecraft/launch/ScanModFolders.cpp index bdffeadd..71e7638c 100644 --- a/launcher/minecraft/launch/ScanModFolders.cpp +++ b/launcher/minecraft/launch/ScanModFolders.cpp @@ -55,6 +55,12 @@ void ScanModFolders::executeTask() if(!cores->update()) { m_coreModsDone = true; } + + auto nils = m_inst->nilModList(); + connect(nils.get(), &ModFolderModel::updateFinished, this, &ScanModFolders::nilModsDone); + if(!nils->update()) { + m_nilModsDone = true; + } checkDone(); } @@ -70,9 +76,15 @@ void ScanModFolders::coreModsDone() checkDone(); } +void ScanModFolders::nilModsDone() +{ + m_nilModsDone = true; + checkDone(); +} + void ScanModFolders::checkDone() { - if(m_modsDone && m_coreModsDone) { + if(m_modsDone && m_coreModsDone && m_nilModsDone) { emitSucceeded(); } } diff --git a/launcher/minecraft/launch/ScanModFolders.h b/launcher/minecraft/launch/ScanModFolders.h index d5989170..111a5850 100644 --- a/launcher/minecraft/launch/ScanModFolders.h +++ b/launcher/minecraft/launch/ScanModFolders.h @@ -33,10 +33,12 @@ public: private slots: void coreModsDone(); void modsDone(); + void nilModsDone(); private: void checkDone(); private: // DATA bool m_modsDone = false; + bool m_nilModsDone = false; bool m_coreModsDone = false; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index f258ad69..3f31b93c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -50,7 +50,7 @@ #include "minecraft/mod/tasks/ModFolderLoadTask.h" #include "modplatform/ModIndex.h" -ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed) : ResourceFolderModel(QDir(dir)), m_is_indexed(is_indexed) +ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), nullptr, create_dir), m_is_indexed(is_indexed) { m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER }; } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 6898f6eb..84e70db9 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -75,7 +75,7 @@ public: Enable, Toggle }; - ModFolderModel(const QString &dir, bool is_indexed = false); + ModFolderModel(const QString &dir, bool is_indexed = false, bool create_dir = true); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 7c572d92..0d35d755 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -37,6 +37,9 @@ void Resource::parseFile() if (file_name.endsWith(".zip") || file_name.endsWith(".jar")) { m_type = ResourceType::ZIPFILE; file_name.chop(4); + } else if (file_name.endsWith(".nilmod")) { + m_type = ResourceType::ZIPFILE; + file_name.chop(7); } else if (file_name.endsWith(".litemod")) { m_type = ResourceType::LITEMOD; file_name.chop(8); diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index fdfb434b..f2a77c12 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -12,9 +12,11 @@ #include "tasks/Task.h" -ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent) : QAbstractListModel(parent), m_dir(dir), m_watcher(this) +ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_watcher(this) { - FS::ensureFolderPathExists(m_dir.absolutePath()); + if (create_dir) { + FS::ensureFolderPathExists(m_dir.absolutePath()); + } m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index f1bc2dd7..3bd78870 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -24,7 +24,7 @@ class QSortFilterProxyModel; class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: - ResourceFolderModel(QDir, QObject* parent = nullptr); + ResourceFolderModel(QDir, QObject* parent = nullptr, bool create_dir = true); ~ResourceFolderModel() override; /** Starts watching the paths for changes. diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 91cb747f..da27a505 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -3,6 +3,7 @@ #include <quazip/quazip.h> #include <quazip/quazipfile.h> #include <toml++/toml.h> +#include <qdcss.h> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> @@ -285,6 +286,32 @@ ModDetails ReadLiteModInfo(QByteArray contents) return details; } +// https://git.sleeping.town/unascribed/NilLoader/src/commit/d7fc87b255fc31019ff90f80d45894927fac6efc/src/main/java/nilloader/api/NilMetadata.java#L64 +ModDetails ReadNilModInfo(QByteArray contents, QString fname) +{ + ModDetails details; + + QDCSS cssData = QDCSS(contents); + auto name = cssData.get("@nilmod.name"); + auto desc = cssData.get("@nilmod.description"); + auto authors = cssData.get("@nilmod.authors"); + + if (name->has_value()) { + details.name = name->value(); + } + if (desc->has_value()) { + details.description = desc->value(); + } + if (authors->has_value()) { + details.authors.append(authors->value()); + } + details.version = cssData.get("@nilmod.version")->value_or("?"); + + details.mod_id = fname.remove(".nilmod.css"); + + return details; +} + bool process(Mod& mod, ProcessingLevel level) { switch (mod.type()) { @@ -401,6 +428,32 @@ bool processZIP(Mod& mod, ProcessingLevel level) mod.setDetails(details); return true; + } else if (zip.setCurrentFile("META-INF/nil/mappings.json")) { + // nilloader uses the filename of the metadata file for the modid, so we can't know the exact filename + // thankfully, there is a good file to use as a canary so we don't look for nil meta all the time + + QString foundNilMeta; + for (auto& fname : zip.getFileNameList()) { + // nilmods can shade nilloader to be able to run as a standalone agent - which includes nilloader's own meta file + if (fname.endsWith(".nilmod.css") && fname != "nilloader.nilmod.css") { + foundNilMeta = fname; + break; + } + } + + if (zip.setCurrentFile(foundNilMeta)) { + if (!file.open(QIODevice::ReadOnly)) { + zip.close(); + return false; + } + + details = ReadNilModInfo(file.readAll(), foundNilMeta); + file.close(); + zip.close(); + + mod.setDetails(details); + return true; + } } zip.close(); diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index 2177ddad..81c94e1b 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -79,7 +79,7 @@ void FlameHasher::executeTask() // CF-specific auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); }; - std::ifstream file_stream(StringUtils::toStdString(m_path), std::ifstream::binary); + std::ifstream file_stream(StringUtils::toStdString(m_path).c_str(), std::ifstream::binary); // TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread. // How do we make this non-blocking then? m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out)); diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 5982c8c9..e8a1d0b0 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln <flowlnlnln@gmail.com> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * 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 @@ -113,10 +114,15 @@ void Download::executeTask() } request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - if (APPLICATION->capabilities() & Application::SupportsFlame - && request.url().host().contains("api.curseforge.com")) { + // TODO remove duplication + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); - }; + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + request.setRawHeader("Authorization", token.toUtf8()); + } QNetworkReply* rep = m_network->get(request); diff --git a/launcher/net/Upload.cpp b/launcher/net/Upload.cpp index 79b6af8d..ccf43c2d 100644 --- a/launcher/net/Upload.cpp +++ b/launcher/net/Upload.cpp @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln <flowlnlnln@gmail.com> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * 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 @@ -216,10 +217,16 @@ namespace Net { } request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgent().toUtf8()); - if (APPLICATION->capabilities() & Application::SupportsFlame - && request.url().host().contains("api.curseforge.com")) { + // TODO remove duplication + if (APPLICATION->capabilities() & Application::SupportsFlame && request.url().host() == QUrl(BuildConfig.FLAME_BASE_URL).host()) { request.setRawHeader("x-api-key", APPLICATION->getFlameAPIKey().toUtf8()); + } else if (request.url().host() == QUrl(BuildConfig.MODRINTH_PROD_URL).host() || + request.url().host() == QUrl(BuildConfig.MODRINTH_STAGING_URL).host()) { + QString token = APPLICATION->getModrinthAPIToken(); + if (!token.isNull()) + request.setRawHeader("Authorization", token.toUtf8()); } + //TODO other types of post requests ? request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QNetworkReply* rep = m_network->post(request, m_post_data); diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp index eb427953..ff885f10 100644 --- a/launcher/ui/dialogs/BlockedModsDialog.cpp +++ b/launcher/ui/dialogs/BlockedModsDialog.cpp @@ -184,7 +184,7 @@ void BlockedModsDialog::directoryChanged(QString path) /// @brief add the user downloads folder and the global mods folder to the filesystem watcher void BlockedModsDialog::setupWatch() { - const QString downloadsFolder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + const QString downloadsFolder = APPLICATION->settings()->get("DownloadsDir").toString(); const QString modsFolder = APPLICATION->settings()->get("CentralModsDir").toString(); m_watcher.addPath(downloadsFolder); m_watcher.addPath(modsFolder); diff --git a/launcher/ui/pages/global/APIPage.cpp b/launcher/ui/pages/global/APIPage.cpp index e3d30475..f662ee1c 100644 --- a/launcher/ui/pages/global/APIPage.cpp +++ b/launcher/ui/pages/global/APIPage.cpp @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org> * Copyright (c) 2022 Lenny McLennington <lenny@sneed.church> + * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me> * * 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 @@ -147,6 +148,8 @@ void APIPage::loadSettings() ui->metaURL->setText(metaURL); QString flameKey = s->get("FlameKeyOverride").toString(); ui->flameKey->setText(flameKey); + QString modrinthToken = s->get("ModrinthToken").toString(); + ui->modrinthToken->setText(modrinthToken); QString customUserAgent = s->get("UserAgentOverride").toString(); ui->userAgentLineEdit->setText(customUserAgent); } @@ -177,6 +180,8 @@ void APIPage::applySettings() s->set("MetaURLOverride", metaURL); QString flameKey = ui->flameKey->text(); s->set("FlameKeyOverride", flameKey); + QString modrinthToken = ui->modrinthToken->text(); + s->set("ModrinthToken", modrinthToken); s->set("UserAgentOverride", ui->userAgentLineEdit->text()); } diff --git a/launcher/ui/pages/global/APIPage.ui b/launcher/ui/pages/global/APIPage.ui index d56a9ef6..40b89d91 100644 --- a/launcher/ui/pages/global/APIPage.ui +++ b/launcher/ui/pages/global/APIPage.ui @@ -196,24 +196,69 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_flame"> + <widget class="QGroupBox" name="groupBox_modrinth"> <property name="enabled"> <bool>true</bool> </property> <property name="title"> - <string>&CurseForge Core API</string> + <string>&Modrinth API</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> - <string>Note: you probably don't need to set this if CurseForge already works.</string> + <string><html><head/><body><p>Note: you only need to set this to access private data. Read the <a href="https://docs.modrinth.com/api-spec/#section/Authentication">documentation</a> for more information.</p></body></html></string> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_7"> <property name="text"> + <string>Enter a custom API token for Modrinth here.</string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLineEdit" name="modrinthToken"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="placeholderText"> + <string>(None)</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_flame"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>&CurseForge Core API</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>Note: you probably don't need to set this if CurseForge already works.</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_9"> + <property name="text"> <string>Enter a custom API Key for CurseForge here.</string> </property> <property name="textFormat"> diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index a9f44c0b..324eb461 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -140,8 +140,8 @@ void LauncherPage::on_instDirBrowseBtn_clicked() if (result == QMessageBox::Ok) { ui->instDirTextBox->setText(cooked_dir); - } - } + } + } else { ui->instDirTextBox->setText(cooked_dir); @@ -160,6 +160,7 @@ void LauncherPage::on_iconsDirBrowseBtn_clicked() ui->iconsDirTextBox->setText(cooked_dir); } } + void LauncherPage::on_modsDirBrowseBtn_clicked() { QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Mods Folder"), ui->modsDirTextBox->text()); @@ -172,6 +173,17 @@ void LauncherPage::on_modsDirBrowseBtn_clicked() } } +void LauncherPage::on_downloadsDirBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Downloads Folder"), ui->downloadsDirTextBox->text()); + + if (!raw_dir.isEmpty() && QDir(raw_dir).exists()) + { + QString cooked_dir = FS::NormalizePath(raw_dir); + ui->downloadsDirTextBox->setText(cooked_dir); + } +} + void LauncherPage::on_metadataDisableBtn_clicked() { ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); @@ -204,6 +216,7 @@ void LauncherPage::applySettings() s->set("InstanceDir", ui->instDirTextBox->text()); s->set("CentralModsDir", ui->modsDirTextBox->text()); s->set("IconsDir", ui->iconsDirTextBox->text()); + s->set("DownloadsDir", ui->downloadsDirTextBox->text()); auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId(); switch (sortMode) @@ -260,6 +273,7 @@ void LauncherPage::loadSettings() ui->instDirTextBox->setText(s->get("InstanceDir").toString()); ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); + ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString()); QString sortMode = s->get("InstSortMode").toString(); diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index c60156c2..33f66f1b 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -88,6 +88,7 @@ slots: void on_instDirBrowseBtn_clicked(); void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); + void on_downloadsDirBrowseBtn_clicked(); void on_metadataDisableBtn_clicked(); /*! diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index f084d970..923b7f95 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -38,7 +38,7 @@ <enum>QTabWidget::Rounded</enum> </property> <property name="currentIndex"> - <number>1</number> + <number>0</number> </property> <widget class="QWidget" name="featuresTab"> <attribute name="title"> @@ -67,20 +67,32 @@ <string>Folders</string> </property> <layout class="QGridLayout" name="foldersBoxLayout"> - <item row="1" column="2"> - <widget class="QToolButton" name="modsDirBrowseBtn"> + <item row="0" column="2"> + <widget class="QToolButton" name="instDirBrowseBtn"> <property name="text"> <string notr="true">...</string> </property> </widget> </item> - <item row="0" column="2"> - <widget class="QToolButton" name="instDirBrowseBtn"> + <item row="1" column="1"> + <widget class="QLineEdit" name="modsDirTextBox"/> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="instDirTextBox"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelInstDir"> <property name="text"> - <string notr="true">...</string> + <string>I&nstances:</string> + </property> + <property name="buddy"> + <cstring>instDirTextBox</cstring> </property> </widget> </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="iconsDirTextBox"/> + </item> <item row="2" column="2"> <widget class="QToolButton" name="iconsDirBrowseBtn"> <property name="text"> @@ -88,8 +100,22 @@ </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="instDirTextBox"/> + <item row="1" column="2"> + <widget class="QToolButton" name="modsDirBrowseBtn"> + <property name="text"> + <string notr="true">...</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelModsDir"> + <property name="text"> + <string>&Mods:</string> + </property> + <property name="buddy"> + <cstring>modsDirTextBox</cstring> + </property> + </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="labelIconsDir"> @@ -101,29 +127,23 @@ </property> </widget> </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="modsDirTextBox"/> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="labelInstDir"> + <item row="3" column="0"> + <widget class="QLabel" name="labelDownloadsDir"> <property name="text"> - <string>I&nstances:</string> + <string>&Downloads:</string> </property> <property name="buddy"> - <cstring>instDirTextBox</cstring> + <cstring>downloadsDirTextBox</cstring> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="iconsDirTextBox"/> + <item row="3" column="1"> + <widget class="QLineEdit" name="downloadsDirTextBox"/> </item> - <item row="1" column="0"> - <widget class="QLabel" name="labelModsDir"> + <item row="3" column="2"> + <widget class="QToolButton" name="downloadsDirBrowseBtn"> <property name="text"> - <string>&Mods:</string> - </property> - <property name="buddy"> - <cstring>modsDirTextBox</cstring> + <string>...</string> </property> </widget> </item> diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index d9069915..4548af59 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -273,3 +273,12 @@ bool CoreModFolderPage::shouldDisplay() const } return false; } + +NilModFolderPage::NilModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent) + : ModFolderPage(inst, mods, parent) +{} + +bool NilModFolderPage::shouldDisplay() const +{ + return m_model->dir().exists(); +} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index ff58b38a..2fc7b574 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -81,3 +81,16 @@ class CoreModFolderPage : public ModFolderPage { virtual bool shouldDisplay() const override; }; + +class NilModFolderPage : public ModFolderPage { + public: + explicit NilModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel> mods, QWidget* parent = 0); + virtual ~NilModFolderPage() = default; + + virtual QString displayName() const override { return tr("Nilmods"); } + virtual QIcon icon() const override { return APPLICATION->getThemedIcon("coremods"); } + virtual QString id() const override { return "nilmods"; } + virtual QString helpPage() const override { return "Nilmods"; } + + virtual bool shouldDisplay() const override; +}; |